Repository navigation
feat(approvals): a submitter's agent can recall and resubmit its own approval request over MCP (#32993) - #33076
Conversation
…approval request over MCP approval_recall and approval_resubmit declare ai.exposed with requiresConfirmation, forwarded to their approvals routes as the caller; ApprovalService stays the only gate. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cwwUDMxb29FgtH4D53
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 6 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 74551ba69249a6bd06dc90dbb195512bd23e39eb && git checkout 74551ba69249a6bd06dc90dbb195512bd23e39eb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4462b4d92b9fff58d43e6fa8e700fd0ea6d4f62d f5587158283eaf01d6ce8b9a3c81e3bc45a65999 && git checkout -B drift-repro 4462b4d92b9fff58d43e6fa8e700fd0ea6d4f62d && git merge --no-ff f5587158283eaf01d6ce8b9a3c81e3bc45a65999
node scripts/docs-audit/affected-docs.mjs --json 4462b4d92b9fff58d43e6fa8e700fd0ea6d4f62d |
|
domain:server#8 · os-elon-musk · session_016WV4cwqTK71kjQzHae8T5J ACCEPT · PR #33076 · head 席位核对
|
Requested by Elon · project thread
Fixes #32993
Clause-②: yes (widening)
Metadata-protocol: none
维护者速读
ai块。packages/plugins/plugin-approvals/src/sys-approval-request.object.ts(approval_recall、approval_resubmit的两个ai块和数组上方说明);packages/qa/dogfood/test/approval-decide-over-mcp.dogfood.test.ts(新增三条端到端钉子)。pnpm --filter @objectstack/dogfood exec vitest run --maxWorkers=2 test/approval-decide-over-mcp.dogfood.test.ts,应见Tests 8 passed (8)。What changes
The submitter-side branch of ruling A on #32442. PR #32886 exposed the approver's four decisions on
sys_approval_request; the submitter's own levers on the same object stayed UI-only, so a submitter's agent that had to recall a request (to fix the record) handed the click back to the person.sys-approval-request.object.ts:approval_recallandapproval_resubmitdeclareai: { exposed: true, description, requiresConfirmation: true }, the shape feat(plugin-approvals): an approver's agent decides a pending approval request over MCP #32886 gave the decisions. Both are already same-origintype: 'api'actions targetingPOST /api/v1/approvals/requests/{id}/recalland/resubmit, sorun_actionforwards them as the caller andApprovalServicestays the only gate:recalladmits the submitter (and, on a pending request, the Approval routed to an empty position permanently locks the record (no admin override, no recovery) #3424 override holder) and refuses anyone else withapproval_recall_not_submitter;resubmitadmits the submitter alone and applies only to areturnedrequest. No check is added in the MCP door or the action door.description, approvals:sys_approval_request.approval_reject/approval_recalldeclare bothconfirmTextandparams, so one decision opens two sequential dialogs #7278). Recall's tells the agent a recalled request is closed, so sending the record for approval again goes through the app's own submit action; resubmit's tells it resubmit applies only to a request sent back for revision.content/docs/ai/connect-mcp.mdx: the default exposure policy gains the submitter's bullet; "the submitter's own levers (remind, recall, resubmit) are not exposed" narrows to remind, request-info and comments.@objectstack/plugin-approvalspatch,Clause-②: yes (widening),Metadata-protocol: none(no spec key changes; two existing actions gain an existing key).Tests
packages/plugins/plugin-approvals/src/sys-approval-request.object.test.ts(18 tests):ai.description" line is replaced by the same not-the-question checkapproval_rejecthas).packages/qa/dogfood/test/approval-decide-over-mcp.dogfood.test.ts(5 → 8, live MCP transport, real security/flows/approvals). The fixture flow gains an ADR-0044 revise window (revise→approval_revise→resubmitback edge); the existing decide pins are unchanged.list_actionsfor both the approver and the submitter lists the four decisions plus recall and resubmit, eachaiExposedandrequiresConfirmation.ACTION_CONFIRMATION_REQUIREDwithoutconfirm: true(request still pending, nothing recorded), then, confirmed, moves the request torecalled, recorded once asrecallby the submitter.FORBIDDEN, carrying theapproval_recall_not_submittersentence verbatim fromBUILTIN_OPERATION_MESSAGES.en; the request stays pending.FORBIDDEN, the request staysreturnedwith no round 2; the submitter's confirmed resubmit opens round 2 (one new pending request), recorded asreviseby the approver andresubmitby the submitter.Ablation: each
aiblock dropped alone (dogfood aliases plugin-approvals to source, so no rebuild is involved)On committed head
f5587158, throughscripts/ablation-replace.mjs --delete, each restored withblob == HEADandgit diff HEADempty.approval_recall'saiblock deleted (anchor x1 → x0):Tests 3 failed | 5 passed (8)— the listing pin and both recall pins red.approval_resubmit'saiblock deleted (anchor x1 → x0):Tests 2 failed | 6 passed (8)— the listing pin and the resubmit pin red.Acceptance notes
sys_*objects, so an agent finds a request id through the app (as the dogfood file does through the system).resubmitapplies only to a request an approver sent back. The recall tool description says so, so an agent does not reach for resubmit after a recall.Verification
Head
f5587158(origin/main12b14462merged before the PR opened; main had not moved). Every exit captured before any pipe.Package tests and typecheck
pnpm --filter @objectstack/plugin-approvals test:Test Files 118 passed (118),Tests 1624 passed (1624).pnpm --filter @objectstack/plugin-approvals typecheck(tsc --noEmit+ scripts +check:test-typecheck): exit 0.pnpm --filter @objectstack/dogfood typecheck: exit 0.sys-approval-request.object.test.ts:Tests 18 passed (18);approval-decide-over-mcp.dogfood.test.ts:Tests 8 passed (8).pnpm --filter '@objectstack/dogfood^...' buildunder the verify lock, exit 0.Derived gates (
dispatch-gates.mjs --commands, 96) and lintcheck-plugin-teardown-shape.mjs --self-test: exit 3 on the shallow clone (pinned fixture commit unreachable); after fetching621a4876, exit 0 (48 cases).check:skill-examples,check:dts-closure,check:dual-build-cjs-loads: PREREQUISITE NOT MET (exit 3 / 1) — they read every package'sdist/, and this tree had built only the dogfood closure. NOT MEASURED at PR open; a whole-treeturbo run buildis running, and the re-run verdicts and the--ranreconciliation go into the os-dev report on MCP: a submitter's agent cannot recall or resubmit its own approval request — approval_recall / approval_resubmit are not ai.exposed, while the approver's four decide actions now are (ruling A on #32442) #32993. None of them reads a file this diff touches beyond the built output.pnpm lint(eslint . --no-inline-config): exit 0.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cwwUDMxb29FgtH4D53