Skip to content

feat(approvals): declare full decision/continuity action set + wire revise/resubmit routes#3300

Merged
os-zhuang merged 1 commit into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l
Jul 19, 2026
Merged

feat(approvals): declare full decision/continuity action set + wire revise/resubmit routes#3300
os-zhuang merged 1 commit into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Backend half of retiring the approvals inbox's hand-written buttons (objectui#2678 P2-4 follow-up). The console's generic action runtime already renders sys_approval_request's server-declared actions anywhere the object is surfaced (the inbox's DeclaredActionsBar included); this completes the declared set so the inbox no longer needs a bespoke button per capability.

What

New declared actions on sys_approval_request — all type:'api', POST, {id}-interpolated, refreshAfter:

action route who gating
approval_send_back /revise approver status == pending
approval_request_info /request-info approver status == pending (comment required)
approval_remind /remind submitter status == pending && submitter_id == ctx.user.id
approval_recall /recall submitter (pending || returned) && submitter_id == ctx.user.id
approval_resubmit /resubmit submitter status == returned && submitter_id == ctx.user.id
  • Submitter-only levers gate on record.submitter_id == ctx.user.id — the console exposes the current user in the predicate scope. Approver actions gate only on record.status == "pending" and defer who-can-act to the service, matching the existing approve/reject precedent (the service is the authority; visible only trims the obvious non-pending case).
  • approval_reassign's to param becomes field-backed on submitter_id, so the dialog resolves its sys_user lookup config and renders a real user picker (keyed as to) instead of a free-text user-id box.

Registers the missing /revise and /resubmit REST routes. The console has been POSTing to these for send-back / resubmit (ADR-0044) since those buttons shipped, but rest-server never wired them — so both returned 404. They now dispatch to the existing ApprovalService.sendBack / resubmit with the standard approval error mapping (VALIDATION_FAILED→400, INVALID_STATE→409, FORBIDDEN→403, REQUEST_NOT_FOUND→404). This fixes a pre-existing gap independently of the SDUI work.

Why

This is the structural payoff from the #2678 audit: approval capabilities ship as backend metadata, and any surface that mounts DeclaredActionsBar renders them with zero per-action UI code. The objectui follow-up retires the inbox's hardcoded send-back / request-info / reassign / remind / recall / resubmit buttons on top of this.

Verification

  • plugin-approvals suite green (143 tests), incl. a new contract test pinning the action set, that every type:'api' target maps to a registered approvals route verb, and the submitter-vs-approver gating.
  • rest builds clean (route additions typecheck).
  • Empirically confirmed the gap this closes: against the running backend, POST /revise returned 404 before this change while /recall returned 401 (auth) — i.e. the route was genuinely absent.
  • End-to-end browser verification of the retired inbox buttons rides with the objectui PR.

Refs

objectui#2678 (P2-4) · ADR-0044 (send-back/resubmit) · ADR-0019 (approval-as-flow-node)

🤖 Generated with Claude Code


Generated by Claude Code

…evise/resubmit routes

Backend half of retiring the approvals inbox's hand-written buttons
(objectui#2678 P2-4 follow-up). The console's generic action runtime already
renders `sys_approval_request`'s declared actions anywhere the object is
surfaced; this completes the declared set so the inbox no longer needs a
bespoke button per capability.

New declared actions on sys_approval_request (all `type:'api'`, POST, {id}-
interpolated, refreshAfter):
  - approval_send_back      → /revise        (approver; status==pending)
  - approval_request_info   → /request-info  (approver; comment required)
  - approval_remind         → /remind        (submitter-gated)
  - approval_recall         → /recall        (submitter-gated; pending|returned)
  - approval_resubmit       → /resubmit      (submitter-gated; returned)

Submitter-only levers gate on `record.submitter_id == ctx.user.id` (the
console exposes the current user in the predicate scope); approver actions
gate only on `record.status == "pending"` and defer who-can-act to the
service, matching the existing approve/reject precedent.

approval_reassign's `to` param becomes field-backed on `submitter_id` so the
dialog resolves its `sys_user` lookup config and renders a real user picker
(keyed as `to`), replacing the free-text user-id box.

Also registers the missing `/revise` and `/resubmit` REST routes: the console
has been POSTing to them for send-back/resubmit (ADR-0044) but rest-server
never wired them, so both 404'd. They now dispatch to the existing
ApprovalService.sendBack / resubmit with the standard approval error mapping.

Contract test pins the action set, that every target hits a registered route
verb, and the submitter-vs-approver gating.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 19, 2026 4:32pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-approvals, @objectstack/rest.

10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/error-catalog.mdx (via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest)
  • content/docs/api/index.mdx (via @objectstack/rest)
  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals)
  • content/docs/plugins/index.mdx (via @objectstack/rest)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-approvals, @objectstack/rest)
  • content/docs/protocol/kernel/i18n-standard.mdx (via packages/rest)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals, @objectstack/rest)
  • content/docs/releases/v12.mdx (via @objectstack/rest)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 19, 2026 16:58
@os-zhuang
os-zhuang merged commit 82a6fb0 into main Jul 19, 2026
15 of 16 checks passed
@os-zhuang
os-zhuang deleted the claude/enterprise-delegation-ooo-proxy-1v3m8l branch July 19, 2026 16:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants