|
| 1 | +--- |
| 2 | +"@objectstack/plugin-approvals": patch |
| 3 | +--- |
| 4 | + |
| 5 | +fix(approvals): the #3424 override reaches a `pending` request only — override-recall of a `returned` request is now refused (#12775) |
| 6 | + |
| 7 | +**Behaviour change, declared explicitly (maintainer ruling 2026-09-02) so that |
| 8 | +one revert of this changeset's PR restores the previous behaviour.** |
| 9 | + |
| 10 | +`ApprovalService.recall` admits two callers: the submitter, and a platform or |
| 11 | +tenant admin releasing a stuck request (the #3424 privileged override). Recall |
| 12 | +is also valid on the LATEST `returned` request of a run — the ADR-0044 revise |
| 13 | +window, where the submitter abandons the revision instead of resubmitting. |
| 14 | +Those two rules met above the state check: the override short-circuit carried |
| 15 | +no status test of its own, so an override actor could recall a `returned` |
| 16 | +request too. Nothing else on the platform said so — `isOverrideActor`'s doc |
| 17 | +block names a PENDING request, `attachViewers` computes |
| 18 | +`viewer.can_override` as `status === 'pending' && isOverrideActor(...)`, and |
| 19 | +the `approval_recall` action's override arm reads that flag — so the reach was |
| 20 | +API-only, never offered by any UI, and pinned by nothing. |
| 21 | + |
| 22 | +What changes: |
| 23 | + |
| 24 | +- **Override-recall of a `returned` request is refused.** The override |
| 25 | + short-circuit in `recall` now applies only while the request is `pending`, |
| 26 | + spelled exactly as the viewer flag is computed. On `returned` an override |
| 27 | + actor is judged exactly as any other non-submitter and receives the existing |
| 28 | + refusal: `403 FORBIDDEN` over REST, with the operation catalog's |
| 29 | + `approval_recall_not_submitter` sentence. No new error code, no new envelope. |
| 30 | +- **Only `pending` requests are override-recallable** — the same scope as the |
| 31 | + other three override levers (approve / reject / reassign are pending-only at |
| 32 | + their endpoints), and the same scope the viewer flag has always declared. |
| 33 | + |
| 34 | +What does not change: |
| 35 | + |
| 36 | +- The submitter's own recall of a `returned` request (the ADR-0044 revise |
| 37 | + window) is untouched; so is the submitter's recall of a `pending` one. |
| 38 | +- The override actor's recall of a `pending` request — the #3424 rescue of a |
| 39 | + request routed to an unstaffed position — is untouched, lock release and all. |
| 40 | +- The refusal's message, wire code and developer log line keep their shape; the |
| 41 | + log line now also names the request status it refused on. |
| 42 | + |
| 43 | +Why: the gate now agrees with the viewer flag and the documented contract at |
| 44 | +one point instead of disagreeing with both. If a real operator workflow |
| 45 | +depended on override-recalling a `returned` request, this is the change to |
| 46 | +revert; the `returned` record lock is already released, so the stuck-record |
| 47 | +rescue motive that justifies the override does not apply on that status. |
0 commit comments