Review domain
Commands define allowed edits. Persistence coordinates retries and database versions; workflow and program functions enforce business rules.
Write contract
commandSchema is a Zod discriminated union on type.
ReviewCommand is inferred from it.
execute(review, command, actor, {id, at}) applies a command without database
I/O. Its default path clones the review; CommandDraft uses a preview path for
coalesced edits. The HTTP boundary validates again and supplies the authoritative
actor/time.
Three version checks
Database version rejects competing saves. Domain revision marks
stored checks stale after relevant edits. facts.inputsRevision marks Final
Results stale after computation inputs change. They serve different purposes and do not
necessarily increase together.
Workflow
new_active / needs_changes
examiner submit → awaiting_review
non-drop: required fields and current Final Results
drop: drop kind; skips computation gates
awaiting_review
supervisor / manager request_changes + note → needs_changes
approve drop → awaiting_manager
approve / route non-drop → PEC_ROUTING_PENDING (currently blocked)
awaiting_manager, drop
manager approve → dropped
Seeded later states also exercise:
PEC decision → needs_changes / awaiting PEC / awaiting_manager
manager approve_memo → awaiting_cao_response
manager release_summary → released_to_reporting
workflow.transition covers submit, request changes, route PEC, and approval.
Other state changes, including PEC and memo/release commands, live in
execute with helpers from downstream.ts. Required-input blockers
prevent submission; stored edit-check findings can be recorded on submission and block
later approval unless resolved or refused.
Received copies, restore, and identity
Received snapshots contain facts and identifying fields, not the whole activity/document
history. Intake/reset captures the examiner copy; submission captures the supervisor copy;
later handoffs capture the manager copy. restore_facts validates the restore
and recomputes a current Final rather than trusting supplied results. UUID identity stays
fixed. Identity edits cannot move the sample month outside its FFY or change staff
assignments in this demo.
Where a new command belongs
Add its schema and domain case together, then call it from the UI through
perform. Decide its role/state gate, revision effects, activity record,
undo/received-copy behavior, and whether draft coalescing is appropriate. Use command
tests for rules and persistence tests for retries/conflicts. Do not write snapshots
directly from components.