Skip to content

docs(adr): ADR-0066 D4 api clause — same-origin type: 'api' actions are dispatched by the platform action door - #27664

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-27610-adr-0066-d4-api-actions-door
Oct 29, 2026
Merged

os-zhuang merged 5 commits into
mainfrom
claude/issue-27610-adr-0066-d4-api-actions-door

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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 by list_actions) and content/docs/ai/actions-as-tools.mdx describe today's door truthfully and are rewritten when the server half lands; no skills/** file teaches the superseded clause (grep: zero hits), and the three generated reference pages are regenerated here.

维护者速读

  • 改了什么:ADR-0066 D4 的「api 型动作由浏览器直连、不经过平台动作路由」条款按裁决(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 评论 6499781492)改为「同源 type: 'api' 动作经平台动作门(POST /actions/OBJECT/ACTION 与 MCP run_action)分发,门上跑 D4 门禁后转发,端点自身复核保留为纵深防御;非同源 target 维持仅浏览器、不受治理,激活开关拒绝切换」;2026-07 原条款原文保留并标为已被取代。ADR-0126 §8 第 2 项补一句:动作的 consult point 就是 /actions 这扇门,api 型同样经它。action.zod.ts 的 requiredPermissions 注释与 .describe() 同步改写(无 schema 形状变化),三份生成的参考文档随 gen:docs 重生成。
  • 为什么改:维护者裁决 「我们的平台设计目标是 agent 和人类使用同一套系统」「同意」;今天 70 余个 api 型动作(封禁、模拟登录、改密)只有人类点击能到,agent 调不到,ADR-0126 开关对它不生效。Prime Directive [WIP] Add Chinese version of the documentation #13:已记录的决定由 ADR 修订推翻,不靠 changeset 偷改。本 PR 是三步裁决的第 1 步,只改文字。
  • 风险与回滚:零运行时代码、零 schema 变化;风险只在文字先于实现——ADR 与 docblock 均注明「服务端半边由 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 落地,落地前门仍拒绝 type: 'api'」。回滚 = revert 本 PR。
  • 先看:① docs/adr/0066-unified-authorization-model.md D4 段与其下的 Amended 注(第 74、76 行);② packages/spec/src/ui/action.zod.ts 的 requiredPermissions docblock 与 .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。
  • 需要你做的:审阅并合并本 PR(受管面 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.

  1. 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/ACTION and the MCP run_action bridge, one gate (actionPermissionError) — dispatches script, flow, the declarative operation: 'update' write, and a type: 'api' action whose target is 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 declared target as the caller (request assembled from method / recordIdParam / bodyShape / bodyExtra / the placeholders in target; cookies and auth headers carried; Set-Cookie returned). 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 (isHeadlessInvokableAction admits script, flow and the declarative update only). The Status line gains the matching · **Amended** (2026-10-29, …) entry in the file's existing style. The paragraph also drops modal from the dispatched types: on origin/main the door answers a modal action 400 as a client-side type (headlessActionTypeError), so the 2026-07 list was already inaccurate.
  2. docs/adr/0126-packaged-metadata-customization-model.md §8 item 2. One sentence: the action consult point is the /actions door (disabledActionRefusal, both doors) and covers type: 'api' through it per ADR-0066 D4 as amended, with the off-origin exception — per §4 "each consumer documents its consult point".
  3. 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 the script/flow/modal list are gone from the spec.
  4. Generated by pnpm --filter @objectstack/spec check:generated --fix (only check:docs was 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.
  5. Changeset .changeset/27610-adr-0066-d4-api-actions-door.md: @objectstack/spec minor, Clause-②: no, Metadata-protocol: semantic. The card and the claim said patch; ruling A (check-changeset-no-major.mjs, the level axis) grades a declared kind on a moved package minor and refuses patch as enforce, 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 main that 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:

  • Restates it and is in this card's surface → changed: docs/adr/0066 L74 (and the Status line L3), packages/spec/src/ui/action.zod.ts docblock + .describe(), the three generated reference pages.
  • Mentions the gate but not the browser clause → unchanged, still true: action.zod.ts the alias comment near permissions: 'requiredPermissions' and the hidden guidance ("enforced with a 403 on the platform action route").
  • About the api action's response or undo, not the gate → unchanged: action.zod.ts onSuccess/outcomeMessages ("the target call's response body") and undoable ("type: 'api' is fulfilled by the console").
  • Describes today's door truthfully, becomes false when the server half lands → not edited here (it would advertise a capability the runtime does not deliver yet), named on the 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 on target; call that endpoint directly"; the 400 troubleshooting row), content/docs/ai/connect-mcp.mdx ("url, modal, form, and api have no headless …"; list_actions row), content/docs/ai/actions-as-tools.mdx (type table row).
  • Runtime comments and prescriptions, 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 surface → unchanged: packages/runtime/src/action-execution.ts headlessActionTypeError docblock and its api prescription ("Call its target endpoint directly"), packages/runtime/src/domains/actions.ts the type-dispatch comment ("a url/modal/form/api action reaching this endpoint has nowhere to go").
  • UI lane (step 3's surface) → unchanged, listed for the ui card: ui/packages/components/src/renderers/layout/containers.tsx (capability-gate comment: "never for a type: '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)
os-verify-lock: VERDICT command-exit 0 · held the lock 79s (1m19s) · waited 1s
`pnpm --filter @objectstack/spec test` — lock VERDICT command-exit 0 (204s): Test Files 752 passed (752) · Tests 22343 passed | 1 todo (22344)
 Test Files  752 passed (752)
      Tests  22343 passed | 1 todo (22344)
os-verify-lock: VERDICT command-exit 0 · held the lock 204s (3m24s) · waited 0s

Ablation: none — the diff changes no tested behaviour (prose, a docblock, a .describe() and its generated projection).

Acceptance notes

  • Changeset level. minor, not the patch the card named — see "What changes" item 5. If the maintainer wants patch, the kind would have to be none, which the triage and the claim both ruled out (semantic).
  • modal dropped from the D4 list. Factual correction inside the amended paragraph, verified against origin/main 0e33108a: SERVER_DISPATCHED_ACTION_TYPES is script, flow; headlessActionTypeError returns the client-side prescription for modal and the route answers it 400 (domains/actions.ts, the type-dispatch branch). Listed as a changed sentence in the report.
  • Today vs after 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. Every amended text names the landing: the ADR note states the three steps and today's refusal; the spec docblock carries a "Landing" paragraph; the .describe() cites "ADR-0066 D4 as amended 2026-10-29" (no tracker id in a published string). The runtime on main is unchanged by this PR: isHeadlessInvokableAction admits script, flow and the declarative update only; handleActionActivationWrite does not yet refuse an api action; the console still fetches target.
  • Not filed as cards (observations only, class-less): the ui/ docblocks above restate the superseded clause and are the ui card's to rewrite; content/docs/ui/actions.mdx row "api | 400" and connect-mcp.mdx's list_actions row 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.
  • No scripts/adr-anchors change: pnpm check:adr-anchors exit 0; no anchor names docs/adr/0066*, docs/adr/0126* or action.zod.ts.

Verification

Head e44fd034 (branch claude/issue-27610-adr-0066-d4-api-actions-door, base origin/main at merge base 0e33108a). Diff: 7 files, +49/−16.

Build + generated (lock VERDICT 0; `check:generated` 1 of 14 stale → `--fix` → 14/14 up to date)
pnpm --filter @objectstack/spec build        → os-verify-lock: VERDICT command-exit 0 · held the lock 87s
pnpm --filter @objectstack/spec check:generated        → exit 1: ✗ 1 of 14 artifact(s) stale: check:docs (content/docs/references/**)
pnpm --filter @objectstack/spec check:generated --fix  → exit 0: ✓ gen:docs · re-check ✓ check:docs
pnpm --filter @objectstack/spec check:generated        → exit 0: ✓ All 14 generated artifacts are 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
exit 0 (94): check:adr-anchors · check:adr-index · check:adr-symbol-anchors · check:adr-links (+self-test) · check:doc-authoring ·
  check-doc-route-spelling --advisory (+self-test) · check-docs-section-name (+self-test) · check-section-landing-index (+self-test) ·
  spec: check:generated (14/14 up to date) · check:corpus-claim-drift · check:doc-anchors · check:doc-frontmatter · check:docs-audit-scope ·
  check:docs-redirects · check:docs-single-h1 · check:docs-spec-enumerations · check:published-readme-links ·
  check:react-page-adapter-contract · check:role-word · check:vendor-version-stamps ·
  check:nul-bytes · check:issue-citations · check:closed-ceremony-text · check:pm-prior-rulings · check:pm-governed-merges ·
  check-adr-0087-registration --base origin/main (+self-test) · check-empty-changeset --base origin/main (+self-test) ·
  check-changeset-no-major --base origin/main · check:changeset-gate-self-tests · check:pm-changeset-deadline-census ·
  check-closing-keyword-parity (+self-test) · spec: check:api-surface, check:authorable-surface, check:browser-reachable-entries,
  check:docs, check:dual-source-exports, check:duration-unit-keys, check:empty-state, check:entry-nameability, check:export-origins,
  check:exported-any, check:liveness, check:llms-txt, check:skill-refs, check:strictness-ledger, check:variant-docs, check:yaml-examples ·
  check:spec-docblock-symbol-anchors · check:spec-parsed-alias · check:published-files · check:dts-closure ·
  check:sourcemap-no-sources-content · check-dts-emitted --self-test · check-dev-prereqs --self-test · the comment-mask, plugin-teardown
  (run), ci/*, docs-audit/*, release-rehearsal-clone, release-pending-publish self-tests · check:ci-filter-parity ·
  check:cross-package-test-inputs · check:dispatcher-error-vocabulary · check:driver-memory-census · check:gitlink-declared ·
  check:keyed-text-bounds · check:logger-receiver-detach · check:merge-driver · check:org-identifier · check:page-declaration-shape ·
  check:platform-object-tenancy-census · check:refd-timer-probe · check:registry-log-declared · check:rest-log-spy-declared ·
  check:skill-identifier-liveness · check:slot-lookup · check:system-context-census · check:test-source-alias ·
  check:tier-file-adoption · check:undeclared-dep-imports · check:watch-hint-literal
exit 3 (7, PREREQUISITE NOT MET in a fresh worktree where only packages/spec is built — declared narrowing: the
  closure these read is 39 workspace packages, a repo-level build that is CI's run, and none reads the prose this diff moves):
  pnpm --filter @objectstack/lint run check:doc-formula-expressions  → `@objectstack/formula` not built
  pnpm --filter @objectstack/lint run check:doc-security-posture     → `@objectstack/lint` not built
  pnpm --filter @objectstack/spec run check:skill-examples           → packages/client-react/dist holds no declarations
  pnpm check:docs-transcript-drift                                   → packages/lint/dist absent (reads the built registry)
  pnpm check:dual-build-cjs-loads                                    → 90 packages' dist absent; "Run pnpm build first"
  pnpm check:lean-entry-closure                                      → packages/objectql/dist absent
  node scripts/check-plugin-teardown-shape.mjs --self-test           → fixture pinned to a commit outside the shallow clone (the gate's own run exits 0)
node scripts/pm/dispatch-gates.mjs --ran ran.list → exit 0: "101 derived famil(ies) accounted for — 94 run, 7 NOT-MEASURED (7 DERIVED from a recorded exit 3)"
Local-only families (readings, not gates)
pnpm check:skill-frame-freshness → exit 0 (frame current with origin/main)
pnpm check:dev-prereqs           → exit 1 (104 of 105 workspace packages have no dist in this fresh worktree; a machine-state reading, "Fix: pnpm build")

Dogfood battery: n/a — no ! in the title and no (narrowing) arm.


Generated by Claude Code

… 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
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation protocol:ui tooling labels Oct 29, 2026
@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/platform-objects, @objectstack/spec, touching 6 documentable anchor(s).

28 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json bfdaba4322e1258a7657aa622af021e31ab071aa.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 58 of 210 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 167 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json bfdaba4322e1258a7657aa622af021e31ab071aa → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 256d31a3d512d821a80a09e86609b7a53879a1d3 — the merge of head 1d906476b9b45d7d1fddee3eb4137047d03e1501 into base bfdaba4322e1258a7657aa622af021e31ab071aa, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs bfdaba4322e1258a7657aa622af021e31ab071aa → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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 Contract-review-handoff: triage)

Pre-review findings · below tier · ⛔ not a contract review record

These were read at TIER_DEFAULT while the mandated tier has been exhausted (first 429 at 2026-10-29T17:05Z). Per contract-review.md this is not the review of record. The at-tier review is still owed, and this PR does not land before it.

Verified right, at the same tier: the changeset is minor (level axis, Metadata-protocol: semantic); modal is dropped as a factual correction; the 2026-07 clause is kept verbatim (PD #13); the ADR-0126 §8 sentence is consistent with §4; and the three generated reference rows match the .describe().

… 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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

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 platform-objects in scope, because otherwise the one-key-two-meanings defect survives in three locales. The stale PR body is corrected in the seat's ACCEPT.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 7b404e6 · 2026-10-29T18:17Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7b404e6c5d1f10c6a3cfe23214516ff90e80cfd0
Local-runs: none
Scope: packages/spec/src/ui/action-declarative-update.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, docs/adr/0066-unified-authorization-model.md, docs/adr/0126-packaged-metadata-customization-model.md + packages/runtime/src/action-execution.ts, packages/runtime/src/domains/actions.ts, packages/runtime/src/domains/mcp.ts (each on origin/main 85663a5 and in PR #27649's diff at its head a87722d, the first consumer), .changeset/27610-adr-0066-d4-api-actions-door.md, content/docs/references/ui/action.mdx, content/docs/references/data/object.mdx, content/docs/references/kernel/metadata-plugin.mdx, packages/platform-objects/src/apps/translations/en, zh-CN, ja-JP and es-ES .metadata-forms.generated.ts, content/docs/ui/actions.mdx, content/docs/ai/connect-mcp.mdx, content/docs/ai/actions-as-tools.mdx (read for the Skills-docs flag), scripts/adr-anchors/ (read for absence), packages/platform-objects/src (api target census). Inputs: card #27610 body and comments 6500467241, 6501669093, 6502938739, 6504664624; ruling 6499781492 on #27506; PR #27664 body, file list, net diff at this head; PR comments 6502891517, 6503385688, 6504717588, 6504824965; the head's check-runs. The rest of the diff (the changeset, the three generated reference rows, the four translation bundles) is the lane seat's ACCEPT at TIER_DEFAULT; read here, not reviewed.

① Derived judgments

Check-runs on this head at the final read: 50 runs, 41 success, 9 skipped, none failed, none pending (earlier reads had Test Core shards and the Type Check jobs in progress; all concluded success). Lint & Repo Gates, Type Check · workspace (the spec generated-artifact gates), Build Core, Check Changeset, Governed Surface Queue Guard, Type Check UI, Test UI, Build & E2E UI, Live E2E UI all success.

  1. Accept set: nothing moves — right. No key, accepted value, default or export of @objectstack/spec changes; check:authorable-surface and check:api-surface are untouched by the diff and green. Clause-②: no is the correct declaration.
  2. ActionSchema.requiredPermissions .describe() (action.zod.ts L1620) — published JSON-schema description, projected into three reference rows — meaning changed, shape unchanged: Metadata-protocol: semantic, right. The text is phrased "on every call the platform action door dispatches", true on main today (isHeadlessInvokableAction admits script, flow, operation: 'update' only) and after fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649. The same-origin gloss is one word short of the code — see ④ S2.
  3. requiredPermissions TSDoc docblock (ships in the .d.ts) — right in substance, consistent with the describe and the ADR; its "Landing" paragraph is time-bound with no named remover — ④ S1.
  4. actionForm requiredPermissions helpText (action.form.ts L178) — right. One key, one meaning across describe, docblock and helpText; the false script, flow and modal list is gone; the endpoint obligation ("must still check these capabilities itself") and the off-origin exclusion are stated.
  5. updateOperationExecutorRefusal prose (action-declarative-update.ts L124) — right. "(api calls the endpoint it names)" is true before and after steps 2 and 3; no test pins either spelling (git grep on this head: zero hits outside the source and the changeset).
  6. Generated reference rows — right. Three rows regenerated from the describe; check:docs holds them (Type Check · workspace success). Never hand-edited.
  7. Translation bundles (outside hits, the lane's ACCEPT) — read to hold item 4. The en value is the generator's mirror of the source string (check:i18n in Lint & Repo Gates, success); the zh-CN, ja-JP and es-ES values are hand-written by rule and each says what the en text says (door refuses with 403 on every call it dispatches, MCP/AI included; the api endpoint stays directly reachable and must still check; off-origin: nothing checked server-side). No key added or dropped.
  8. Governed text: ADR-0066 Status entry, D4 paragraph and Amended note; ADR-0126 §8 item 2 sentence — judged in ④. The 2026-07 clause is quoted verbatim and marked superseded in its browser-only half only; the endpoint obligation stands (Prime Directive [WIP] Add Chinese version of the documentation #13 provenance intact).
  9. Runtime unchanged by this PR — right, as the card orders. No file under packages/runtime moves; isHeadlessInvokableAction, SERVER_DISPATCHED_ACTION_TYPES, headlessActionTypeError, handleActionActivationWrite are as on main.
  10. No scripts/adr-anchors change — right. No anchor file names docs/adr/0066, docs/adr/0126 or action.zod.ts (grep over scripts/adr-anchors on this head: zero).
  11. The ADR's path#symbol citations resolve on main 85663a5 — actionPermissionError (L832), disabledActionRefusal (L912), isHeadlessInvokableAction (L1008), SERVER_DISPATCHED_ACTION_TYPES (L1028), headlessActionTypeError (L1044), invokeBusinessAction (L2637), isSystemObjectName (L97), domains/actions.ts#handleActionActivationWrite (L173), domains/mcp.ts#buildMcpBridge (L551); check:adr-symbol-anchors is in Lint & Repo Gates, success.
  12. modal dropped from the dispatched types — right. On main, SERVER_DISPATCHED_ACTION_TYPES is script, flow; headlessActionTypeError answers modal with the client-side prescription and the route returns 400.

② Semver level

  • Changeset .changeset/27610-adr-0066-d4-api-actions-door.md: @objectstack/spec: minor, @objectstack/platform-objects: patch; body carries Clause-②: no and Metadata-protocol: semantic as line-initial declarations; the PR body carries the same two lines plus Skills-docs: follow-up #27506 — …. Check Changeset and Lint & Repo Gates (check-changeset-no-major level axis, check-empty-changeset, check-adr-0087-registration) are success.
  • Level — right. AGENTS.md Post-Task Checklist step 3: a declared kind takes at least minor on a package the diff moves, so semantic on @objectstack/spec is minor; the card's and the claim's "patch" was the dispatch word against the standard clause, and the dev resolved it the right way and flagged it. check-changeset-no-major.mjs grades a declared kind on at least one moved package minor, which spec satisfies, so platform-objects: patch for value-only translation edits is admitted.
  • Kind — right. The documented meaning of requiredPermissions on a type: 'api' action changes; nothing is added, refused or tightened: semantic, not added or narrowed.
  • Clause-②: no — right (item ① 1). No (narrowing) arm: nothing a consumer declared is taken away by this diff; the 409 on the activation write door and the header-type widening are fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649's narrowing, declared there.
  • Skills-docs: line — present and well-formed (follow-up #N with the pages named). Its content is only partly covered by the named follow-up — escalated in ③.

③ Boundary flags

PR author is objectstack-fleet[bot]; no author note owed.

Dev flags, round 1 (os-dev-report 6502938739):

Dev flags, round 2 (os-dev-report 6504664624):

  • One commit, no WIP push before each minutes-long step — a process deviation with no effect on the tree; noted, nothing to change.
  • Surface reach: the four platform-objects translation bundles and platform-objects: patch — answered: right. The en bundle is generator-owned and rewritten from the source string (the hand edit lives in action.form.ts; check:i18n green), the three translated values are hand-written by rule (AGENTS.md Documentation Guardrails), and without them the one-key-two-meanings defect (M1 of 6503385688) survives in three locales. In the lane's ACCEPT; read here only to hold ① 4.
  • git fetch --depth=1 of one fixture commit into the shared object store — answered: no ref moved, no FETCH_HEAD written; within the letter of the multi-agent discipline (fetch into nothing you do not own). Noted.
  • The .describe() drops the type list — answered: acceptable; the docblock keeps the list, and the describe stays true across the landing (S1 of 6503385688).
  • Open question 2 (keep the translation edits in this Tier H PR) — answered: A, keep. The landing is whole-PR under Tier H whatever the split, and the edits are value-only.

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 platform-objects: patch, the changed-sentence list omits action.form.ts and action-declarative-update.ts) — the body is not judged here; the seat rewrites it at ACCEPT, and ④ M1's clause also appears in body item 1, so it is rewritten together.

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: ui/packages/plugin-detail/src/RelatedList.tsx L810 and ui/packages/plugin-detail/src/__tests__/RelatedList.toolbarCapabilityGate-9782.test.tsx L14 quote the old .describe() verbatim ("action route (script/flow/modal + MCP) and mirrored as a UI hide"); add them to #27611's note. The runtime prescriptions (headlessActionTypeError's api arm, the type-dispatch comment) are rewritten in #27649's diff — verified.

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 ai.exposed and fail-closed sys_* guards named with their symbols; verified: invokeBusinessAction throws on isSystemObjectName before the headless gate, buildMcpBridge L830 skips sys_*, the REST door in domains/actions.ts has no sys_* guard, so sys_user.ban_user is REST-reachable and MCP-refused as recorded); S3 applied (ADR-0126 §8: the switch stops the dispatch, not a direct call; step 3 is #27611); S4 applied (definition matches isSameOriginApiTarget — see ④).

④ Design judgment

docs/adr/0066-unified-authorization-model.md — against the ruling 6499781492, the first consumer #27649 (head a87722d) and the three-step plan.

  • Holds: the same-origin definition ("exactly one / and goes on past it; / alone, //host and /\host are not") is exactly isSameOriginApiTarget (a string of two or more characters, first /, second neither / nor backslash), applied to the declared target in isHeadlessInvokableAction, headlessActionTypeError and offOriginApiActivationRefusal, and again to the interpolated path in buildApiActionRequest (400 when it fails) — the ADR's "the door holds the interpolated path to the same test" is right. The gate order is right on both doors: REST handleActionsRequest runs the anonymous baseline as its first statement, then actionPermissionError (403), then disabledActionRefusal (409), then the type branch, the param contract and the subject load, then the forward; invokeBusinessAction runs sys_*, headless, ai.exposed, D4, the consult, then the forward. The request is assembled from method, recordIdParam/recordIdField, bodyShape.wrap, bodyExtra, the ${param.*}/${ctx.*}/{field} placeholders (plus the outputs.* fold and organizationId for an /api/v1/auth/ target — a superset, not a contradiction). Caller credentials cross on an allowlist (authorization, cookie, x-admin-session-token, tenant and proxy headers). Off-origin: headlessActionTypeError refuses (400) on both doors, buildMcpBridge skips it (isHeadlessInvokableAction false), handleActionActivationWrite step 3b refuses the flip 409 with the reason — all three clauses right. The endpoint obligation ("MUST still enforce the declared capability itself") matches fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649's "the target still authorizes the caller itself". The modal correction and the "until step 2 lands" statements are right on main (① 12, ① 9). The list_actions widening is in fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649's mcp.ts diff as the note says. All 59 type: 'api' declarations under packages/platform-objects/src have a bare-/ target, so the amended clause reaches every platform api action — the ruling's "70-odd" become same-origin, none falls into the off-origin arm.
  • M1 — must-fix. D4 L74 says of "the route and the bridge" that they forward "… the caller's cookies and auth headers carried, the target's Set-Cookie returned." The first consumer relays Set-Cookie on the REST response only (forwardApiAction collects getSetCookie() into relayed['set-cookie']; domains/actions.ts emits it as header lines, one per cookie) and the MCP bridge relays only the bearer-lane pair set-auth-token / set-admin-session-token (mcpRelayableForwardHeaders: "⛔ Never Set-Cookie: the MCP door admits cookie sessions too, and a tool result is JSON text … an HttpOnly session cookie's value must not travel in it"; invokeBusinessAction's api arm returns only that subset). As written the governing paragraph records, for the bridge, a credential relay its first consumer refuses on a security ground; under Prime Directive [WIP] Add Chinese version of the documentation #13 the ADR binds and a later MCP change would be entitled to implement the leak. Fix, one clause at L74 (and the stale PR body item 1 beside it): the target's Set-Cookie is returned on the REST route's response; 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.
  • S3 — should-fix. L74 anchors the definition on a bare symbol in an unmerged PR ("isSameOriginApiTarget … PR fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649"). Right today (the symbol is not on main, so a path#symbol spelling would red check:adr-symbol-anchors), but nothing holds the two together once fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 lands: name in fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649's acceptance that it rewrites this citation to packages/runtime/src/action-execution.ts#isSameOriginApiTarget. Also the Amended note records the MCP sys_* gap "and does not close" it without naming its carrier; card mcp: run_action refuses every action on a sys_* object, so an agent cannot reach the identity actions (ban_user, impersonate_user, set_user_password) that REST POST /actions serves #27681 (open, needs-user-decision) is that carrier — cite it so the recorded gap has a card (a decision nobody can find is a decision that will be reversed).

docs/adr/0126-packaged-metadata-customization-model.md §8 item 2 — against §4 and the first consumer. Holds: the consult point is disabledActionRefusal, called by the REST door (domains/actions.ts L655) and by the MCP bridge (invokeBusinessAction, and resumeAdmissionRefusal on the resume path); §4 "each consumer documents its consult point" is satisfied by this sentence; "the switch stops the action's dispatch, not a direct call to its target endpoint" is exactly what #27649 delivers (409 ACTION_DISABLED at the door; the endpoint untouched); the step-3 pointer #27611 is open and is that card. No finding.

packages/spec/src/ui/action.zod.ts — against the ruling, ADR-0066 D4 as amended, and #27649.

  • Holds: the describe and docblock state the door, the gate, the same-origin arm, the endpoint obligation and the off-origin exclusion consistently with D4; the describe makes no present-tense claim that api is dispatched today; the describe carries no tracker id (check:doc-authoring green); the generated rows match it byte-exact (check:docs green). The neighbouring alias comment (L980) and hidden guidance (L995) still say "platform action route" — terminology, still true; the undoable describe ("type: 'api' is fulfilled by the console") is about the undo envelope, not the gate — both rightly left alone.
  • S1 — should-fix. The docblock's "Landing: … Until it lands the door still refuses type: 'api' … and the console fetches target directly" ships in the published .d.ts and is time-bound; fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649's file list touches no packages/spec file, so nothing is owed to remove it when it becomes stale. Either date it ("as of 2026-10-29") or name its removal as owed by the server PR (its Skills-docs: or acceptance notes).
  • S2 — should-fix. The describe's gloss "(exactly one leading /, followed by neither / nor a backslash)" does not by itself exclude / alone, which the docblock ("goes on past it"), the ADR and isSameOriginApiTarget (false for a target shorter than two characters) all exclude. Add the length clause ("with a path after it"); regenerate the three rows.

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 en bundle mirrors it. No finding.

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: claude/issue-27610-adr-0066-d4-api-actions-door
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head a594447 · 2026-10-29T18:56Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a594447013e3bd2dab35e8e4ddd9d7421b3cf46a
Local-runs: none
Scope: packages/spec/src/ui/action-declarative-update.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, docs/adr/0066-unified-authorization-model.md, docs/adr/0126-packaged-metadata-customization-model.md + packages/runtime/src/action-execution.ts, packages/runtime/src/domains/actions.ts, packages/runtime/src/domains/mcp.ts (each on origin/main 85663a5 and at PR #27649's current head 533f8da, the first consumer; git diff --name-only a87722dd..533f8da1 touches only content/docs/, so the runtime read in record 6505129020 is the runtime read here), content/docs/references/ui/action.mdx, content/docs/references/data/object.mdx, content/docs/references/kernel/metadata-plugin.mdx (the regenerated rows), .changeset/27610-adr-0066-d4-api-actions-door.md (unchanged by the move, read for the definition wording), scripts/symbol-anchors.mjs and scripts/check-adr-symbol-anchors.mjs (the anchor grammar, read for the new file-level citation), scripts/check-issue-citations.mjs (read for the new tracker citations), PR #27649's file list and comments 6499889795, 6505278007, 6505698132 (the consumer's claim surface and its refreshed ACCEPT), cards #27681 and #27611 (state and threads). Inputs: card #27610 body and comments 6500467241, 6501669093, 6502938739, 6504664624; ruling 6499781492 on #27506; PR #27664 body, file list, net diff at this head (13 files, the same list as at 7b404e6); PR comments 6502891517, 6503385688, 6504717588, 6504824965, 6505129020, 6505671967; the head's check-runs. This is a DELTA record over 6505129020 (FAIL on 7b404e6): the move is one commit, a594447 on parent 7b404e6, no merge from main and no carry-over to separate; git diff 7b404e6c a594447013 is 5 files, +12/−10: docs/adr/0066 L74 and L76, action.zod.ts L1614–1619 (docblock) and L1621 (describe), and the three reference rows. Every section below says what the move touches and judges it afresh; what it does not touch stands as judged in 6505129020, named. The rest of the diff (the changeset, the three generated rows, the four translation bundles) is the lane seat's ACCEPT at TIER_DEFAULT; read here, not reviewed.

① Derived judgments

Check-runs on this head at the final read: 50 runs, 41 success, 9 skipped, 0 failed, 0 pending (the first read at 18:59Z had Type Check UI, Type Check · workspace and Test Core shards 2, 4, 6, 7 in_progress; each concluded success). The gate carriers for this diff: Lint & Repo Gates, Lint · docs & code guards (check:adr-symbol-anchors, check:i18n), Lint · repo & tooling gates (check:doc-authoring, check:adr-anchors), Lint · CI, release & engine gates (check:issue-citations, diff-scoped), Type Check · source gates (pnpm --filter @objectstack/spec check:docs), Type Check · workspace, Build Core, Build Docs, Check Changeset, Governed Surface Queue Guard, Check PR Size — all success. The 9 skips are opt-in or matrix placeholders (Lint UI, Test UI (shard), Dogfood Regression Gate (shard), Dogfood Verify CLI, UI Published Artifacts, Temporal Conformance, Packed-tarball smoke, 18.x upgrade harness, Schedule gate), the same roster as on 7b404e6.

  1. The move's reach — right. Five files, all inside the four findings of 6505129020 (M1, S1, S2, S3): no runtime path, no schema shape, no changeset, no translation bundle, no scripts/adr-anchors file moves. The commit message names exactly those four findings; the tree agrees with it line for line. Nothing outside the review's findings is added.
  2. Accept set: nothing moves — stands (6505129020 ① 1). Clause-②: no remains the right declaration; the move edits prose, a TSDoc docblock and a .describe() only.
  3. ActionSchema.requiredPermissions .describe() (action.zod.ts L1621) — re-judged, right. One clause added to the same-origin gloss: "(exactly one leading /, followed by neither / nor a backslash, with a path after it)". It now excludes / alone, which the docblock ("goes on past it"), ADR-0066 L74 ("/ alone … [is] not") and the consumer's isSameOriginApiTarget (a target shorter than two characters is false; action-execution.ts L1065–1072 at 533f8da) all exclude. Still phrased "on every call the platform action door … dispatches" — true on main today and after fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649. Still no tracker id in the published string (check:doc-authoring, success). Metadata-protocol: semantic, right (6505129020 ① 2).
  4. Generated reference rows — re-judged, right. The three requiredPermissions action rows (ui/action.mdx L60; data/object.mdx the fourth of five requiredPermissions rows, the action one; kernel/metadata-plugin.mdx L350) are byte-equal to the describe after MDX pipe-escaping is undone (compared on this head from git show, not regenerated locally); check:docs holds them (Type Check · source gates, success). Never hand-edited.
  5. requiredPermissions TSDoc docblock (ships in the .d.ts) — re-judged. The Landing paragraph is dated ("Landing, as of 2026-10-29") and otherwise unchanged in substance; it gains one sentence naming who removes each clause ("The server PR (fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649) removes this paragraph's door clause when it lands; its console clause goes with step 3 (Console: send a same-origin type: 'api' action click through POST /actions/:object/:action instead of fetching its target directly (ruling on #27506, step 3 of 3) #27611)"). The date resolves 6505129020 ④ S1. The new sentence is judged in ④ S1 below.
  6. ADR-0066 L74 — re-judged in ④. The Set-Cookie clause is split by door (the M1 fix) and the isSameOriginApiTarget citation gains a landing clause. L76 (the Amended note) gains "card mcp: run_action refuses every action on a sys_* object, so an agent cannot reach the identity actions (ban_user, impersonate_user, set_user_password) that REST POST /actions serves #27681 carries it". L3 (Status) is untouched.
  7. ADR citations on this head — right. The new L74 clause names packages/runtime/src/action-execution.ts in a bare code span: under the scripts/symbol-anchors.mjs grammar that is a FILE-LEVEL anchor, and the file exists on main; no path#isSameOriginApiTarget anchor is written (the symbol is not on main, so one would red). check:adr-symbol-anchors in Lint · docs & code guards: success. The new tracker citations (fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 — resolves as a PR; mcp: run_action refuses every action on a sys_* object, so an agent cannot reach the identity actions (ban_user, impersonate_user, set_user_password) that REST POST /actions serves #27681; Console: send a same-origin type: 'api' action click through POST /actions/:object/:action instead of fetching its target directly (ruling on #27506, step 3 of 3) #27611) resolve; check:issue-citations diff-scoped in Lint · CI, release & engine gates: success. The eight path#symbol anchors judged in 6505129020 ① 11 are unchanged and still resolve on 85663a5.
  8. Items 4 (action.form.ts helpText), 5 (action-declarative-update.ts refusal), 7 (translation bundles), 8 (ADR-0126 §8), 9 (runtime unchanged), 10 (no scripts/adr-anchors change), 12 (modal dropped) — stand as judged in 6505129020. None of those files or lines moves between 7b404e6 and this head.

② Semver level

Stands as judged in 6505129020 ②: .changeset/27610-adr-0066-d4-api-actions-door.md (@objectstack/spec: minor, @objectstack/platform-objects: patch, body lines Clause-②: no and Metadata-protocol: semantic) is not in the move's diff. Read for one thing the move could have desynchronised: the changeset's own same-origin definition ("starts with exactly one / and goes on past it — not //, nor / followed by a backslash") already excludes / alone, so it agrees with the describe's new gloss; no changeset edit was owed. Check Changeset, Lint & Repo Gates (check-changeset-no-major level axis, check-empty-changeset, check-adr-0087-registration): success. Level minor on spec for a declared semantic kind — right; patch on platform-objects for value-only translation edits — right. The PR body carries Clause-②: no, Metadata-protocol: semantic and the Skills-docs: follow-up #27506 — … line unchanged.

③ Boundary flags

PR author is objectstack-fleet[bot]; no author note owed.

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 hits.

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 isSameOriginApiTarget citation into a symbol anchor; removes the docblock's door clause). #27649's tree at 533f8da touches no docs/adr/** or packages/spec/** path; its re-claim 6499889795 says "ADR-0066 / ADR-0126 / packages/spec are NOT in this surface; they are #27610"; its ACCEPT 6505698132 records no such obligation; and docs/adr/** is a Tier H governed surface (AGENTS.md Prime Directive #14), so the sentence, read literally, orders a Tier S PR onto a Tier H surface. Whoever lands step 2 must be told, or the sentence must stop naming a PR — the seat chooses at ACCEPT.

④ Design judgment

docs/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 disabledActionRefusal on both doors; the switch stops the dispatch, not a direct call; step 3 is #27611, open). No finding.

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: claude/issue-27610-adr-0066-d4-api-actions-door
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

On should-fix S1 of record 6505934898 (the two plan sentences that hand #27649 an edit to docs/adr/0066 and packages/spec/src/ui/action.zod.ts): the first consumer's lane (domain:server#1, #27649) answers that #27649 will not take those two edits. Taking the ADR would make #27649 Tier H, and touching packages/spec would void its PASS. Ruling C's order also lands the ADR before the server half, so the ADR should not assign work to a later PR.

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 (domain:skills or documentation, body naming Blocked-by #27649). The domain:server#1 seat will name the card in #27649's landing comment. The reword is a should-fix and does not block this PR; a head that carries it gets a delta review from this seat.


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
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract-review-claim: session_01L6bhgV4fMGJHSdSdx3u9mK · head 1d90647 · 2026-10-29T19:31Z


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

triage#1 · os-sales · session_01L6bhgV4fMGJHSdSdx3u9mK

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1d906476b9b45d7d1fddee3eb4137047d03e1501
Local-runs: none
Scope: packages/spec/src/ui/action-declarative-update.ts, packages/spec/src/ui/action.form.ts, packages/spec/src/ui/action.zod.ts, docs/adr/0066-unified-authorization-model.md, docs/adr/0126-packaged-metadata-customization-model.md + scripts/pm/check-half-states.mjs (DIRECTIVE_PREFIX, directiveKey, BLOCKED_BY_PRESENT, hasBlockedByLine, PROSE_BLOCKER_ANCHORS, proseBlockerRefs, the H4 and H19 definitions — the machine half of the pm:blocked card the move names), .claude/skills/pm-dispatch/references/state-machine.md L23 and SKILL.md L110 (the canonical Blocked-by: #N line), .github/workflows/half-state-patrol.yml and package.json check:pm-half-states (whether that sweep is a gate on this head), scripts/check-issue-citations.mjs (read for the one new tracker citation), packages/runtime/src/action-execution.ts on origin/main 85663a5 (read for the absence of isSameOriginApiTarget; the consumer-side read of record 6505934898 at PR #27649's head 533f8da stands — 533f8da is still #27649's head at this read and the move touches nothing it bears on), .changeset/27610-adr-0066-d4-api-actions-door.md (unchanged by the move, read for the declarations), card #27694 (body, labels, zero comments), PR #27649 (state, head, file list, body closing keyword, comments 6505278007 and 6505698132). Inputs: card #27610 body and comments 6500467241, 6501669093, 6502938739, 6504664624 (no new comment since record 6505934898); ruling 6499781492 on #27506; PR #27664 body, file list at this head (13 files, the same list as at a594447 and 7b404e6), the move's diff; PR comments 6502891517, 6503385688, 6504717588, 6504824965, 6505129020, 6505671967, 6505934898, 6505960146, 6506412093; the head's check-runs. This is a DELTA record over 6505934898 (PASS on a594447, should-fix S1): the move is ONE commit, 1d90647 on parent a594447 (git log a594447013..1d906476 is that commit alone; no merge from main, so no carry-over to separate — the PR's GitHub base moved with main to 4278c3b and CI merged the head onto bfdaba4, but the PR's own commits carry none of that); git diff a594447013 1d906476 is 2 files, +4/−4: docs/adr/0066 L74 (one parenthetical) and action.zod.ts L1617–1619 (three TSDoc lines). Every section below says what the move touches and judges it afresh; what it does not touch stands as judged in 6505934898, named (and through it in 6505129020). The rest of the diff (the changeset, the three generated reference rows, the four translation bundles) is the lane seat's ACCEPT at TIER_DEFAULT; read here, not reviewed.

① Derived judgments

Check-runs on this head at the final read (2026-10-29T19:45:12Z): 50 runs, 41 success, 9 skipped, 0 failed, 0 pending — all eight Test Core shards and the Test Core aggregate, TypeScript Type Check, Type Check · workspace, Type Check UI, Test UI, Build & E2E UI, Live E2E UI, Dogfood Regression Gate included (the first read at 19:33Z had Type Check UI, Type Check · workspace and all eight Test Core shards in_progress; each concluded success, the last at 19:44Z). The gate carriers for this diff: Lint & Repo Gates, Lint · docs & code guards (check:adr-symbol-anchors, check:i18n), Lint · repo & tooling gates (check:doc-authoring, check:adr-anchors), Lint · CI, release & engine gates (check:issue-citations, diff-scoped), Type Check · source gates (pnpm --filter @objectstack/spec check:docs), Build Core, Build Docs, Check Changeset, Governed Surface Queue Guard, Check PR Size, PR-body guards — all success at the final read. The 9 skips are the same opt-in or matrix placeholders as on a594447 (Lint UI, Test UI (shard), Dogfood Regression Gate (shard), Dogfood Verify CLI, UI Published Artifacts, Temporal Conformance, Packed-tarball smoke, 18.x upgrade harness, Schedule gate). CI computed on the merge of this head into main bfdaba4 (the docs-drift comment names the tree), so the anchor and citation gates judged the ADR against today's main, not the branch's merge base.

  1. The move's reach — right. Two files, four lines, both exactly the two sites of 6505934898 ④ S1: the L74 parenthetical that named fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 as the rewriter, and the docblock sentence that named fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 as the remover. No runtime path, no schema shape, no .describe(), no generated row, no changeset, no translation bundle, no scripts/adr-anchors file moves (git diff --stat over every other path of the PR between the two heads is empty). The commit message names that one finding and nothing else; the tree agrees with it line for line. Nothing outside the review's findings is added.
  2. Accept set: nothing moves — stands (6505934898 ① 2, 6505129020 ① 1). Clause-②: no remains the right declaration: the move edits one ADR parenthetical and three TSDoc lines.
  3. ActionSchema.requiredPermissions .describe() (action.zod.ts L1621) — unchanged by the move, byte-identical to a594447 (compared from git show). Stands as judged in 6505934898 ① 3: phrased "on every call the platform action door … dispatches", same-origin gloss excludes / alone, no tracker id in the published string. Metadata-protocol: semantic, right.
  4. Generated reference rows — unchanged by the move, so 6505934898 ① 4 stands (byte-equal to the describe after MDX pipe-escaping is undone; check:docs in Type Check · source gates, success).
  5. requiredPermissions TSDoc docblock (ships in the .d.ts) — re-judged in ④. The Landing paragraph keeps its date and its today-statement; its last sentence now names card docs(adr): after the api-action door lands, rewrite ADR-0066 D4's isSameOriginApiTarget citation into a symbol anchor and drop the requiredPermissions docblock's "Landing" door clause #27694 as the remover of the door clause and assigns no work to fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 or Console: send a same-origin type: 'api' action click through POST /actions/:object/:action instead of fetching its target directly (ruling on #27506, step 3 of 3) #27611.
  6. ADR-0066 L74 — re-judged in ④. The isSameOriginApiTarget parenthetical now reads "PR fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649; once the symbol is on main, card docs(adr): after the api-action door lands, rewrite ADR-0066 D4's isSameOriginApiTarget citation into a symbol anchor and drop the requiredPermissions docblock's "Landing" door clause #27694 rewrites this citation into a symbol anchor …". L76 (the Amended note) and L3 (Status) are untouched by the move.
  7. Tracker citations on this head — right. The move adds ONE new citation, #27694, at two sites (L74, L1618). It resolves as an ISSUE, not a PR (GET docs(adr): after the api-action door lands, rewrite ADR-0066 D4's isSameOriginApiTarget citation into a symbol anchor and drop the requiredPermissions docblock's "Landing" door clause #27694: pull_request absent), open, filed 2026-10-29T19:11:19Z by the dev's session — ten minutes before the commit (19:21:51Z), so the carrier existed before the text named it. check:issue-citations, diff-scoped, in Lint · CI, release & engine gates: success. The citations already present at a594447 (fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 resolving as a PR, 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, Console: send a same-origin type: 'api' action click through POST /actions/:object/:action instead of fetching its target directly (ruling on #27506, step 3 of 3) #27611, mcp: run_action refuses every action on a sys_* object, so an agent cannot reach the identity actions (ban_user, impersonate_user, set_user_password) that REST POST /actions serves #27681, framework#3923 cross-repo) are unchanged. The bare packages/runtime/src/action-execution.ts span at L74 is still a FILE-LEVEL anchor under the scripts/symbol-anchors.mjs grammar (read in 6505934898 ① 7), the file exists on main, and no path#isSameOriginApiTarget anchor is written (the symbol is not on main: grep over packages/runtime/src/action-execution.ts at 85663a5, zero hits — so one would red); check:adr-symbol-anchors in Lint · docs & code guards: success. No tracker id enters the describe or any customer-facing string (check:doc-authoring in Lint · repo & tooling gates: success).
  8. Items 8 of 6505934898 ① (action.form.ts helpText, action-declarative-update.ts refusal, translation bundles, ADR-0126 §8, runtime unchanged, no scripts/adr-anchors change, modal dropped) — stand. None of those files or lines moves between a594447 and this head.

② Semver level

Stands as judged in 6505934898 ② and 6505129020 ②: .changeset/27610-adr-0066-d4-api-actions-door.md is not in the move's diff; read on this head it carries @objectstack/spec: minor, @objectstack/platform-objects: patch, and the line-initial Clause-②: no and Metadata-protocol: semantic. Check Changeset and Lint & Repo Gates (check-changeset-no-major level axis, check-empty-changeset, check-adr-0087-registration): success. The move changes no published meaning (the describe is byte-identical; the docblock's rule text is untouched), so no changeset edit was owed. The PR body carries Clause-②: no, Metadata-protocol: semantic and the Skills-docs: follow-up #27506 — … line unchanged.

③ Boundary flags

PR author is objectstack-fleet[bot]; no author note owed.

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 hits.

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 pm:blocked but leaves the machine nothing to unlock it on, and its "Wanted" leaves the docblock's console clause without a carrier in the likely landing order. One write on the card, not a tree change; the seat or the dev makes it.

④ Design judgment

docs/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 Blocked-by: #27649 so H4 clears and H19 can unlock it on #27649's merge, and name who drops the docblock's console clause when #27694 lands before #27611). The ③ item (stale PR body) is the seat's at ACCEPT, not a tree change.

Implemented-by: claude/issue-27610-adr-0066-d4-api-actions-door
Reviewed-by: session_01L6bhgV4fMGJHSdSdx3u9mK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU

席位核对 — ACCEPT · #27610 · head 1d906476b9b45d7d1fddee3eb4137047d03e1501 · 2026-10-29T19:51Z

维护者速读

席位意见:最后一轮复核没有 must-fix。它唯一的 should-fix 在卡 #27694 上,不在本 PR 的文件里,已由本席在 #27694 补上首行 Blocked-by: #27649,并写明控制台条款的去向(6506768233)。

席位核对

  1. Shape: 13 files, +64/−22, the same file list since 7b404e6c. Every file is inside the surface claimed in 6501669093. Tier H (docs/adr/**); check-governed-merges.mjs --pr 27664: H(人合), 86 lines.
  2. Premise check:
  3. Gate check-runs on 1d906476: 41 success, 9 skipped, none failed. check-expected-skips.mjs --pr 27664: OK, all 9 skips in main's roster. Mergeable clean. The dev's local battery: 103 gate families exit 0 on this head, --ran reconciliation 0 NOT-MEASURED (os-dev-report 6506756222 on docs(adr): ADR-0066 D4 and ADR-0126 §8 — same-origin type: 'api' actions are dispatched by the platform action door (ruling on #27506, step 1 of 3) #27610).
  4. Accepted deviations:
    • The PR body is stale after two patch rounds; this comment carries the correct reading. The relay refuses a body issue_patch whose first line is the attribution marker.
    • Round 2 took two commits (a5944470, 1d906476), not one: the second carries the seat's mid-round order on S1 of 6505934898. No amend, no force push.
    • The Opus pre-review (6503385688) was below the contract-review tier and was posted as findings, not as a record. The three records above are at tier.
  5. Out-of-scope dispositions:
  6. Serial constraints: lands before fix(runtime)!: a same-origin type: 'api' action is dispatched through the action doors, so its packaged-action switch holds (#27506) #27649 (step 2), which is draft and touches no docs/adr/** or packages/spec/** path. No other open PR touches the 13 files.
  7. Landing clause: Tier H. This hands off to GOVERNED_APPROVERS (os-zhuang, hotlong) via pr_request_reviewers, with needs-user-decision on the PR while it waits. After an APPROVED review with every check green, the seat will:

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU

更正 ACCEPT 6506803986 的「两分钟验证」· 2026-10-29T19:52Z:check:generated 是 spec 包的脚本,根目录没有。请跑 pnpm check:adr-symbol-anchors 和 pnpm --filter @objectstack/spec check:generated,两条都应 exit 0。其余内容不变。

@os-zhuang
os-zhuang marked this pull request as ready for review October 29, 2026 20:12
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 29, 2026
Merged via the queue into main with commit 86c9d53 Oct 29, 2026
59 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-27610-adr-0066-d4-api-actions-door branch October 29, 2026 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(adr): ADR-0066 D4 and ADR-0126 §8 — same-origin type: 'api' actions are dispatched by the platform action door (ruling on #27506, step 1 of 3)

2 participants