createMaintenanceRecord()'s AssetItem status guard pinned status:{not:"retired"}
instead of status:"in_stock", so two concurrent opens (or an open racing a
concurrent createAssignment()) could both pass the guard, producing two open
MaintenanceRecords or an orphaned active Assignment. Guard now pins "in_stock"
exactly, mirroring createAssignment()'s own atomic guard.
closeMaintenanceRecord()'s targetStatus accepted any of the 4 AssetItemStatus
values, including "assigned" — which sets AssetItem.status without creating a
corresponding Assignment row, desyncing Assignment (source of truth for who
holds what) from AssetItem.status with no way back. Restricted to
in_stock|retired, the only two outcomes a closed repair can actually resolve
to.
createMaintenanceRecord() opens a record and flips AssetItem.status to
"repair" inside one transaction, rejecting on missing/retired AssetItem,
an active Assignment, or an already-open record for the same item.
closeMaintenanceRecord() sets endDate/resolutionNotes and applies a
caller-supplied targetStatus (never defaulted) to the AssetItem, guarding
double-close and endDate>=startDate. Mirrors src/lib/assignments.ts's
Zod-validate + $transaction + WHERE-guarded updateMany race-closing pattern.