Summary
PUT /api/v1/work-centers/{id} has no validator. CreateWorkCenter.cs carries the full rule set (~L24-34) but no AbstractValidator<UpdateWorkCenterCommand> exists anywhere in the codebase, so MediatR's ValidationBehavior has nothing to run on the update path.
Observed (beta.25, Engineer kiosk and Admin alike)
| Request |
Result |
PUT {"dailyCapacityHours": -5, "efficiencyPercent": -100, "numberOfMachines": 0} |
200, persisted |
PUT {"name": "", "code": ""} |
200, persisted |
PUT {"efficiencyPercent": 99999} |
500 (numeric(5,2) overflow, unhandled) |
POST /work-centers with a duplicate code |
opaque 500 instead of 409 (the unique index on Code throws through) |
Related 500s found on the same pass:
PATCH /api/v1/parts/{id} with a nonexistent preferredVendorId → 500 (UpdatePart.cs ~L95); should be 400/404.
DELETE of a BOM child that an Active part depends on → 204, and the released recipe silently loses its only input. No referential guard.
Suggested fix
- Add
UpdateWorkCenterValidator mirroring the create rules (non-empty name/code, capacity > 0, efficiency in [0, 999.99], machines ≥ 1).
- Catch the unique-index violation on
code in create/update and return 409 with the conflicting code.
- Validate
preferredVendorId existence in UpdatePart.
- Refuse (or at least warn on) BOM-line deletion when the parent part is Active.
Found by the 2026-08-30 arsenal-as-client gap audit; reproduced live, adversarially verified.
Summary
PUT /api/v1/work-centers/{id}has no validator.CreateWorkCenter.cscarries the full rule set (~L24-34) but noAbstractValidator<UpdateWorkCenterCommand>exists anywhere in the codebase, so MediatR'sValidationBehaviorhas nothing to run on the update path.Observed (beta.25, Engineer kiosk and Admin alike)
PUT {"dailyCapacityHours": -5, "efficiencyPercent": -100, "numberOfMachines": 0}200, persistedPUT {"name": "", "code": ""}200, persistedPUT {"efficiencyPercent": 99999}500(numeric(5,2) overflow, unhandled)POST /work-centerswith a duplicatecode500instead of409(the unique index onCodethrows through)Related 500s found on the same pass:
PATCH /api/v1/parts/{id}with a nonexistentpreferredVendorId→500(UpdatePart.cs~L95); should be400/404.DELETEof a BOM child that an Active part depends on →204, and the released recipe silently loses its only input. No referential guard.Suggested fix
UpdateWorkCenterValidatormirroring the create rules (non-empty name/code, capacity > 0, efficiency in [0, 999.99], machines ≥ 1).codein create/update and return409with the conflicting code.preferredVendorIdexistence inUpdatePart.Found by the 2026-08-30 arsenal-as-client gap audit; reproduced live, adversarially verified.