Part of steedos-labs/os-project-titanwind-ehr#1225
TL;DR
An admin viewing a pending approval they hold no slot in (or have already voted on) sees the exact same filled Approve / Reject / Reassign buttons as a designated approver — no override styling, no confirmation, nothing. One unmarked click takes the #3424 privileged-override branch, which is authoritative: it finalizes the node even under per_group/unanimous/quorum, silently bypassing every remaining co-sign group. And although #4466 made the service record sys_approval_action.via_override, no UI surface anywhere reads that column — the timeline renders an override byte-for-byte like an ordinary approval.
The practical consequence in our app project: our PM walked a 3-stage countersign flow with two admin-posture accounts, saw Approve still rendered after each co-signer had voted, clicked it "to see if it was real", and it decided the next stage for an approver who never acted. They filed it as "countersign is broken — the Approve button is still there after I approved" (steedos-labs/os-project-titanwind-ehr#1225, private repo — screenshots there). The behavior is your documented design working correctly; the UI gives an admin no way to see that, so it reads as a broken countersign to the very people most likely to be driving a test.
Stale-premise check (done before filing)
Repro (standalone — no app metadata needed)
- Any flow with an
approval node; a request goes pending with a staffed slate that does not include the admin (or from which the admin has already been dropped after voting under per_group).
- View the record (or the approvals inbox drawer) as a platform/tenant admin.
- Observed: Approve / Reject / Reassign render exactly as they do for a designated approver —
visible: "record.viewer.can_act || record.viewer.can_override" (plugin-approvals action declarations), primary/filled variant, no hint which predicate admitted you.
- Click Approve → 200, node finalizes (on a multi-group countersign node: all remaining groups bypassed),
sys_approval_action.via_override = true is written —
- — and the rendered timeline shows a plain "Approved · <admin>", indistinguishable from a slot holder's approval. The only place the truth exists is the raw
sys_approval_request/sys_approval_action tables under Setup.
Expected vs actual
|
expected |
actual |
| Affordance |
An override-only viewer (can_act:false, can_override:true) sees a visually distinct action ("Override approve…" / warning variant), ideally behind a confirm that names the pending approvers being bypassed — this is an authoritative, group-bypassing act, not one vote |
Identical filled Approve; one click, no confirm (Approve has no description, unlike Reject) |
| Timeline |
via_override rows marked ("Approved — admin override") — the surfacing #4466's Expected asked for |
Column written, never read by any UI; override renders as an ordinary approval |
| Contract cited |
docs content/docs/automation/approvals.mdx: the decision records "which door it came through" precisely because "an override and an ordinary approval were byte-for-byte identical" — that argument applies to the rendered timeline exactly as it did to the DB row |
the byte-for-byte identity is fixed at rest, still true on screen |
What we did NOT do
No app-side workaround exists or was attempted: the buttons are the platform's own sys_approval_request declared actions rendered by the console — an app cannot restyle, gate, or annotate them, and hiding admin rescue entirely would break the #3424 recovery path we do want. Our app-side issue is parked as Blocked-by this one.
Versions
@objectstack/{runtime,spec,objectql,cli}@17.0.0 (GA), @objectstack/console@17.0.0, @objectstack/plugin-approvals@17.0.0; re-verified unchanged on objectstack@f63afb237c09 and objectui@68d9e282c83a. Node 22 / macOS.
Part of steedos-labs/os-project-titanwind-ehr#1225
TL;DR
An admin viewing a pending approval they hold no slot in (or have already voted on) sees the exact same filled Approve / Reject / Reassign buttons as a designated approver — no override styling, no confirmation, nothing. One unmarked click takes the #3424 privileged-override branch, which is authoritative: it finalizes the node even under
per_group/unanimous/quorum, silently bypassing every remaining co-sign group. And although #4466 made the service recordsys_approval_action.via_override, no UI surface anywhere reads that column — the timeline renders an override byte-for-byte like an ordinary approval.The practical consequence in our app project: our PM walked a 3-stage countersign flow with two admin-posture accounts, saw Approve still rendered after each co-signer had voted, clicked it "to see if it was real", and it decided the next stage for an approver who never acted. They filed it as "countersign is broken — the Approve button is still there after I approved" (steedos-labs/os-project-titanwind-ehr#1225, private repo — screenshots there). The behavior is your documented design working correctly; the UI gives an admin no way to see that, so it reads as a broken countersign to the very people most likely to be driving a test.
Stale-premise check (done before filing)
objectstackmain @f63afb237c09—packages/plugins/plugin-approvals/src/approval-service.tsstill gates the decision actions onrecord.viewer.can_act || record.viewer.can_override, anddecideNodestill finalizes on a non-slot-holder approve (if (approve && behavior !== 'first_response' && isSlotHolder)— the tally branch requiresisSlotHolder, so an override approve falls through to finalize).objectuimain @68d9e282c83a—via_overridehas zero hits in the repo (can_overrideis consumed inapps/console/src/services/approvalsApi.ts,packages/app-shell/src/views/RecordDetailView.tsx,ApprovalsInboxPage.tsx— only to show the buttons, never to mark them). Same in the shipped@objectstack/console@17.0.0bundle.via_overridecolumn — its own Expected said the marker should be "surfaced in the timeline", which is the half that never landed. Approval routed to an empty position permanently locks the record (no admin override, no recovery) #3424 designed the override. QA run QA run · tier2b:browser-1 (21/21) · e4e5c6e3 · 2026-08-18 · 7 PASS / 12 PARTIAL / 2 FAIL #9453 (item 14,per-group-signoff) empirically re-confirmed one4e5c6e3:can_override:trueadmits the admin with a 200. No open issue in either repo covers the affordance/rendering gap.Repro (standalone — no app metadata needed)
approvalnode; a request goespendingwith a staffed slate that does not include the admin (or from which the admin has already been dropped after voting underper_group).visible: "record.viewer.can_act || record.viewer.can_override"(plugin-approvals action declarations), primary/filled variant, no hint which predicate admitted you.sys_approval_action.via_override = trueis written —sys_approval_request/sys_approval_actiontables under Setup.Expected vs actual
can_act:false, can_override:true) sees a visually distinct action ("Override approve…" / warning variant), ideally behind a confirm that names the pending approvers being bypassed — this is an authoritative, group-bypassing act, not one votedescription, unlike Reject)via_overriderows marked ("Approved — admin override") — the surfacing #4466's Expected asked forcontent/docs/automation/approvals.mdx: the decision records "which door it came through" precisely because "an override and an ordinary approval were byte-for-byte identical" — that argument applies to the rendered timeline exactly as it did to the DB rowWhat we did NOT do
No app-side workaround exists or was attempted: the buttons are the platform's own
sys_approval_requestdeclared actions rendered by the console — an app cannot restyle, gate, or annotate them, and hiding admin rescue entirely would break the #3424 recovery path we do want. Our app-side issue is parked as Blocked-by this one.Versions
@objectstack/{runtime,spec,objectql,cli}@17.0.0(GA),@objectstack/console@17.0.0,@objectstack/plugin-approvals@17.0.0; re-verified unchanged onobjectstack@f63afb237c09andobjectui@68d9e282c83a. Node 22 / macOS.