Repository navigation
docs(adr): ADR-0066 D4 api clause — same-origin type: 'api' actions are dispatched by the platform action door - #27664
Conversation
… are dispatched by the platform action door ADR-0066 D4 "Scope of the server half": the 2026-07 clarification (framework#3923) said a `type: 'api'` action is fetched by the browser directly and never passes through the platform action route. It is superseded, not deleted: a `type: 'api'` action whose `target` is a same-origin platform path is dispatched by `POST /actions/:object/:action` and the MCP `run_action` bridge, which run the anonymous baseline, the D4 gate and the ADR-0126 consult before forwarding in-process to the declared target as the caller; the endpoint keeps re-checking its own authorization (defence in depth); an off-origin api action stays browser-only and the activation write door refuses to flip it. The paragraph also drops `modal` from the dispatched types (the door answers it 400). ADR-0126 §8 item 2: one sentence recording that the action consult point is the `/actions` door and covers `type: 'api'` through it (§4: each consumer documents its consult point). packages/spec `action.zod.ts`: the `requiredPermissions` docblock and `.describe()` reworded to match; no schema shape change. The server half lands with the ruling's step 2; until then the door still refuses `type: 'api'`. Clause-②: no Metadata-protocol: semantic Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
…tion (gen:docs) `pnpm --filter @objectstack/spec check:generated --fix` regenerated the three reference pages that project the `requiredPermissions` `.describe()`: ui/action.mdx, data/object.mdx, kernel/metadata-plugin.mdx. Mechanical. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
📓 Docs Drift CheckThis PR changes 2 package(s): 28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 167 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 256d31a3d512d821a80a09e86609b7a53879a1d3 && git checkout 256d31a3d512d821a80a09e86609b7a53879a1d3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bfdaba4322e1258a7657aa622af021e31ab071aa 1d906476b9b45d7d1fddee3eb4137047d03e1501 && git checkout -B drift-repro bfdaba4322e1258a7657aa622af021e31ab071aa && git merge --no-ff 1d906476b9b45d7d1fddee3eb4137047d03e1501
node scripts/docs-audit/affected-docs.mjs --json bfdaba4322e1258a7657aa622af021e31ab071aa
|
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU Contract-review-hold: session_017ihL2dPRZXfgE1A2Vct3FU · head e44fd03 · CONTRACT_REVIEW_TIER unavailable (HTTP 429, 2026-10-29T17:11Z) and a patch round is coming for the findings below · until the patched head is pushed and CONTRACT_REVIEW_TIER answers again (then the seat runs the at-tier review or posts Pre-review findings · below tier · ⛔ not a contract review recordThese were read at
Verified right, at the same tier: the changeset is |
… obligation; same-origin defined; MCP sys_* gap recorded Patch round on the pre-review findings for the ADR-0066 D4 / ADR-0126 §8 amendment. - The Studio action form's `requiredPermissions` help text (and its en, zh-CN, ja-JP and es-ES bundles) no longer teaches the superseded clause or the false `modal` list. - ADR-0066 D4, the action.zod.ts docblock and `.describe()`: the door gates the calls it dispatches; the `target` endpoint stays directly reachable and MUST still enforce the declared capability itself, the door's gate being the added check. The Amended note supersedes only the browser-only half of the 2026-07 clarification; its re-check clause stands. - `.describe()` reads "on every call the door dispatches", true before and after the server half lands. - The Amended note records that MCP keeps its `ai.exposed` opt-in and fail-closed `sys_*` guard, so `sys_user.ban_user` and the like reach an agent through REST `POST /actions`, not MCP. - ADR-0126 §8: the switch stops the action's dispatch, not a direct call to the endpoint; the console click meets the consult from step 3. - "Same-origin platform path" defined as the server predicate judges it: the declared `target`, before interpolation, starts with exactly one `/` and goes on past it. - The `operation: 'update'` refusal no longer says the browser calls an api action's endpoint. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429, 2026-10-29T18:13Z) · head 7b404e6 The patch round for the below-tier findings in 6503385688 has landed on this head: M1, M2 and S1–S4 are all applied (delta os-dev-report 6504664624 on #27610). The seat's own probe of the mandated tier was still refused just now. The at-tier full review (ADR + spec face, read against #27649 as the first consumer) is owed on this head. Whichever comes first runs it: triage per contract-review.md (a), or this seat once its tier answers. The PR does not land before that. The seat keeps the dev's retranslation of the zh-CN / ja-JP / es-ES helpText in |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 7b404e6 · 2026-10-29T18:17Z Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: ① Derived judgmentsCheck-runs on this head at the final read: 50 runs, 41
② Semver level
③ Boundary flagsPR author is Dev flags, round 1 (os-dev-report 6502938739):
Dev flags, round 2 (os-dev-report 6504664624):
Seat flags (6504717588): translations kept in scope — agree (above). The PR body is stale against this head (item 1 still reads "keeps re-checking its own authorization", item 5 omits Dev out-of-scope observations: the ui docblocks restating the superseded clause are the ui card's (#27611, open, Blocked-by #27506) — the dev's list misses two: Below-tier findings 6503385688 (M1, M2, S1–S4) — each checked on the tree, not on the delta report: M1 applied (action.form.ts L178 and the four bundles); M2 applied (ADR-0066 L3, L74, L76; docblock; describe — "the door gates the calls it dispatches, and only those … MUST still enforce"); S1 applied ("on every call the door dispatches"); S2 applied (the ④ Design judgmentdocs/adr/0066-unified-authorization-model.md — against the ruling 6499781492, the first consumer #27649 (head a87722d) and the three-step plan.
docs/adr/0126-packaged-metadata-customization-model.md §8 item 2 — against §4 and the first consumer. Holds: the consult point is packages/spec/src/ui/action.zod.ts — against the ruling, ADR-0066 D4 as amended, and #27649.
packages/spec/src/ui/action.form.ts L178 — against the describe and ADR-0066 D4. Holds: same meaning as the describe, phrased for a Studio author; true before and after #27649; the packages/spec/src/ui/action-declarative-update.ts L124 — against the ADR and the executor contract. Holds: the refusal's parenthetical is true for all three steps; "performed by the platform action route" in the same sentence is the pre-existing name for the same door and is not false. No finding. Must-fix: M1. Should-fix: S1, S2, S3; the ③ escalations (Skills-docs coverage in #27649; the two RelatedList restatements for #27611) are for the seats named, not for this diff. Implemented-by: VERDICT: FAIL Generated by Claude Code |
… paragraph dated; `/` alone excluded
Patch round on the at-tier contract review of the ADR-0066 D4 / ADR-0126
§8 amendment.
- ADR-0066 D4: the target's `Set-Cookie` is returned on the REST route's
response only; the MCP bridge relays only the bearer-lane rotation
headers (`set-auth-token`, `set-admin-session-token`), and no cookie
value enters a tool result.
- ADR-0066 D4: the `isSameOriginApiTarget` citation names the anchor the
server PR rewrites it into once the symbol is on main.
- ADR-0066 Amended note: the recorded MCP `sys_*` gap names its carrier
card.
- action.zod.ts docblock: the Landing paragraph is dated and names who
removes each of its clauses.
- action.zod.ts `.describe()`: the same-origin gloss also excludes `/`
alone ("with a path after it"); the three generated reference rows are
regenerated (gen:docs).
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head a594447 · 2026-10-29T18:56Z Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: ① Derived judgmentsCheck-runs on this head at the final read: 50 runs, 41
② Semver levelStands as judged in 6505129020 ②: ③ Boundary flagsPR author is Dev flags, rounds 1 and 2 (os-dev-reports 6502938739, 6504664624) — answered in 6505129020 ③; nothing in the move reopens them. The card carries no round-3 os-dev-report at the read (its last comment is 6504664624, 18:11Z; the move is 18:44Z): the move's account is its commit message, which names the four findings it lands and nothing else; this record judged the tree, not that account. A process observation for the seat, not a finding on Escalations of 6505129020 ③, re-read on the trees named:
New boundary item raised by the move — escalated, and graded in ④ S1: ADR-0066 L74 and the action.zod.ts docblock now state that PR #27649 performs two edits on landing (rewrites the ④ Design judgmentdocs/adr/0066-unified-authorization-model.md — the move touches L74 and L76; judged against the ruling 6499781492, the first consumer #27649 at 533f8da, and the three-step plan.
docs/adr/0126-packaged-metadata-customization-model.md §8 item 2 — not touched by the move; stands as judged in 6505129020 ④ (consult point is packages/spec/src/ui/action.zod.ts — the move touches the docblock Landing paragraph (L1614–1619) and the describe (L1621).
packages/spec/src/ui/action.form.ts L178 — not touched by the move; stands as judged in 6505129020 ④. No finding. packages/spec/src/ui/action-declarative-update.ts L124 — not touched by the move; stands as judged in 6505129020 ④. No finding. Must-fix: none. Should-fix: S1 (ADR-0066 L74 landing clause and the action.zod.ts Landing paragraph's remover sentence — record the obligation on #27649 or on a docs card Blocked-by #27649 and stop naming the PR). The ③ item (stale PR body) is the seat's at ACCEPT, not a tree change. Implemented-by: VERDICT: PASS Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK On should-fix S1 of record 6505934898 (the two plan sentences that hand #27649 an edit to So the remedy is the second one: on this PR, reword both sentences to say that once the server half (#27506 / #27649) lands, the door clause is removed by a docs card that is Blocked-by #27649, and file that card ( Generated by Claude Code |
…low-up edits go to a docs card, not the server PR Delta-review should-fix on the ADR-0066 D4 amendment: the two sentences that handed follow-up edits to the server PR now name the docs card that carries them, Blocked-by that PR, because the server PR does not take them. - ADR-0066 D4: once `isSameOriginApiTarget` is on main, the docs card rewrites its bare-name citation into a symbol anchor. - action.zod.ts docblock: once the server half lands, the docs card removes the Landing paragraph's door clause; its console clause goes when step 3 lands. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2dPRZXfgE1A2Vct3FU
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 1d90647 · 2026-10-29T19:31Z Generated by Claude Code |
|
triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK Contract reviewServed-tier: ① Derived judgmentsCheck-runs on this head at the final read (2026-10-29T19:45:12Z): 50 runs, 41
② Semver levelStands as judged in 6505934898 ② and 6505129020 ②: ③ Boundary flagsPR author is Dev flags, rounds 1 and 2 (os-dev-reports 6502938739, 6504664624) — answered in 6505129020 ③ and 6505934898 ③; nothing in the move reopens them. The card still carries no round-3 os-dev-report at the read (its last comment is 6504664624, 18:11Z; the move is 19:21Z): the move's account is its commit message and the body of card #27694 it filed; this record judged the tree. A process observation for the seat, not a finding on Escalations of 6505934898 ③, re-read on the trees named:
New boundary item raised by the move — escalated, and graded in ④ S1: the carrier card #27694 is ④ Design judgmentdocs/adr/0066-unified-authorization-model.md — the move touches L74 only, one parenthetical; judged against the ruling's execution plan (6499781492: the ADR card "lands before the server PR lands", so the ADR records the decision and may not order work of a later PR), and against the first consumer #27649 at 533f8da.
docs/adr/0126-packaged-metadata-customization-model.md §8 item 2 — not touched by the move; stands as judged in 6505934898 and 6505129020 ④. No finding. packages/spec/src/ui/action.zod.ts — the move touches L1617–1619, the last sentence of the docblock's Landing paragraph; the describe at L1621 is byte-identical to a594447.
packages/spec/src/ui/action.form.ts L178 — not touched by the move; stands as judged in 6505934898 and 6505129020 ④. No finding. packages/spec/src/ui/action-declarative-update.ts L124 — not touched by the move; stands as judged in 6505934898 and 6505129020 ④. No finding. Must-fix: none. Should-fix: S1 (card #27694: add the line-initial Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU 席位核对 — ACCEPT · #27610 · head
|
|
domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU 更正 ACCEPT 6506803986 的「两分钟验证」· 2026-10-29T19:52Z: |
Fixes #27610
Clause-②: no
Metadata-protocol: semantic
Skills-docs: follow-up #27506 —
content/docs/ui/actions.mdx(the type table, the "api → 400, call the endpoint directly" rows),content/docs/ai/connect-mcp.mdx(api never listed bylist_actions) andcontent/docs/ai/actions-as-tools.mdxdescribe today's door truthfully and are rewritten when the server half lands; noskills/**file teaches the superseded clause (grep: zero hits), and the three generated reference pages are regenerated here.维护者速读
type: 'api'动作经平台动作门(POST /actions/OBJECT/ACTION与 MCPrun_action)分发,门上跑 D4 门禁后转发,端点自身复核保留为纵深防御;非同源 target 维持仅浏览器、不受治理,激活开关拒绝切换」;2026-07 原条款原文保留并标为已被取代。ADR-0126 §8 第 2 项补一句:动作的 consult point 就是/actions这扇门,api 型同样经它。action.zod.ts的requiredPermissions注释与.describe()同步改写(无 schema 形状变化),三份生成的参考文档随gen:docs重生成。type: 'api'」。回滚 = revert 本 PR。docs/adr/0066-unified-authorization-model.mdD4 段与其下的 Amended 注(第 74、76 行);②packages/spec/src/ui/action.zod.ts的requiredPermissionsdocblock 与.describe()(第 1593–1620 行);③docs/adr/0126-packaged-metadata-customization-model.md§8 第 2 项末句。pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated— 预期 「All 14 generated artifacts are up to date」;pnpm check:adr-anchors预期退出 0。docs/adr/**+packages/spec/src/**,Tier H,由维护者落地)。What changes
Step 1 of the three-step ruling on #27506 (comment 6499781492). ADR and spec-text amendment only: no runtime code, no schema shape change.
docs/adr/0066-unified-authorization-model.md, D4 "Scope of the server half". The live paragraph now reads: the platform action door —POST /api/v1/actions/OBJECT/ACTIONand the MCPrun_actionbridge, one gate (actionPermissionError) — dispatchesscript,flow, the declarativeoperation: 'update'write, and atype: 'api'action whosetargetis a same-origin platform path. For that api action the door runs the anonymous baseline, the D4 gate and the ADR-0126 §8 consult, then forwards in-process to the declaredtargetas the caller (request assembled frommethod/recordIdParam/bodyShape/bodyExtra/ the placeholders intarget; cookies and auth headers carried;Set-Cookiereturned). The endpoint keeps re-checking its own authorization: defence in depth, not a replacement. An off-origin api action stays browser-only and ungoverned: the door does not dispatch it, MCP does not list it, and the activation write door (handleActionActivationWrite) refuses to flip it with a reason. A block-quoted Amended (2026-10-29) note keeps the 2026-07 clause (framework#3923) verbatim as superseded, carries the ruling reference and the maintainer's words untranslated, records the three landing steps, and states today's behaviour (isHeadlessInvokableActionadmitsscript,flowand the declarative update only). The Status line gains the matching· **Amended** (2026-10-29, …)entry in the file's existing style. The paragraph also dropsmodalfrom the dispatched types: onorigin/mainthe door answers amodalaction 400 as a client-side type (headlessActionTypeError), so the 2026-07 list was already inaccurate.docs/adr/0126-packaged-metadata-customization-model.md§8 item 2. One sentence: the action consult point is the/actionsdoor (disabledActionRefusal, both doors) and coverstype: 'api'through it per ADR-0066 D4 as amended, with the off-origin exception — per §4 "each consumer documents its consult point".packages/spec/src/ui/action.zod.ts,requiredPermissions. Docblock and.describe()reworded to the amended rule, with a landing note in the docblock. No schema shape change. The old "fetched by the browser / never passes the route / endpoint must re-check" wording and thescript/flow/modallist are gone from the spec.pnpm --filter @objectstack/spec check:generated --fix(onlycheck:docswas stale):content/docs/references/ui/action.mdx,content/docs/references/data/object.mdx,content/docs/references/kernel/metadata-plugin.mdx— one table row each. Never hand-edited..changeset/27610-adr-0066-d4-api-actions-door.md:@objectstack/specminor,Clause-②: no,Metadata-protocol: semantic. The card and the claim saidpatch; ruling A (check-changeset-no-major.mjs, the level axis) grades a declared kind on a moved packageminorand refusespatchasenforce, and AGENTS.md Post-Task Checklist step 3 says the same, so the level follows the kind. Flagged in the report as a dispatch-vs-standard-clause conflict, resolved in the standard clause's favour.Every other sentence on
mainthat restates the old api clause, found by grep (fetched by the browser,called by the browser,self-authored endpoint,pointed at a custom endpoint,never passes through that route,platform never sees the request) and classified:docs/adr/0066L74 (and the Status line L3),packages/spec/src/ui/action.zod.tsdocblock +.describe(), the three generated reference pages.action.zod.tsthe alias comment nearpermissions: 'requiredPermissions'and thehiddenguidance ("enforced with a 403 on the platform action route").action.zod.tsonSuccess/outcomeMessages("thetargetcall's response body") andundoable("type: 'api'is fulfilled by the console").Skills-docs:line for Packaged action activation: switching off an api-type packaged action (Ban User) does not stop it, because the activation consult runs only at action dispatch #27506:content/docs/ui/actions.mdx(type table; "api → 400 — it dispatches ontarget; call that endpoint directly"; the 400 troubleshooting row),content/docs/ai/connect-mcp.mdx("url,modal,form, andapihave no headless …";list_actionsrow),content/docs/ai/actions-as-tools.mdx(type table row).packages/runtime/src/action-execution.tsheadlessActionTypeErrordocblock and its api prescription ("Call itstargetendpoint directly"),packages/runtime/src/domains/actions.tsthe type-dispatch comment ("aurl/modal/form/apiaction reaching this endpoint has nowhere to go").ui/packages/components/src/renderers/layout/containers.tsx(capability-gate comment: "never for atype: 'api'action pointed at a custom endpoint"),ui/packages/components/src/__tests__/action-bar.test.tsx,ui/packages/components/src/__tests__/page-header-capability-gate.test.tsx,ui/packages/plugin-grid/src/__tests__/bulkActionCapabilityGate.test.tsx(the same sentence in their docblocks).Tests
No behaviour changes, so no new tests. The affected package's suites were run under the shared verify lock on head
e44fd034:`pnpm --filter @objectstack/spec typecheck` — lock VERDICT command-exit 0 (79s)
`pnpm --filter @objectstack/spec test` — lock VERDICT command-exit 0 (204s): Test Files 752 passed (752) · Tests 22343 passed | 1 todo (22344)
Ablation: none — the diff changes no tested behaviour (prose, a docblock, a
.describe()and its generated projection).Acceptance notes
minor, not thepatchthe card named — see "What changes" item 5. If the maintainer wantspatch, the kind would have to benone, which the triage and the claim both ruled out (semantic).modaldropped from the D4 list. Factual correction inside the amended paragraph, verified againstorigin/main0e33108a:SERVER_DISPATCHED_ACTION_TYPESisscript,flow;headlessActionTypeErrorreturns the client-side prescription formodaland the route answers it 400 (domains/actions.ts, the type-dispatch branch). Listed as a changed sentence in the report..describe()cites "ADR-0066 D4 as amended 2026-10-29" (no tracker id in a published string). The runtime onmainis unchanged by this PR:isHeadlessInvokableActionadmitsscript,flowand the declarative update only;handleActionActivationWritedoes not yet refuse an api action; the console still fetchestarget.ui/docblocks above restate the superseded clause and are the ui card's to rewrite;content/docs/ui/actions.mdxrow "api| 400" andconnect-mcp.mdx'slist_actionsrow are Packaged action activation: switching off an api-type packaged action (Ban User) does not stop it, because the activation consult runs only at action dispatch #27506's docs to flip.scripts/adr-anchorschange:pnpm check:adr-anchorsexit 0; no anchor namesdocs/adr/0066*,docs/adr/0126*oraction.zod.ts.Verification
Head
e44fd034(branchclaude/issue-27610-adr-0066-d4-api-actions-door, baseorigin/mainat merge base0e33108a). Diff: 7 files, +49/−16.Build + generated (lock VERDICT 0; `check:generated` 1 of 14 stale → `--fix` → 14/14 up to date)
Derived gates (`node scripts/pm/dispatch-gates.mjs --commands`, 101 families on the final diff): 94 exit 0, 7 exit 3 (prerequisite not met — NOT MEASURED locally, CI measures); `--ran` reconciliation: 101 derived · 94 run · 7 NOT-MEASURED · 0 UNRUN, exit 0
Local-only families (readings, not gates)
Dogfood battery: n/a — no
!in the title and no(narrowing)arm.Generated by Claude Code