You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit b158bc9
Browse filesBrowse the repository at this point in the historyBrowse files
fix(isms): serialize approve/decline to prevent concurrent double-publish (CS-701) (#3366)
Flagged on the production deploy PR (#3364): assertPendingApprovalBy runs before
the transaction, so two racing approve() calls could both pass the guard.
Root cause (verified): under READ COMMITTED a plain in-transaction status re-read
does NOT serialize — the loser reads the still-committed `needs_review`. The
@@unique([documentId, version]) constraint only turns the perfectly-simultaneous
case into an error; the staggered case silently creates a second published version
and overwrites currentVersionId.
Fix:
- approve() now takes a per-document advisory lock and then claims the approval
atomically via updateMany({ where: status='needs_review' + approverId }) inside
the transaction, aborting when count !== 1. Exactly one caller freezes a version;
losers abort before any derivation/version creation. The pre-transaction guard is
kept for the normal-case specific error messages.
- decline() gets the same atomic claim, so an approve/decline race can no longer
leave a "declined" document that still owns a live currentVersionId.
- Renamed the shared advisory-lock helper lockDocumentForPositions -> lockDocument
(it now serializes approval too, not just register-row position allocation);
updated the four register-create callers.
Adversarially audited the whole approve/publish/version/export flow for related
concurrency + IDOR issues; getVersionExport org-scoping, S3 keys, snapshot parsing,
currentVersionId integrity and the migration paths verified safe. The narrow
edit-during-approve window for register update/delete is low-severity/self-healing
and left as a follow-up.
Tests: 321 api ISMS tests passing (incl. new approve + decline race tests);
typecheck clean.
Claude-Session: https://claude.ai/code/session_012CweXoSSEP89mX93u3Bdcp
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments