fix(api): wrap critical mutations in transactions and fix TOCTOU race conditions

- applyProjectScenario: wrap assignment loop in db.$transaction to prevent partial updates
- vacation approve/reject: fix TOCTOU race via updateMany with status-guard in WHERE + CONFLICT on count=0
- vacation cancel: wrap vacation.update + entitlement.updateMany in $transaction
- batchApprove: collect mutations, wrap in $transaction, dispatch SSE/notifications after commit
- Fix dead-code bug in createHappyPathDb where $transaction was assigned after return
- Add atomicity and concurrency tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-09 08:34:59 +02:00
parent b103e79e92
commit 1d6d75ecf6
6 changed files with 245 additions and 129 deletions
@@ -72,15 +72,15 @@ describe("assistant vacation mutation tools", () => {
message: "Rejected vacation for Alice Example: Capacity freeze",
}),
);
expect(db.vacation.update).toHaveBeenCalledWith(
expect(db.vacation.updateMany).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: "vac_cancelled" },
where: expect.objectContaining({ id: "vac_cancelled" }),
data: expect.objectContaining({ status: "APPROVED" }),
}),
);
expect(db.vacation.update).toHaveBeenCalledWith(
expect(db.vacation.updateMany).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: "vac_pending" },
where: expect.objectContaining({ id: "vac_pending" }),
data: expect.objectContaining({ status: "REJECTED", rejectionReason: "Capacity freeze" }),
}),
);