Repository navigation
Description
eval#1 · os-bob · session_012dn2AcTB69pEwZ9ugr1g1w
Filing gate: ① product gap with a public-entry reproduction (an MCP client acting for the submitter), the submitter-side branch of ruling A on #32442 — per the meta-criterion "a new branch reuses the parent card's ruling and enters the queue directly". ⛔ Not a claim; triage decides lane and grade.
What happened
An end user's agent (a third-party MCP client, model GLM-5.3-Flash) operates an OA app on the os-dev-test environment for its user. It submitted an expense claim through the app's own submit_expense_claim action; the approval request opened (approval_status: pending). The user then updated the claim's line items and asked the agent to fill them in. The app locks the lines while the request is pending, so the agent needs to recall the request first. It cannot:
现在还撤不了 —— 这条单子的 approval_status 仍是 pending … 我这边没有撤回审批的可用动作(审批撤回属于审批中心的操作,MCP 只暴露了 submit_expense_claim 提交动作)。需要你操作一步:在 OA 里打开这张报销单 …,点「撤回」。
So the agent hands the step back to the person, who has to switch to the console for one click.
Why
On main (packages/plugins/plugin-approvals/src/sys-approval-request.object.ts), PR #32886 (decide half of ruling A on #32442) declared ai.exposed + requiresConfirmation: true on approval_approve, approval_reject, approval_reassign, approval_send_back. The submitter's own actions on the same object — approval_recall (:787) and approval_resubmit (:829) — stay unexposed. Ruling A covered the approver's side only ("the approval request's own decide actions"), so the submitter's half of the same process is still UI-only.
Expected
Same shape as ruling A, on the submitter's side: approval_recall and approval_resubmit declare ai.exposed with requiresConfirmation: true, forwarded to their approvals routes as the caller, so ApprovalService stays the only gate (its existing approval_recall_not_submitter refusal already keeps a non-submitter out). An agent can then "recall my claim, fix the lines, resubmit" end to end, with the user confirming each step.
Steps
- Any record-triggered or action-triggered flow with an
approvalnode; submit a record as user U (the request ispending). - Over MCP as U:
list_actions— no recall / resubmit action is listed (only the approver's decide actions, after feat(plugin-approvals): an approver's agent decides a pending approval request over MCP #32886). - The only way to recall is the console's 撤回 button (
POST /api/v1/approvals/requests/:id/recall).
Filing notes
- Business reason: every submit-then-correct cycle (expense, leave, purchase) run by an agent breaks at "recall"; North Star's "agents through MCP — every step of the road must hold for both kinds of user" covers the submitter as much as the approver.
- Reader who acts: the
domain:serverapprovals seat that landed feat(plugin-approvals): an approver's agent decides a pending approval request over MCP #32886 (same file, same pattern, its dogfood testapproval-decide-over-mcp.dogfood.test.tsextends to recall/resubmit). - Dedupe:
repo:objectstack-ai/objectstack is:issueforrecall approval MCP(206),submitter recall agent(28),approval_recall ai.exposed(2),approval resubmit MCP(38) — none on exposing the submitter's recall/resubmit to AI; [Decision] Should an approver be able to see and decide their pending approvals over MCP? #32442 (approver side, ruled A, decide half landed), A non-admin approver cannot open an approval request: GET /meta/object/sys_approval_request answers 404 since #32428, so the inbox row, ?request= links and the record panel lose Approve/Reject #32955 (approver cannot open a request, different).
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 13, 2026
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Triage: first touch →
domain:server· p1 ·pm:queue· 2026-11-13T02:44Z⛔ Not a claim.
domain:server(the approval actions over/api/v1/mcp). It is the submitter-side branch of ruling A on [Decision] Should an approver be able to see and decide their pending approvals over MCP? #32442, so it reuses that ruling and needs no new decision card.Generated by Claude Code
objectstack-fleet commentedon Nov 13, 2026
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Triage: regrade p1 → p2 under the adopted grading rules (#32997) · 2026-11-13T03:07Z
⛔ Not a claim; the card's claim and state are unchanged.
Generated by Claude Code
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Triage: regrade p2 → A submitter's agent cannot recall or resubmit, and it says so plainly and tells the user to recall in the console. That is a loud gap with a remedy, so the MCP focus raise does not apply (rule 8). A core-flow gap with a workaround is p2. under the adopted grading rules (#32997) · 2026-11-13T03:07Z
⛔ Not a claim; the card's claim and state are unchanged.
Generated by Claude Code
os-bob commentedon Nov 13, 2026
Re-grade on the maintainer's instruction:
priority:p2→priority:p0. State unchanged (pm:queue, unassigned). Written by a test session, not a seat.Generated by Claude Code
objectstack-fleet commentedon Nov 13, 2026
domain:server#8 · os-elon-musk · session_016WV4cwqTK71kjQzHae8T5J
Claim: PM loop round 3 (p0,
pm:queue; card ≡ 2 (mod 3); sub-issue of the tracking epic #32897, which has no seat of its own; the submitter-side branch of ruling A on #32442) · 2026-11-13T07:39ZSession:
session_016WV4cwqTK71kjQzHae8T5JAccount:
os-elon-muskBranch: the card thread's designated
claude/project-thread-*branch, written in its whole re-posted claim before the first pushWorktree: the card thread's own checkout
Domain:
domain:serverSeat:
domain:server#8File surface:
packages/plugins/plugin-approvals/src/sys-approval-request.object.ts(theapproval_recallandapproval_resubmitdeclarations only), the approvals action forwarding those two actions to their routes as the caller where #32886 forwarded the decide actions, tests beside them,packages/qa/dogfood/test/approval-decide-over-mcp.dogfood.test.ts(extended),content/docs/**where the decide actions' AI exposure is documented,.changeset/**(stop on breach; explain in the report)Container & model:
S,mode:thread,model: opus(TIER_DEFAULT unlessdispatch-gates.mjs --tierderives more),effort: highClause-②: yes (widening)
Metadata-protocol: none
Declaration note: two existing actions become visible and runnable to an AI caller (
ai.exposed,requiresConfirmation: true), the same shape #32886 gave the decide actions;ApprovalServicestays the only gateResponsibility:
n/a — gap, ruling A on #32442 extended to the submitter's side (the card's own filing gate)Thread-read: 6846917997
Premise: holds (measured on origin/main 157051c:
approval_recallatsys-approval-request.object.ts:796andapproval_resubmitat:838declare noaiblock;approval_recall_not_submitteris enforced inapproval-service.ts)Serial constraints cleared: no in-flight claim in this lane names
plugin-approvals; #32442's read half ispm:blockedon spec #32865 and touches the read door, not these declarationsobjectstack-fleet commentedon Nov 13, 2026
domain:server#8 · os-elon-musk · session_016WV4cwwUDMxb29FgtH4D53
Claim: PM loop round 3 (p0,
pm:queue; card ≡ 2 (mod 3); sub-issue of the tracking epic #32897, which has no seat of its own; the submitter-side branch of ruling A on #32442) · 2026-11-13T07:43Z — card-thread re-post of claim 6847131641 with its branchSession:
session_016WV4cwwUDMxb29FgtH4D53Account:
os-elon-muskBranch:
claude/project-thread-49wiahWorktree: the card thread's own checkout
Domain:
domain:serverSeat:
domain:server#8File surface:
packages/plugins/plugin-approvals/src/sys-approval-request.object.ts(theapproval_recallandapproval_resubmitdeclarations only), the approvals action forwarding those two actions to their routes as the caller where #32886 forwarded the decide actions, tests beside them,packages/qa/dogfood/test/approval-decide-over-mcp.dogfood.test.ts(extended),content/docs/**where the decide actions' AI exposure is documented,.changeset/**(stop on breach; explain in the report)Container & model:
S,mode:thread,model: opus(TIER_DEFAULT unlessdispatch-gates.mjs --tierderives more),effort: highClause-②: yes (widening)
Metadata-protocol: none
Declaration note: two existing actions become visible and runnable to an AI caller (
ai.exposed,requiresConfirmation: true), the same shape #32886 gave the decide actions;ApprovalServicestays the only gateResponsibility:
n/a — gap, ruling A on #32442 extended to the submitter's side (the card's own filing gate)Thread-read: 6847131641
Premise: holds (measured on origin/main 157051c:
approval_recallatsys-approval-request.object.ts:796andapproval_resubmitat:838declare noaiblock;approval_recall_not_submitteris enforced inapproval-service.ts)Serial constraints cleared: no in-flight claim in this lane names
plugin-approvals; #32442's read half ispm:blockedon spec #32865 and touches the read door, not these declarationsobjectstack-fleet commentedon Nov 13, 2026
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Triage: p0 → p1 · not an 18.4.0 blocker · 2026-11-13T08:00Z
⛔ Not a claim; the claim and state are unchanged, and the work continues.
mainor merge-queue red and the release window's blockers (「阻塞卡升 p0」). These six cards are MCP approval and upload capability work, not 18.4.0 regressions, so they carry p1: the highest working grade, first in their lanes after the blockers. That keeps the intent of the 07:27Z regrade (「…这些也都开始处理.这样我们审批相关的MCP就非常好用了.」) without holding the cut.Generated by Claude Code
objectstack-fleet commentedon Nov 13, 2026
domain:server#8 · os-elon-musk · session_016WV4cwwUDMxb29FgtH4D53
os-dev-report
{ "issue": 32993, "status": "done", "branch": "claude/project-thread-49wiah", "pr": "https://github.com/objectstack-ai/objectstack/pull/33076", "session": "session_016WV4cwwUDMxb29FgtH4D53", "premise_still_valid": true, "summary": "approval_recall and approval_resubmit on sys_approval_request declare ai: { exposed: true, description, requiresConfirmation: true }, the shape #32886 gave the four decisions. Both were already same-origin type 'api' actions targeting POST /api/v1/approvals/requests/{id}/recall and /resubmit, so run_action forwards them as the caller and ApprovalService stays the only gate (no check added in the MCP or action door). Tool descriptions are their own text (the recall dialog question stays in the top-level description): recall's says a recalled request is closed and re-sending goes through the app's own submit action; resubmit's says it applies only to a returned request. connect-mcp.mdx gains the submitter's bullet. Changeset plugin-approvals patch, Clause-②: yes (widening), Metadata-protocol: none. Head f5587158; draft PR, Fixes #32993; assignee and review request os-elon-musk.", "tests": "sys-approval-request.object.test.ts Tests 18 passed (18) (exposed set = 4 decisions + recall + resubmit; both confirm, name the submitter, target their own route; the recall question never armed as ai.description). approval-decide-over-mcp.dogfood.test.ts 5 -> 8, Tests 8 passed (8): list_actions for approver and submitter lists the six, each aiExposed + requiresConfirmation; submitter recall refused ACTION_CONFIRMATION_REQUIRED unconfirmed (still pending, nothing recorded), confirmed -> recalled, recorded once as recall by the submitter; the approver's recall refused FORBIDDEN carrying the approval_recall_not_submitter sentence (BUILTIN_OPERATION_MESSAGES.en), still pending; sent back over MCP, record edited, unconfirmed resubmit refused, approver's resubmit refused FORBIDDEN, no round 2, then the submitter's confirmed resubmit opens round 2. plugin-approvals test: Test Files 118 passed (118), Tests 1624 passed (1624); plugin-approvals typecheck exit 0; dogfood typecheck exit 0. Ablation (dogfood aliases plugin-approvals to source, no rebuild; committed head f5587158; scripts/ablation-replace.mjs --delete, each restored blob == HEAD, git diff HEAD empty): A recall ai block x1->x0: 3 failed | 5 passed (8) (listing + both recall pins); B resubmit ai block x1->x0: 2 failed | 6 passed (8) (listing + resubmit pin).", "gates": "Head f5587158 (origin/main 12b14462 merged before the PR; main had not moved). dispatch-gates --commands derived 96; 92 exit 0 on the first pass; check-plugin-teardown-shape --self-test exit 3 until fixture commit 621a4876 was fetched, then 0; check:skill-examples / check:dts-closure / check:dual-build-cjs-loads PREREQUISITE NOT MET until a whole-tree turbo build (120/120, exit 0), then all 0. dispatch-gates --ran: 96 run, 0 NOT-MEASURED. pnpm lint exit 0. The PR body's Verification names those three as NOT MEASURED at PR open; they are measured green here (seat may amend the body). CI check-runs on #33076 at report time: 9 success, 14 skipped, 7 in_progress (not re-run, not waited on).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "4 relay writes as objectstack-fleet[bot]: comment on #32993 (claim re-post 6847193267), pr_create (#33076), pr_request_reviewers (#33076, os-elon-musk), this report comment", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: #32865 · noted, not touched: the approver's read door (#32442 read half) is unchanged; MCP object tools still refuse sys_* objects, so an agent finds a request id through the app.", "carrier: none · noted: the card's 'recall, fix the lines, resubmit' story ends with the app's own submit action, because a recalled request is closed and resubmit applies only to a returned request; the recall tool description says so.", "carrier: none · noted: the #3424 override holder's agent can recall a pending request it did not submit, the same admission the console's Recall button already has (existing service rule, not a new reach)." ] }Generated by Claude Code