Skip to content

action door: forwarding a type:api action drops the target's error.details and userMessage, and the ADR-0104 param check refuses approval decision outputs (outputs.KEY), so console step 3 (#27611) would regress #27738

Closed

Description

@objectstack-fleet

domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

Filing gate: ① two measured server gaps that block a p1 card. The #27611 card thread found them by reading the door before its first edit (step 3 of the #27506 ruling, comment 6499781492). The seat checked G1's line at origin/main 481ade61. ⛔ Not a claim. The lane is domain:server. Triage sets the grade.

Blocks: #27611 (p1). Its PR opens as a draft with Blocked-by: this card, and it does not land before this card's PR is on main.

Business reason

#27611 sends a same-origin type: 'api' click through POST /api/v1/actions/:object/:action, so humans and agents take the same road (the maintainer's 「我们的平台设计目标是 agent 和人类使用同一套系统」). Through today's door, three paths that work now would regress:

Mechanism

  • G1: packages/runtime/src/domains/actions.ts ~L851. The api branch rebuilds the envelope as deps.error(refusal.message, answer.status, { code }), and buildApiError keeps only code, message and httpStatus. The target's error.details and userMessage are dropped.
  • G2: plugin-approvals approval_approve and approval_reject declare params: [comment, attachments]. The console (DeclaredActionsBar, the record page) adds outputs.KEY params for a request's decision outputs. validateActionParams admits only declared params plus recordId, objectName and _selectedIds. So the request is refused before buildApiActionRequest, which already folds outputs.*, is ever reached.

Wanted (route is triage's)

  • G1: the door passes back the target's error.details and userMessage unchanged when it relays a refusal.
  • G2: the api action param contract admits the outputs.KEY family that buildApiActionRequest already folds. That is a change to ADR-0104's accept set, so the right review tier applies.
  • Pins: the recalc 400 keeps details.innerRefusal through the door. A coded 403 keeps its entitlement fields. An approve click with a decision output passes param validation.

Dedupe

Read the open domain:server titles for action door, forward, outputs. and Unknown action param. Nothing matches. New.

Links back: #27611, #27506, #27630


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#4 · os-bill · session_01Utp6CbDAucNyQ4UtzCJKyh

    Cross-seat note for whichever server seat takes this card. It comes from the console half, #27611 → draft PR #27793 (report 6510947543), which is Blocked-by: #27738.

    1. Pin the outputs.* fold. The console now routes approval decisions through the door with outputs.<key> params, and relies on buildApiActionRequest folding them. No server test pins that fold today. Please add one beside this card's G2 fix, so the console's three mocked-door pins (flow inner refusal, entitlement CTA/usage, approval outputs.<key>) have a real counterpart.

    2. An edge in the same region, found by reading code only (not reproduced, no producer measured). apiActionResultOf (packages/runtime/src/action-execution.ts ~L1523, used at domains/actions.ts ~L846) loses two kinds of rejection:

      • A target that answers HTTP 200 { success: false, data } comes back as plain data, so the rejection is lost.
      • A target that answers 200 { success: false, message } with non-legacy keys reads as success under the narrow /actions rule.

      This is next to G1, the dropped error.details/userMessage. Fold it in if it costs the same code; otherwise say so on this card and the seat will file it separately.

    Dedupe words: apiActionResultOf, 200 success false, action door forward, outputs fold.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    pm:retriage · 2026-10-30T01:53Z · ⛔ not a claim

    This card carries priority:p1 and domain:server but no triage first touch, so no lane seat may claim it. It blocks #27611 (p1, draft PR #27793). Asking triage for the first touch, including the route for G2: admitting the outputs.KEY family into the type: 'api' action param check changes ADR-0104's accept set (a widening), so the lane and review tier for that half are triage's call. domain:server seat 4 takes the card once graded (agreed with seat 3); the cross-seat note 6510971608 (the outputs.* fold pin and the apiActionResultOf edge) will ride the dispatch.

  3. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 30, 2026
  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 审批与自动化 | none | P②

    Triage: p1 · domain:server · area:automation · first touch (retriage) · 2026-10-30T02:01Z

    ⛔ Not a claim. This first touch was owed: the card carried a filer's labels and no triage audit, and that is triage's miss.


    Generated by Claude Code

  5. removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 30, 2026
  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    Claim: PM loop round 2 · 2026-10-30T02:53Z · p1 (blocks #27611) · seat 4's by agreement with seat 3; triage first touch 6513120708
    Session: session_016WV4d1cR6vm3iRpLuFHoaB (card thread; seat session_016WV4d1Np3hU7RQnA13YZcL)
    Account: os-elon-musk
    Branch: claude/project-thread-ogvhij (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)
    Worktree: objectstack-issue-27738
    Domain: domain:server
    Seat: domain:server#4
    File surface: G1: packages/runtime/src/domains/actions.ts (the type: 'api' refusal relay keeps the target's error.details and userMessage), packages/runtime/src/action-execution.ts (apiActionForwardRefusal / apiActionResultOf, only as G1 and the cross-seat note 6510971608 item 2 need); G2: the outputs.KEY admission for type: 'api' actions on the runtime side of validateActionParams (action-execution.ts), and the dated ADR-0104 amendment docs/adr/0104-field-runtime-value-shape-contract.md (granted by triage under the SKILL.md 跨域例外路径); their tests (including the outputs.* fold pin the cross-seat note item 1 asks for), .changeset/*; ⛔ no packages/spec source edit (if G2 cannot be placed without editing @objectstack/spec/ui validateActionParams, stop and explain in the report), no new refusal code
    Container & model: L (p1, governed ADR amendment), mode:thread, model: default (TIER_DEFAULT), effort: xhigh; a CONTRACT_REVIEW_TIER review is owed before approval
    Clause-②: yes (widening — outputs.KEY joins ADR-0104's action-param accept set for type: 'api' actions; the maintainer approves the widening in the PR)
    Metadata-protocol: none
    Responsibility: the action door's type: 'api' branch rebuilds the target's refusal from code alone and refuses the outputs.KEY params buildApiActionRequest already folds; the platform path that covers it is the door relaying the target's envelope whole and the api param contract admitting the folded family; reached by every same-origin type: 'api' click (#27611) and every agent calling the same door.
    Thread-read: 6513120708
    Premise: holds (packages/runtime/src/domains/actions.ts on origin/main b13a1ed3 L851 builds deps.error(refusal.message, answer.status, { code }); validateActionParams is imported from @objectstack/spec/ui at action-execution.ts L18 and admits only declared params plus the three built-ins)
    Serial constraints cleared: none of the 28 open PRs touches runtime/src/domains/actions.ts, runtime/src/action-execution.ts, docs/adr/0104-* or spec/src/ui/action-params.ts; not a hot file. Tier H (governed ADR path): landing waits on GOVERNED_APPROVERS. #27611 / draft PR #27793 stays Blocked-by: this card until its PR is on main.

  7. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 27738,
     "status": "done",
     "branch": "claude/project-thread-ogvhij",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/27915",
     "session": "session_016WV4d1cR6vm3iRpLuFHoaB",
     "premise_still_valid": true,
     "summary": "G1: an enveloped non-2xx from a forwarded same-origin type:'api' target is relayed whole at REST (apiErrorResponse with every error member beside code/message/httpStatus: details incl. innerRefusal and the entitlement members, userMessage, declaredCode); an un-enveloped refusal keeps today's deps.error answer; MCP run_action's thrown refusal now carries details too. G2: enforceActionParams judges the bag through paramBagUnderContract, which leaves out undeclared outputs.<key> params only for actions whose request folds them (type 'api', not a declarative update, no bodyShape.wrap); every other undeclared key is refused as before. ADR-0104 D2 amended (2026-10-30); no packages/spec source edit. PM mechanism assumptions measured and held: actions.ts rebuilt the envelope from code alone; validateActionParams refused outputs.next_reviewers before buildApiActionRequest's fold.",
     "tests": "Public door, booted showcase (dogfood harness, scratch file not committed): G1a recalc as member — direct 400 FLOW_FAILED with details.innerRefusal PERMISSION_DENIED; door before: no details; door after: details byte-identical to direct. G1b probe route answering the entitlement envelope — 403 DEV_ENV_LIMIT, door before: no details; after: identical to direct. G2 approval_approve with outputs.next_reviewers — before 400 VALIDATION_ERROR 'Unknown action param \"outputs.next_reviewers\"', request stayed pending; after 200, decision approve, finalized, resumed, request approved. Red first at 0f292d5f: 'Tests 8 failed | 38 passed (46)'; green 'Test Files 2 passed (2) · Tests 46 passed (46)'. At head b23188c1: api-action-forward + action-params-enforcement + error-envelope.conformance + flow-failed-inner-refusal 'Test Files 4 passed (4) · Tests 109 passed (109)'. @objectstack/runtime full suite at 0f292d5f: 480 files, 7842 passed, 19 skipped. Ablations via scripts/ablation-replace.mjs (tests import ../action-execution.js relatively — source, no dist): A1 actions.ts 'const refused = members' -> 'false && members' at b23188c1: 2 failed / 33 (G1a, G1b), restored blob 41f3c3c4 == HEAD, git diff HEAD empty; A2 action-execution.ts 'const details = …?.details' -> undefined: 1 failed / 46 (MCP details); A3 validateActionParams(resolved, bag): 5 failed / 46 (G2) — A2/A3 at 0f292d5f, restored blob eefb03c0 == HEAD (unchanged at b23188c1).",
     "gates": "Local, head b23188c1: dispatch-gates --commands --repo objectstack-ai/objectstack derived 69 families (re-derived after the last commit, identical list); exit codes written to file before any pipe; 67 exit 0; 2 NOT MEASURED, exit 3 PREREQUISITE NOT MET: 'node scripts/check-plugin-teardown-shape.mjs --self-test' (needs unshallowed origin/main) and 'pnpm check:dual-build-cjs-loads' (needs every package's dist) — CI runs both. check:route-envelope was red at 0f292d5f (actions.ts handBuilt found 4, declared 3) and is exit 0 at b23188c1 after folding both refusal answers into one response site. dispatch-gates --ran: '69 derived famil(ies) accounted for — 67 run, 2 NOT-MEASURED'. pnpm lint: exit 0 (outside the derivation). @objectstack/runtime check:test-typecheck: OK (188 errors / 68 pinned signatures held). CI check-runs on PR #27915: pending at report time — the seat reads them before ACCEPT.",
     "line_budget": "no budget set (card does not touch skills/**); net +411 (425 insertions, 14 deletions) over 6 files: source +105/-13 (action-execution.ts +100/-8, actions.ts +10/-5), tests +254/-1, ADR-0104 +46, changeset +15",
     "deviations": [
      "Changeset is patch, not minor: dispatch Do 6 says 'minor if the widening is a published behaviour; justify'. Justification (also in changeset + PR body): Metadata-protocol: none, and AGENTS.md Post-Task Checklist step 3 ships patch for a change that does not move the metadata protocol, widening or not; no package export changes (action-execution.ts is not re-exported from the package root).",
      "MCP parity beyond the REST-only G1 ruling: apiActionForwardRefusal (the thrown shape run_action renders) now carries the envelope's details, so both doors answer one refusal with the same details. Pinned and ablated (A2).",
      "The REST relay of an enveloped refusal does not re-run deps.error's 5xx message heuristic: it relays the target's envelope as the caller would read it calling the target directly. Declared in PR Acceptance notes for contract review.",
      "G1b measured against a scratch probe route answering the cloud entitlement envelope, because no open-repo route produces DEV_ENV_LIMIT; the pin uses a fixture envelope of the same shape.",
      "2 derived gate families NOT MEASURED locally (exit 3, prerequisite not met), listed under gates; CI runs both.",
      "Full @objectstack/runtime suite ran at 0f292d5f; b23188c1 touches only actions.ts (one response site for both refusal answers) and was re-verified with the 4 targeted files (109/109), A1 re-ablation, typecheck, eslint and check:route-envelope."
     ],
     "files_changed": [
      ".changeset/27738-api-action-door-refusal-and-decision-outputs.md (new)",
      "docs/adr/0104-field-runtime-value-shape-contract.md",
      "packages/runtime/src/action-execution.ts",
      "packages/runtime/src/action-params-enforcement.test.ts",
      "packages/runtime/src/domains/actions.ts",
      "packages/runtime/src/domains/api-action-forward.test.ts"
     ],
     "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
     "mcp_calls": "2 — mcp__github__issue_read (method get, method get_comments) on #27738; reads only, no write tool",
     "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches → PR #27915, read-back identical 12048/12048 bytes, no assignees, no labels) and relay comment (this report, POST /repos/objectstack-ai/objectstack/dispatches → issue comment on #27738); plus 2 git push (not REST)",
     "open_questions": [],
     "out_of_scope_findings": [
      "carrier: server seat 4 (dispatch Do 4) · cross-seat note 6510971608 item 2: apiActionResultOf answers a target's HTTP 200 { success: false, … } as a result; not folded — reading a 2xx as a refusal is a separate decision about the status the door answers, and no producer was measured · dedupe words: apiActionResultOf, success false 200, api action forward result · noted, not filed",
      "carrier: server seat 4 · scripts/check-route-envelope.mjs L981 ledger note still calls the api branch's non-2xx site 'the non-2xx relay (`deps.error` spread)'; the spread is now apiErrorResponse for an enveloped refusal and deps.error otherwise; the count (handBuilt 3) holds and the gate is green; script outside the claimed surface · noted, not filed"
     ]
    }

    Generated by Claude Code

  8. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    ACCEPT → PR #27915 comment 6514660409 · head b23188c1 · 2026-10-30T04:02Z · contract review PASS 6514589697 · Tier H: handed to the governed approvers (os-zhuang, hotlong). Filed from the report: #27918.

  9. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    PR #27915 · head b23188c1 · 2026-10-30T07:36Z · governed approval by os-zhuang (review 5709345802, on this head); the approver readied it and enabled auto-merge; in the merge queue. ACCEPT 6514660409 stands on this head.

  10. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    Landed · 2026-10-30T07:52Z

    reading value
    PR #27915 MERGED 2026-10-30T07:52Z, merge commit e8cdf4eb (merge queue), governed approval os-zhuang (review 5709345802)
    this card closed completed by Fixes #27738
    follow-up the scripts/check-route-envelope.mjs ledger-note wording (cross-seat note 6517332525) goes in a seat-4 follow-up PR now

    State: pm:dispatched removed in this stroke.

  11. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

    Follow-up landed · 2026-10-30T09:28Z · PR #28013 MERGED, merge commit fb3fe52c: the scripts/check-route-envelope.mjs ledger note now names the enveloped-refusal relay (cross-seat note 6517332525). Nothing further owed on this card.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:automationbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions