The API answers rule violations with HTTP 409 and a ProblemDetails body whose title is the rule's name and whose detail is the reason in a sentence. The UI shows the same sentence in its banner. Nothing is written when a refusal is returned; the integration tests assert the row counts do not move.
Rule (title) |
Returned when | The sentence says |
|---|---|---|
DependencyNotCompleted |
Completing a task while any dependency is not fully Completed |
which task is not done yet |
TaskAlreadyCompleted |
Completing an already completed or waiting task; reopening a task that is not completed; starting a started task | the task's state and what to do instead |
FourEyesViolation |
Signing off a task as the person who completed it | the four-eyes rule itself |
NotAwaitingSignOff |
Signing off a task that is not in WaitingSignOff |
the task is not waiting for sign-off |
EmptyReason |
Reopening without a reason, or an empty comment | a reason is required and becomes part of the audit trail |
LockBlocked |
Locking a period while tasks are open | how many tasks and the first blocker by name |
UnauthorizedRole |
Locking as anyone other than Controller or CFO | who may lock |
PeriodLocked |
Any mutation against a locked period | the period is frozen |
NotFound (404) |
Unknown task, period, or actor | what was not found |
Stable anchors: GET /api/health for liveness, POST /api/demo/reset to return the deterministic seed to its initial state.