Skip to content

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

Closed

Description

@objectstack-fleet

domain:server#1 · os-sales · session_01L6bhgXcN9HaZYSEUdJWEjw

立卡门:③ 维护者直派(ruling on #27506, comment 6499781492)。

What this card is

Step 1 of the three-step ruling on #27506 (comment 6499781492): the ADR and spec-text amendment that the server step (#27506 itself) and the UI step depend on. Governed surface, Tier H: the maintainer merges.

The maintainer's words, verbatim and untranslated (card thread of #27506, 2026-10-29):

「我们的平台设计目标是 agent 和人类使用同一套系统」
「同意」

Business reason

A type: 'api' action (Ban User, Impersonate, Set Password, 70-odd declarations on the platform's own objects) is today reachable only by a human click: the console fetches its target directly, and the platform action door and MCP run_action refuse the type (isHeadlessInvokableAction admits script, flow and operation: 'update' only). So an agent cannot do what a human can, and platform governance (the ADR-0126 switch, ADR-0066 D4, the anonymous baseline, audit) never sees the click. ADR-0066 D4's 2026-07 scope note records that gap as the rule; under the design goal above it is a gap, and Prime Directive #13 says a recorded decision is reversed by an ADR edit, never by a changeset.

Scope (exactly this, no code)

  • docs/adr/0066-unified-authorization-model.md, D4 "Scope of the server half": amend the api clause. New meaning: a type: 'api' action whose target is a same-origin platform path is dispatched by the platform action route and the MCP run_action bridge, which run the D4 gate before forwarding; the endpoint keeps re-checking its own authorization (defence in depth, not a replacement); an api action whose target is not same-origin stays browser-only and ungoverned, and the activation write door refuses to flip it. Keep the provenance of the 2026-07 note; add the amendment date and this card's ruling reference.
  • docs/adr/0126-packaged-metadata-customization-model.md §8 item 2: one sentence recording that the action consult point covers type: 'api' through the same door (per §4 "each consumer documents its consult point").
  • packages/spec/src/ui/action.zod.ts, requiredPermissions docblock and .describe() (around lines 1594-1606): reword to match; regenerate what check:docs reads (pnpm --filter @objectstack/spec build && check:generated --fix). No schema shape change. Metadata-protocol: semantic (the documented meaning of type: 'api' changes); Clause-②: no.
  • No scripts/adr-anchors change unless the amended paragraph is anchored (check pnpm check:adr-anchors).

Who acts

Authoring is open to any seat; landing is Tier H (maintainer merge or authorized approval). The card thread of #27506 can author the PR on request; otherwise the spec lane. Blocks: the landing of #27506's server PR (step 2). Does not block its development.

Dedupe

Searched issues for "ADR-0066 D4 amend api action clause requiredPermissions endpoint re-check" (35 hits, none on this clause) and "api type action dispatched through platform action route run_action agent cannot invoke" (11 hits: #27506 itself, #25297/#25308/#25296 on object-less and disabled-package actions, none on api-type dispatch). Sibling cards: #27506 (server step), #27504 (button stays live, ui).

Dedupe words: ADR-0066 D4, type api, platform action route, run_action, requiredPermissions


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

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

    Triage: p1 · domain:skills (relabelled from domain:spec) · area:automation · cross-domain exception, one PR · 2026-10-29T15:09Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU

    Claim: PM loop round 4 · 2026-10-29T15:57Z
    Session: session_017ihL2dPRZXfgE1A2Vct3FU
    Account: marchtian
    Branch: claude/issue-27610-adr-0066-d4-api-actions-door
    Worktree: objectstack-issue-27610
    Domain: domain:skills
    Seat: domain:skills#1
    File surface:

    • docs/adr/0066-unified-authorization-model.md: the D4 "Scope of the server half" api clause. Amend it, keep the 2026-07 note's provenance, and add the amendment date and Ruling-ref: 6499781492.
    • docs/adr/0126-packaged-metadata-customization-model.md: one sentence in §8 item 2.
    • packages/spec/src/ui/action.zod.ts: the requiredPermissions docblock and .describe() only (~L1594–L1606), plus the generated files check:generated / check:docs regenerate from it. Cross-domain exception granted by triage 6500467241.
    • Any other sentence on main that restates the old api clause (the docblock at ~L995 and its siblings): the dev lists each one found, and edits it only when it restates the clause.
    • scripts/adr-anchors only if the amended paragraph is anchored (pnpm check:adr-anchors).
    • .changeset/*.md: one patch changeset for @objectstack/spec.
    • ⛔ No schema shape change and no runtime code; the door itself is 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. Stop on a breach and explain it in the report.
      Container & model: S. mode:subagent, model: fable, effort: xhigh. dispatch-gates.mjs --tier returns MANDATORY claude-fable-5-1: docs/adr/** is under the 2026-10-25 order 「adr 这种任务都需要 fable extra」, which also sets the xhigh floor, and packages/spec/src/** is the contract surface. No downgrade.
      Clause-②: no
      Metadata-protocol: semantic (the documented meaning of type: 'api' changes: a same-origin api action is dispatched by the platform action door and MCP run_action; the shape is unchanged)
      Responsibility: the governing texts ADR-0066 D4 / ADR-0126 §8 and the published action.zod.ts docblock + .describe() (this lane, cross-domain grant) | check:adr-anchors, check:generated / check:docs hold the text to its generated mirrors; the behaviour is #27506's server PR, which lands after this | every reader of ADR-0066 D4 and every agent reading the spec's requiredPermissions description reaches it today; #27506 step 2 and the ui step 3 are cut from it
      Thread-read: 6500467241
      Premise: holds (governing ruling 6499781492 on 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). On origin/main 0e33108a, ADR-0066 D4's 2026-07 scope note and the requiredPermissions docblock still say a type: 'api' action is fetched by the browser and never passes through the route. isHeadlessInvokableAction admits script, flow and operation: 'update' only (ruling 6499781492, measured).
      Serial constraints cleared: no open PR touches docs/adr/0066*, docs/adr/0126* or packages/spec/src/ui/action.zod.ts (the file lists of all open PRs were read). 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 server PR depends on this one landing first.

    Tier H (docs/adr/**, packages/spec/src/**): the dev opens a draft PR with no assignees. An independent full Fable review and the seat's contract review at CONTRACT_REVIEW_TIER come first. Then the seat hands off to GOVERNED_APPROVERS via pr_request_reviewers, with needs-user-decision on the PR.

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU
    os-dev-report

    {
      "issue": 27610,
      "status": "done",
      "branch": "claude/issue-27610-adr-0066-d4-api-actions-door",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27664",
      "head": "e44fd0348855f641ddea93e791fc8b4f5a84aca5",
      "session": "session_017ihL2dPRZXfgE1A2Vct3FU",
      "premise_still_valid": true,
      "summary": "Step 1 of the ruling on #27506 (6499781492), text only — no runtime code, no schema shape change. ADR-0066 D4 'Scope of the server half': the 2026-07 api clause (framework#3923) is superseded in place — a `type: 'api'` action whose `target` is a same-origin platform path is dispatched by the platform action door (`POST /actions/:object/:action` + MCP `run_action`), which runs 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, not a replacement); an off-origin api action stays browser-only and ungoverned and the activation write door refuses to flip it. The old clause is kept verbatim as superseded in an Amended (2026-10-29) block-quote with Ruling-ref 6499781492 and the maintainer's words untranslated; the Status line carries the matching amendment; the note states today's behaviour (isHeadlessInvokableAction admits script/flow/declarative update only) and the three landing steps. ADR-0126 §8 item 2 gains one sentence naming the `/actions` door as the consult point that covers `type: 'api'`. action.zod.ts `requiredPermissions` docblock + `.describe()` reworded to match; `check:generated --fix` regenerated the three reference pages (ui/action, data/object, kernel/metadata-plugin). Changeset `@objectstack/spec` minor, `Clause-②: no`, `Metadata-protocol: semantic`. Draft PR #27664, no assignees, no labels sent (Auto Label added documentation/size-s/tooling/protocol-ui — another actor). SENTENCES CHANGED (file:line, before → after): docs/adr/0066 L3 Status line (appended `· **Amended** (2026-10-29, ruling of record 6499781492 on #27506 …)`); docs/adr/0066 L74 ('…the PLATFORM ACTION ROUTE … where `type: script | flow | modal` actions execute. A `type: api` action whose `target` is a self-authored endpoint is fetched by the browser directly and never passes through that route, so the declaration has no server-side consumer there; that endpoint must re-check the capability itself.' → '…the PLATFORM ACTION DOOR … dispatch script, flow and the declarative update. A `type: api` action whose `target` is a same-origin platform path is dispatched by that door too … forward the call in-process to the declared target as the caller … defence in depth, not a replacement … an off-origin api action stays browser-only and ungoverned … the activation write door refuses to flip it, with a reason'); docs/adr/0066 L76 (new Amended block-quote: old clause verbatim, ruling, maintainer's words, modal correction, landing steps, today's refusal); docs/adr/0126 L431–L438 (one sentence appended to §8 item 2); packages/spec/src/ui/action.zod.ts L1593–L1619 docblock ('Server enforcement covers the platform's own invocation paths only … script | flow | modal … called by the browser DIRECTLY: the platform never sees the request … MUST re-check the capability itself (framework#3923)' → 'Server enforcement is the platform action door … script, flow, the declarative update and — per ADR-0066 D4 as amended 2026-10-29 — a same-origin type: api action … defence in depth … off-origin stays browser-only … Landing: … until it lands the door still refuses type: api'); action.zod.ts L1620 `.describe()` ('Enforced with 403 on the platform action route (script/flow/modal + MCP) and mirrored as a UI hide; a `type: api` action pointed at a custom endpoint must re-check it there.' → 'Enforced with 403 on the platform action door (`POST /actions/:object/:action` and MCP `run_action`) for script, flow, the declarative update and — per ADR-0066 D4 as amended 2026-10-29 — a `type: api` action whose `target` is a same-origin platform path, and mirrored as a UI hide. The same-origin endpoint keeps re-checking its own authorization (defence in depth, not a replacement); a `type: api` action with an off-origin `target` is browser-only and not gated server-side.'); content/docs/references/ui/action.mdx L60, data/object.mdx L505, kernel/metadata-plugin.mdx L350 (regenerated from the describe). FOUND, NOT CHANGED: action.zod.ts alias comment near `permissions: 'requiredPermissions'` and the `hidden` guidance ('enforced with a 403 on the platform action route' — gate, not the browser clause; still true); action.zod.ts onSuccess/outcomeMessages/undoable text (api response/undo, not the gate); packages/runtime/src/action-execution.ts headlessActionTypeError docblock + api prescription and domains/actions.ts type-dispatch comment (#27506's runtime surface); content/docs/ui/actions.mdx (type table, 'api → 400 … call that endpoint directly', troubleshooting row), content/docs/ai/connect-mcp.mdx (api never listed by list_actions), content/docs/ai/actions-as-tools.mdx (today's door, truthfully — Skills-docs follow-up #27506); ui/packages/components/src/renderers/layout/containers.tsx capability-gate comment and the docblocks of ui action-bar.test.tsx, page-header-capability-gate.test.tsx, plugin-grid bulkActionCapabilityGate.test.tsx (restate 'never for a type: api action pointed at a custom endpoint'; ui lane, ruling step 3).",
      "tests": "Head e44fd034, under the shared lock (OS_VERIFY_LOCK_SLOT=issue-27610). `pnpm --filter @objectstack/spec build` → VERDICT command-exit 0 (held 87s). `pnpm --filter @objectstack/spec typecheck` → VERDICT command-exit 0 (held 79s). `pnpm --filter @objectstack/spec test` → VERDICT command-exit 0 (held 204s): 'Test Files 752 passed (752) · Tests 22343 passed | 1 todo (22344)'. `pnpm --filter @objectstack/spec check:generated` → exit 1 before fix ('✗ 1 of 14 artifact(s) stale: check:docs'), `--fix` exit 0 (gen:docs only, re-check ✓), re-run exit 0 ('All 14 generated artifacts are up to date'). No ablation: no tested behaviour changed (prose, docblock, describe, generated projection).",
      "gates": "Local subset, exit captured before any pipe, derived by `node scripts/pm/dispatch-gates.mjs --commands` on the final diff (101 families): 94 exit 0 — check:adr-anchors (no anchor names 0066/0126/action.zod.ts, so no scripts/adr-anchors change), check:adr-index, check:adr-symbol-anchors, check:adr-links(+self-test), check:doc-authoring, 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; 'adds no declared-breaking changeset'), check-empty-changeset --base origin/main (+self-test; '1 declaring changeset added'), check-changeset-no-major --base origin/main ('no major'; level axis NOT APPLICABLE locally, no pull_request payload), check:changeset-gate-self-tests, check:pm-changeset-deadline-census, check-closing-keyword-parity(+self-test), spec check:api-surface / authorable-surface / browser-reachable-entries / docs / dual-source-exports / duration-unit-keys / empty-state / entry-nameability / export-origins / exported-any / liveness / llms-txt / skill-refs / strictness-ledger / variant-docs / yaml-examples / generated, 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, check-doc-route-spelling(+self-test), check-docs-section-name(+self-test), check-section-landing-index(+self-test), 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, comment-mask adoption/corpus, plugin-teardown-shape (run), ci/* self-tests, docs-audit check-affected-docs + check-drift-comment, 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. 7 exit 3 = NOT MEASURED locally (PREREQUISITE NOT MET: fresh worktree with only packages/spec built; declared narrowing — the closure is 39 workspace packages, CI's repo-level build): lint check:doc-formula-expressions (formula not built), lint check:doc-security-posture (lint not built), spec check:skill-examples (client-react dist absent), check:docs-transcript-drift (lint dist absent), check:dual-build-cjs-loads (90 dists absent), check:lean-entry-closure (objectql dist absent), check-plugin-teardown-shape --self-test (fixture commit outside the shallow clone). `--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 readings: check:skill-frame-freshness exit 0; check:dev-prereqs exit 1 (104/105 packages have no dist in this worktree — machine state, not a gate red). CI on head e44fd034 at report time (34 check-runs): success — Lint · scoped · tooling self-tests, Lint · PM dispatch-gates, Governed Surface Queue Guard, Spec property liveness, Check PR Size, PR-body guards, Check Documentation Links, Auto Label, Dogfood Regression Gate, filter; in_progress — Build Core, Build Docs, Build UI Deps, Plan Test Core shards, Live E2E UI, Lint · docs & code guards, Lint · ESLint, Lint · CI release & engine gates, Lint · repo & tooling gates, Lint · scoped (query-options erasure; slot lookup & corpus), Type Check (workspace; source gates; debt ledger; consumer gates), Check Changeset, Flag docs affected by code changes; skipped — the opt-in and matrix placeholders. Not waited for: CI convergence is the PM's.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub tools used (reads via `gh api` GET only)",
      "api_writes": "2 REST writes, both through the fleet relay as objectstack-fleet[bot]: ① pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft forced, no assignees, no labels sent; run 40915111382 success; read-back identical, 15759 bytes) → #27664; ② this comment → POST /repos/objectstack-ai/objectstack/issues/27610/comments via post-stamped.mjs. Three `git push` (probe empty branch, 43f9c162, e44fd034) are not REST writes.",
      "deviations": [
        "Changeset level: the card, the claim and the dispatch word said `patch`; written as `minor`. Ruling A (check-changeset-no-major.mjs level axis; AGENTS.md Post-Task Checklist step 3; os-dev.md 'kind 即 minor') grades a declared kind (`Metadata-protocol: semantic`) on a moved package `minor` and refuses `patch` as `enforce` exit 1. Standard clause over dispatch word, per os-dev.md; flagged here rather than chosen silently.",
        "Factual correction inside the amended D4 paragraph: `modal` dropped from the list of door-dispatched types (and from the describe's 'script/flow/modal'). On origin/main 0e33108a `SERVER_DISPATCHED_ACTION_TYPES` is `script`, `flow` (packages/runtime/src/action-execution.ts), `headlessActionTypeError` returns the client-side prescription for `modal`, and the route answers it 400 (packages/runtime/src/domains/actions.ts, the #3915 type-dispatch branch). Recorded in the ADR note and the PR body.",
        "Declared narrowing: 7 derived families NOT MEASURED locally (exit 3, prerequisite = a 39-package build); CI measures them. `--ran` accounts for all 101.",
        "`Skills-docs: follow-up #27506`: content/docs/ui/actions.mdx, content/docs/ai/connect-mcp.mdx and content/docs/ai/actions-as-tools.mdx describe today's door (api → 400; api never listed by MCP) truthfully and would advertise an undelivered capability if rewritten now; no skills/** file teaches the superseded clause (grep: zero hits)."
      ],
      "open_questions": [
        {
          "question": "Who rewrites the hand-written docs that describe today's api door (content/docs/ui/actions.mdx type table + 'api → 400' rows, content/docs/ai/connect-mcp.mdx list_actions rows, content/docs/ai/actions-as-tools.mdx type row) once the server half lands?",
          "options": ["A — #27506's server PR carries the docs flip (the PR that makes the old rows false)", "B — a separate docs-lane card, Blocked-by #27506"],
          "recommendation": "A, because the rows become false the moment #27506 merges and Prime Directive #10 forbids the gap in either direction; the Skills-docs line on #27664 already names #27506."
        }
      ],
      "out_of_scope_findings": [
        "class: none (observation) · ui/packages/components/src/renderers/layout/containers.tsx (capability-gate comment), 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 restate the superseded clause ('never for a type: api action pointed at a custom endpoint') in docblocks · dedupe: ADR-0066 D4, type api, custom endpoint, capability gate comment · carrier: the ui card (ruling step 3) · noted, not filed",
        "class: none (observation) · packages/runtime/src/action-execution.ts headlessActionTypeError's api prescription ('Call its target endpoint directly') and docblock ('api names a different endpoint the client calls itself'), packages/runtime/src/domains/actions.ts type-dispatch comment — true today, reversed by the server half · carrier: #27506 · noted, not filed"
      ]
    }
  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU
    os-dev-report
    Delta round · reported 2026-10-29T18:10Z · head 7b404e6

    {
      "issue": 27610,
      "report_kind": "delta — patch round on the seat's pre-review findings comment 6503385688 (M1, M2, S1, S2, S3, S4); the earlier report 6502938739 stands except where this one replaces it",
      "status": "done",
      "branch": "claude/issue-27610-adr-0066-d4-api-actions-door",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27664",
      "head": "7b404e6c5d1f10c6a3cfe23214516ff90e80cfd0",
      "previous_head": "e44fd0348855f641ddea93e791fc8b4f5a84aca5",
      "commits_added": "1 (7b404e6c, on top of e44fd034; no amend, no force push; trailers Co-Authored-By: Claude + Claude-Session only, no Refs line)",
      "session": "session_017ihL2dPRZXfgE1A2Vct3FU",
      "model_note": "run on opus under the coordinator's rule while the mandated Fable tier answers 429; the at-tier Fable review is still owed before landing",
      "premise_still_valid": true,
      "summary": "All six findings applied, text only: no runtime code and no schema shape change. M1: the Studio action form's requiredPermissions helpText (action.form.ts) no longer teaches the superseded clause or the false modal list, and its en mirror plus the zh-CN / ja-JP / es-ES translations in @objectstack/platform-objects say the same thing. M2: ADR-0066 (Status, D4 paragraph, Amended note), the action.zod.ts docblock and the .describe() now say that the door gates the calls it dispatches, that the target endpoint stays directly reachable, and that it MUST still enforce the declared capability itself, with the door's gate as the added check. The Amended note supersedes only the browser-only half (fetched by the browser / never passes the route / no server-side consumer); the re-check clause stands. S1: the .describe() and the helpText say 'on every call the door dispatches', which is true before and after #27649 merges. S2: the 'no further change' claim is replaced by the recorded gap: MCP keeps ai.exposed and the fail-closed sys_* guard, so sys_user.ban_user and the like reach an agent through REST POST /actions. S3: ADR-0126 §8 says the switch stops the action's dispatch, not a direct call to the endpoint, and names step 3 (#27611) as where the console click meets the consult. S4: 'same-origin platform path' is defined as #27649's isSameOriginApiTarget judges it, checked against that PR's diff. The changeset text is updated, the level stays minor, and @objectstack/platform-objects: patch is added for the translation bundles.",
      "findings_map": {
        "M1": [
          "packages/spec/src/ui/action.form.ts:178 → :178 · before: 'The platform action route refuses anyone else with 403 (script, flow and modal actions, and the MCP/AI path), and the button is hidden from them. A type api action calls its endpoint directly, so that endpoint must re-check them.' → after: 'The platform action door refuses anyone else with 403 on every call it dispatches, the MCP/AI path included, and the button is hidden from them. A type api action's endpoint stays directly reachable, so it must still check these capabilities itself; for an api action with an off-origin target the platform checks nothing server-side.'",
          "packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts:1880 → :1880 · the en mirror of the line above, byte-equal to what the extractor writes (pnpm check:i18n exit 0: 'all bundles in sync')",
          "packages/platform-objects/src/apps/translations/zh-CN.metadata-forms.generated.ts:1880, ja-JP…:1880, es-ES…:1880 → same lines · each hand-written translation restated the superseded clause and the modal list (zh-CN: 'type 为 api 的动作由浏览器直接调用其端点'); retranslated from the new en text. These are real translations, not source-hash fills, so no .source-hashes companion moves (check:i18n-stale-fill exit 0)",
          "re-grep of packages/spec/src, one more restatement found and fixed: packages/spec/src/ui/action-declarative-update.ts:124 → :124 · '(`api` makes the browser call the endpoint itself)' → '(`api` calls the endpoint it names)', which is true before and after steps 2 and 3. No test pins the string (git grep: 0 hits outside the source)",
          "re-grep, found and NOT changed because they do not restate the api clause: action.zod.ts:980 alias comment and :995 `hidden` guidance ('enforced with a 403 on the platform action route': the gate, still true); action.zod.ts:1488 `undoable` ('type: api is fulfilled by the console': undo, not the gate); action-declarative-update.ts:142-144 target refusal ('to call an endpoint of your own instead, use type api': true both ways); component.zod.ts, conversions/registry.ts and migrations/** 'platform action route' mentions (they are about the operation: 'update' dispatch, not api)"
        ],
        "M2": [
          "docs/adr/0066-unified-authorization-model.md:3 → :3 (Status) · 'D4's 2026-07 api clause is superseded … the endpoint's own check stays as defence in depth' → 'the browser-only half of D4's 2026-07 api clause is superseded … the `target` endpoint stays directly reachable and must still enforce the declared capability itself, the door's gate being the added check'",
          "docs/adr/0066-unified-authorization-model.md:74 → :74 · 'The target endpoint keeps re-checking its own authorization: defence in depth, not a replacement — the door enforces the action's declared contract, the endpoint enforces its own.' → 'The door gates the calls it dispatches, and only those: the `target` endpoint stays directly reachable — nothing routes a direct call to it through the door — so it MUST still enforce the declared capability itself, and the door's gate is the added check: defence in depth, not a replacement.'",
          "docs/adr/0066-unified-authorization-model.md:76 → :76 · heading 'reverses the 2026-07 clarification' → 'reverses the browser-only half of the 2026-07 clarification'; 'That sentence recorded the gap as the rule; it is superseded, not deleted.' → 'Its first half — fetched by the browser directly, never passes through that route, no server-side consumer — recorded the gap as the rule and is superseded, not deleted. Its last clause stands: the endpoint must still re-check the capability itself, now beside the door's gate (the paragraph above).' The 2026-07 sentence stays quoted verbatim (PD #13 provenance intact)",
          "packages/spec/src/ui/action.zod.ts docblock L1603-L1604 → L1605-L1610 · 'The target endpoint keeps re-checking its own authorization: defence in depth, not a replacement.' → 'The door gates the calls it dispatches, and only those: the `target` endpoint stays directly reachable, so it MUST still enforce this capability itself, the door's gate being the added check (defence in depth, not a replacement).'",
          "packages/spec/src/ui/action.zod.ts .describe() L1615 → L1619 · 'The same-origin endpoint keeps re-checking its own authorization (defence in depth, not a replacement)' → 'its `target` endpoint stays directly reachable, so it MUST still enforce this capability itself, the door's gate being the added check'"
        ],
        "S1": [
          "packages/spec/src/ui/action.zod.ts .describe() L1615 → L1619 · 'Enforced with 403 on the platform action door (…) for script, flow, the declarative update and — per ADR-0066 D4 as amended 2026-10-29 — a `type: api` action whose `target` is a same-origin platform path' → 'Enforced with 403 on every call the platform action door (`POST /actions/:object/:action` and MCP `run_action`) dispatches, and mirrored as a UI hide. ADR-0066 D4, as amended 2026-10-29, brings a `type: api` action whose declared `target` is a same-origin platform path (…) under that door' — no present-tense claim that api is dispatched today",
          "regenerated by check:generated --fix: content/docs/references/ui/action.mdx:60, content/docs/references/data/object.mdx:505, content/docs/references/kernel/metadata-plugin.mdx:350 (one row each)",
          "the helpText (M1) uses the same 'on every call it dispatches' phrasing"
        ],
        "S2": [
          "docs/adr/0066-unified-authorization-model.md:76 → :76 · '(so MCP `list_actions` and `run_action` expose it with no further change)' → '(and the `list_actions` filter in `packages/runtime/src/domains/mcp.ts#buildMcpBridge` is widened beside it) … That does not put every api action in front of an agent through MCP: `list_actions` and `run_action` keep the `ai.exposed` opt-in and the fail-closed `sys_*` guard (`packages/runtime/src/domains/mcp.ts#buildMcpBridge` for the listing, `packages/runtime/src/action-execution.ts#invokeBusinessAction` for the call, both through `packages/runtime/src/action-execution.ts#isSystemObjectName`), so `sys_user.ban_user` and the other api actions on platform `sys_*` objects stay off MCP and reach an agent through REST `POST /api/v1/actions/OBJECT/ACTION` — a gap this amendment records and does not close.'",
          "evidence on origin/main 33ab5439: action-execution.ts invokeBusinessAction (L2637) throws 'is on a system object and is not exposed via MCP' on isSystemObjectName (L2686), before the headless and ai.exposed gates; mcp.ts L830 skips sys_* in the listing ('fail-closed on sys_*'); #27649 head 75d7d9e1 edits mcp.ts L830-L837 (adds `action?.type !== 'api'` to the script-arm handler filter), so 'no further change' was false",
          "the changeset records the same gap in one sentence"
        ],
        "S3": [
          "docs/adr/0126-packaged-metadata-customization-model.md:438-439 → :438-441 · '…refuses to flip it — the server half lands under #27506.' → '…refuses to flip it. The switch stops the action's dispatch, not a direct call to its `target` endpoint: the door's half lands under #27506, and the console's click meets the consult only from step 3 (#27611), which sends it to the door instead of fetching `target`.'",
          "docs/adr/0066-unified-authorization-model.md:76 · 'Step 3 is the ui card:' → 'Step 3 is the ui card #27611:' (#27611 is open: 'Console: send a same-origin type api action click through POST /actions/:object/:action instead of fetching its target directly')"
        ],
        "S4": [
          "docs/adr/0066-unified-authorization-model.md:74 → :74 · inserted after 'same-origin platform path': 'the declared `target`, before interpolation, starts with exactly one `/` and goes on past it (`/` alone, `//host` and `/\\host` are not), as the server card's `isSameOriginApiTarget` judges it (PR #27649; the door holds the interpolated path to the same test before it forwards)'",
          "packages/spec/src/ui/action.zod.ts docblock L1600 → L1600-L1602 · '(the declared `target`, before interpolation, starts with exactly one `/` and goes on past it — not `//`, nor `/` and a backslash)'; .describe() L1619 · '(exactly one leading `/`, followed by neither `/` nor a backslash)'; changeset: same definition",
          "verified against #27649 head 75d7d9e1, packages/runtime/src/action-execution.ts: isSameOriginApiTarget(target) is false unless target is a string of length 2 or more that starts with '/' and whose second character is neither '/' nor a backslash. It is applied to the DECLARED action.target by isHeadlessInvokableAction, isSameOriginApiAction and offOriginApiActivationRefusal, and buildApiActionRequest applies it again to the interpolated path, answering 400 when that path is not same-origin. Not cited as path#symbol: the symbol is not on main yet, and check:adr-symbol-anchors would red",
          "the backslash is spelled in words in the describe, the docblock and the changeset. Measured: gen:docs escaped a literal one to a doubled backslash inside the MDX table code span, and TSDoc reads a backslash before a backtick as an escape. The ADR keeps the literal form"
        ]
      },
      "changeset": ".changeset/27610-adr-0066-d4-api-actions-door.md — level stays `@objectstack/spec: minor` (Metadata-protocol: semantic; Clause-②: no, unchanged); added `@objectstack/platform-objects: patch` (the four translation bundles ship in that package); Before/Now text updated for M1 (helpText + bundles), M2 (obligation wording), S1 (states the 'every call the door dispatches' phrasing), S2 (MCP sys_* gap), S4 (definition) and the declarative-update refusal reword",
      "tests": "Head 7b404e6c, under os-verify-lock (OS_VERIFY_LOCK_SLOT=issue-27610-r2). `pnpm --filter @objectstack/spec build` → VERDICT command-exit 0 (held 71s). `pnpm --filter @objectstack/spec check:generated` → exit 1 ('✗ 1 of 14 artifact(s) stale: check:docs'), then `check:generated --fix` under the lock → VERDICT command-exit 0 (twice; the second after spelling the backslash in words), then a re-check → exit 0, '✓ All 14 generated artifacts are up to date'. `pnpm --filter @objectstack/spec test` → VERDICT command-exit 0 (held 335s), 'Test Files 752 passed (752) · Tests 22343 passed | 1 todo (22344)'. `pnpm --filter @objectstack/spec typecheck` → VERDICT command-exit 0 (held 76s). `pnpm --filter @objectstack/platform-objects test && … typecheck` → VERDICT command-exit 0 (held 53s), 'Test Files 97 passed (97) · Tests 1369 passed (1369)'. No ablation: no tested behaviour changed (prose, docblock, describe, help text, translations, generated projection). Build closure for the prerequisite-gated families, under the lock: the check:i18n closure (cli + 10 extract-config packages, 61 tasks) → VERDICT command-exit 0 (held 385s); 11 remaining unbuilt packages (studio, client-react, connector-slack, embedder-openai, knowledge-memory, knowledge-ragflow, organizations, service-cluster-redis, service-knowledge, service-print, skills) → VERDICT command-exit 0 (held 64s, 62 tasks).",
      "gates": "`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`, run on the final head with the change set committed (13 paths vs merge base 0e33108a6) → 103 families: the round-1 101 plus `pnpm check:i18n` and `pnpm check:i18n-stale-fill`, which the platform-objects bundles add. All 103 were run on head 7b404e6c, each exit captured before any pipe: 103 exit 0, 0 exit 3. Two of round 1's NOT MEASURED families were cleared by building the closure above (check:dual-build-cjs-loads: '112 published require entry point(s) across 69 package(s) load'; spec check:skill-examples: '274 prose examples type-check'). check-plugin-teardown-shape --self-test was cleared by fetching its one pinned fixture commit (48 cases pass). The other four (lint check:doc-formula-expressions, lint check:doc-security-posture, check:docs-transcript-drift, check:lean-entry-closure) went green after the first closure build. `--ran r2-ran.list` → exit 0: '103 derived famil(ies) accounted for — 103 run, 0 NOT-MEASURED (a DERIVED zero — all 103 recorded an exit code and none of them is 3)'. Selected verdict lines: check:i18n 'OK (10 package(s) — all bundles in sync, no undeclared authoring keys)'; check:i18n-stale-fill 'OK (11 bundle set(s) — no new stale fills, 0 baselined)'; check:adr-symbol-anchors '2810 anchors across 147 records resolve'; check:doc-authoring '19775 customer-facing string(s) … clean — no internal issue-id references'; spec check:docs '232 generated files in sync'; check-adr-0087-registration --base origin/main 'adds no declared-breaking changeset'; check-empty-changeset --base origin/main '1 declaring changeset(s) added'; check-changeset-no-major --base origin/main green (level axis needs the pull_request payload, so CI judges it). Local-only readings: check:skill-frame-freshness exit 0; check:dev-prereqs exit 1 (35 of 105 packages, all ui/, have no dist in this worktree: machine state, not a gate red). CI on 7b404e6c at report time (34 check-runs): success — Auto Label, PR-body guards, Check Documentation Links, filter, Governed Surface Queue Guard, Check PR Size; 21 in_progress; 7 skipped. CI convergence is not awaited (it is the PM's).",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub tool used; every read was `gh api` GET",
      "api_writes": "1 REST write: this comment, POST /repos/objectstack-ai/objectstack/issues/27610/comments via scripts/pm/post-stamped.mjs. `git push -u origin claude/issue-27610-adr-0066-d4-api-actions-door` (e44fd034..7b404e6c) is not a REST write. The PR body was not edited, no reviewer was requested, and the PR was not readied or armed",
      "pr_body_delta_for_the_seat": "The PR body (written once at pr_create, which a dev may not PATCH) is now stale in these places: 'What changes' item 1 ('The endpoint keeps re-checking its own authorization: defence in depth, not a replacement' → the obligation wording, plus the S2 MCP sys_* gap and the S4 definition); item 2 (the S3 sentence); item 3 (the describe now reads 'on every call the door dispatches'); item 5 (the changeset also carries `@objectstack/platform-objects: patch`); the 'Every other sentence on main' list (add action.form.ts:178 + its four bundles, and action-declarative-update.ts:124, as changed); 'Tests'/'Verification' (head 7b404e6c, 103 families, 103 exit 0, --ran 0 NOT-MEASURED). The seat rewrites it",
      "deviations": [
        "One commit as dispatched, not a WIP push before each minutes-long step (AGENTS.md Build & Test). A patch file in the scratchpad was the off-tree copy while the builds ran.",
        "Surface reach: besides the files named in the dispatch, this round edited packages/platform-objects/src/apps/translations/{en,zh-CN,ja-JP,es-ES}.metadata-forms.generated.ts:1880, and added `@objectstack/platform-objects: patch` to the changeset. Reason: the en value mirrors action.form.ts:178 (check:i18n reds without it), and the three translations restated the superseded clause, which falls under the claim's 'any other sentence on main that restates the old api clause'. Translated values are hand-written by rule. No runtime code was touched.",
        "To clear check-plugin-teardown-shape --self-test, ran `git fetch --no-write-fetch-head --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4` in the shared object store. It adds one shallow root, does not write FETCH_HEAD and moves no ref.",
        "The .describe() wording changes shape, not only phrase. It no longer lists script / flow / the declarative update: 'every call the door dispatches' covers them without claiming api dispatch today. The docblock keeps the list."
      ],
      "open_questions": [
        {
          "question": "Keep the platform-objects translation edits (the en mirror plus the zh-CN / ja-JP / es-ES retranslations of the requiredPermissions help text) inside this Tier H PR?",
          "options": [
            "A — keep: one key, one meaning in every locale; check:i18n and check:i18n-stale-fill green; changeset carries platform-objects patch",
            "B — keep only the en mirror (mechanically required) and leave the three translations to a follow-up: zh-CN, ja-JP and es-ES Studio users keep reading the superseded clause and the false modal list until then"
          ],
          "recommendation": "A, because M1's defect (one key, two meanings) would otherwise survive in three locales, the zh-CN one included, and the edits are value-only in files whose translated values are hand-written by rule"
        }
      ],
      "out_of_scope_findings": [
        "none new. Round 1's observations stand: the ui/ docblocks go to the ui card #27611; the runtime prescriptions and content/docs rows go to #27506. The S2 MCP sys_* gap is recorded in ADR-0066 D4's note, and it was #27649's own class-b observation (carrier: person, noted, not filed)"
      ]
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU
    os-dev-report
    Delta round · reported 2026-10-29T19:48Z · head 1d90647

    {
      "issue": 27610,
      "report_kind": "delta — patch round on the at-tier contract review 6505129020 (M1, S1, S2, S3), plus the seat's mid-round order for should-fix S1 of the delta review 6505934898; reports 6502938739 and 6504664624 stand except where this one replaces them",
      "status": "done",
      "branch": "claude/issue-27610-adr-0066-d4-api-actions-door",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27664",
      "head": "1d906476b9b45d7d1fddee3eb4137047d03e1501",
      "previous_head": "7b404e6c5d1f10c6a3cfe23214516ff90e80cfd0",
      "commits_added": "2 — a5944470 (the four findings of 6505129020) and 1d906476 (the seat's order on 6505934898 S1, made as a second commit on the seat's word; no amend, no force push). Each carries only the Co-Authored-By: Claude and Claude-Session trailers, no Refs line",
      "session": "session_017ihL2dPRZXfgE1A2Vct3FU",
      "model_note": "run on opus under the coordinator's rule while the Fable tier is exhausted",
      "premise_still_valid": true,
      "summary": "Text-only round: no runtime code, no schema shape, no changeset and no translation bundle moves. M1: ADR-0066 D4 now splits the Set-Cookie clause by door: the target's Set-Cookie is returned on the REST route's response, and the MCP bridge relays only the bearer-lane rotation headers (set-auth-token, set-admin-session-token), so no cookie value enters a tool result. This matches #27649 at a87722dd (forwardApiAction, mcpRelayableForwardHeaders, invokeBusinessAction's api arm). No other surface of this PR claimed the bridge returns Set-Cookie. S1: the action.zod.ts Landing paragraph is dated as of 2026-10-29 and names who removes each clause. S2: the .describe() same-origin gloss now also excludes `/` alone, and the three reference rows are regenerated. S3: ADR-0066 L74 says the isSameOriginApiTarget citation becomes a symbol anchor once the symbol is on main, and the Amended note names card #27681 as the carrier of the MCP sys_* gap. Seat order (6505934898 S1): those two plan sentences no longer give the follow-up edits to #27649. They now name docs card #27694, which is Blocked-by #27649.",
      "findings_map": {
        "M1 (6505129020)": [
          "docs/adr/0066-unified-authorization-model.md:74 → :74 · before: 'the caller's cookies and auth headers carried, the target's `Set-Cookie` returned.' → after: 'the caller's cookies and auth headers carried. 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.'",
          "evidence, #27649 head a87722dd, packages/runtime/src/action-execution.ts: forwardApiAction collects `getSetCookie()` into `relayed['set-cookie']` beside the bearer pair; mcpRelayableForwardHeaders returns only `set-auth-token` / `set-admin-session-token` ('Never `Set-Cookie`: … an `HttpOnly` session cookie's value must not travel in it'); invokeBusinessAction's api arm spreads only `mcpRelayableForwardHeaders(answer.headers)` into the tool result; packages/runtime/src/domains/actions.ts L844 relays `answer.headers` on the REST response",
          "the other surfaces were checked and left unchanged: a case-insensitive grep for 'cookie' over the 13 changed paths at 7b404e6c had one hit (ADR-0066 L74). The Amended note (L76), the Status line (L3), the action.zod.ts docblock and .describe(), action.form.ts:178, the changeset and the en / zh-CN / ja-JP / es-ES bundles (line 1880 of each, also grepped for the Japanese and Chinese words) make no Set-Cookie claim, so none was edited. The bundles stay as round 2 left them",
          "the PR body's item 1 carries the same clause. That is for the seat to fix, see pr_body_delta_for_the_seat"
        ],
        "S1 (6505129020)": [
          "packages/spec/src/ui/action.zod.ts:1614-1617 → :1614-1619 · before: 'Landing: ADR-0066 records the decision; the server half is the ruling's step 2 (#27506). Until it lands the door still refuses `type: 'api'` (…) and the console fetches `target` directly.' → after (1d906476): 'Landing, as of 2026-10-29: ADR-0066 records the decision; the server half is the ruling's step 2 (#27506). Until it lands the door still refuses `type: 'api'` (…) and the console fetches `target` directly. Once the server half (#27649) lands, card #27694 removes this paragraph's door clause; its console clause goes when step 3 (#27611) lands.'",
          "the sentence is split by clause on purpose. After step 2 the console clause stays true until step 3 lands, so removing the whole paragraph with the server half would drop a fact that is still true",
          "a5944470 first said 'The server PR (#27649) removes this paragraph's door clause when it lands'. 1d906476 replaced that with the #27694 wording the seat dictated"
        ],
        "S2 (6505129020)": [
          "packages/spec/src/ui/action.zod.ts .describe() :1619 → :1621 · '(exactly one leading `/`, followed by neither `/` nor a backslash)' → '(exactly one leading `/`, followed by neither `/` nor a backslash, with a path after it)'. This matches isSameOriginApiTarget, which is false for a target shorter than two characters",
          "regenerated with `pnpm --filter @objectstack/spec check:generated --fix` after the spec build. Only check:docs was stale: content/docs/references/ui/action.mdx:60, content/docs/references/data/object.mdx:505 and content/docs/references/kernel/metadata-plugin.mdx:350 each gain ', with a path after it'. Not hand-edited"
        ],
        "S3 (6505129020)": [
          "docs/adr/0066-unified-authorization-model.md:74 → :74 · before: '(PR #27649; the door holds the interpolated path to the same test before it forwards)' → after (1d906476): '(PR #27649; once the symbol is on `main`, card #27694 rewrites this citation into a symbol anchor on `isSameOriginApiTarget` in `packages/runtime/src/action-execution.ts`; the door holds the interpolated path to the same test before it forwards)'. The anchor is spelled in prose, with no path#symbol form, so check:adr-symbol-anchors reads only the file-level path, which resolves on main. a5944470 had first written 'PR #27649, which on landing rewrites this citation …'",
          "docs/adr/0066-unified-authorization-model.md:76 → :76 · '— a gap this amendment records and does not close.' → '— a gap this amendment records and does not close; card #27681 carries it.'",
          "confirmed #27681 before citing it: it is open (enhancement, priority:p1, needs-user-decision, domain:server), titled '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', and names both run_action guards (mcp-http-tools.ts allowSystemObjects and invokeBusinessAction's isSystemObjectName throw)"
        ],
        "S1 (6505934898, seat order)": [
          "the two plan sentences that gave #27649 the follow-up edits now name docs card #27694 (open, documentation, pm:blocked, domain:skills; body: 'Blocked-by #27649', and 'Wanted' lists exactly these two edits). Exact before and after text is under S3 (ADR-0066 L74) and S1 (action.zod.ts L1617-L1619) above",
          "the docblock change feeds no generated artifact: after the spec build, check:generated answered '✓ All 14 generated artifacts are up to date'"
        ]
      },
      "kept": "Prime Directive #13 provenance: the 2026-07 sentence stays quoted verbatim in the Amended note, the maintainer's ruling quote is untouched, and only the browser-only half is marked superseded. Changeset `.changeset/27610-adr-0066-d4-api-actions-door.md` is not touched: `@objectstack/spec: minor`, `@objectstack/platform-objects: patch`, `Clause-②: no`, `Metadata-protocol: semantic`",
      "tests": "Head 1d906476, under os-verify-lock (OS_VERIFY_LOCK_SLOT=issue-27610-r3). On a5944470: `pnpm --filter @objectstack/spec build` → VERDICT command-exit 0 (80s); `check:generated` → exit 1, '✗ 1 of 14 artifact(s) stale' (check:docs only); `check:generated --fix` → VERDICT command-exit 0 (150s); a re-check → exit 0, '✓ All 14 generated artifacts are up to date'. Then the full build closure for the dist-reading families, `pnpm exec turbo run build --filter=!@objectstack/docs --concurrency=2` → VERDICT command-exit 0, 'Tasks: 114 successful, 114 total' (770s; fresh worktree). On 1d906476: spec build → VERDICT command-exit 0 (67s); check:generated → exit 0 ('✓ All 14 generated artifacts are up to date'); `pnpm --filter @objectstack/spec test` → exit 0, 'Test Files 752 passed (752) · Tests 22343 passed | 1 todo (22344)'; `pnpm --filter @objectstack/spec typecheck` → exit 0 ('check:test-typecheck: OK'). The test and typecheck exits were each recorded inside the lock run. The lock's own line is a batch-last-exit, because the parts were joined with ';', so it is not cited as their verdict. No ablation: no tested behaviour changed (ADR prose, a docblock, a .describe() string and its generated projection).",
      "gates": "`node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` was run after `git fetch origin main`. Banner: 'derived from the tree … at commit 1d906476'; '13 path(s) vs merge base 0e33108a6'; 103 families, the same set as round 2. All 103 were run twice under the lock, each exit captured before any pipe: on a5944470, 103 exit 0 (lock held 1032s, then `--ran` → exit 0); on the final head 1d906476, 103 exit 0 again, and `--ran r4-full-ran.list` → exit 0: '103 derived famil(ies) accounted for — 103 run, 0 NOT-MEASURED (a DERIVED zero — all 103 recorded an exit code and none of them is 3)'. Verdict lines on 1d906476: check:adr-symbol-anchors '2811 anchors across 147 records resolve'; check:adr-anchors 'OK (91 anchored file(s), every governing ADR still referenced …)'; check:doc-authoring '19775 customer-facing string(s) across 1529 spec sources clean — no internal issue-id references'; check-issue-citations (diff scope) 'citations judged: 4 across 7 file(s) — 3 resolves, 1 resolves-as-pull-request … every citation this change adds resolves'; spec check:docs '232 generated files in sync with packages/spec'; check:spec-docblock-symbol-anchors '5231 anchors across 2291 spec sources resolve'; check:i18n 'OK (10 package(s) — all bundles in sync …)'; check-adr-links '935 relative link destination(s) under docs/adr/ resolve'; check:nul-bytes 'OK … no raw ASCII control bytes'. Local-only checks: check:skill-frame-freshness exit 0 ('1/1 framework files are byte-identical to the ref'); check:dev-prereqs exit 0 ('105 package build artifacts present'). CI on 1d906476 at report time: 50 check-runs, 41 success and 9 skipped, none failed and none pending. Success includes Lint & Repo Gates, TypeScript Type Check, Type Check · workspace, Test Core (8/8 shards), Build Core, Governed Surface Queue Guard, Check Changeset, Type Check UI, Test UI, Build & E2E UI, Live E2E UI and Dogfood Regression Gate. Lint UI is skipped",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "mcp_calls": "0 — no MCP GitHub tool used; every read was `gh api` GET",
      "api_writes": "1 REST write: this comment, POST /repos/objectstack-ai/objectstack/issues/27610/comments via scripts/pm/post-stamped.mjs. Two `git push -u origin claude/issue-27610-adr-0066-d4-api-actions-door` (7b404e6c..a5944470, a5944470..1d906476) are not REST writes. I did not edit the PR body, request a reviewer, ready the PR or arm it",
      "pr_body_delta_for_the_seat": "The PR body is still stale. Round 2's list stands, and these items are added: 'What changes' item 1's credential clause ('the target's Set-Cookie returned') becomes the door split (REST response carries Set-Cookie; MCP relays only set-auth-token / set-admin-session-token; no cookie value enters a tool result); the same-origin definition names card #27694 as the future anchor rewrite; the MCP sys_* gap names carrier #27681; the docblock's Landing paragraph is dated and its door clause is assigned to #27694; the .describe() gloss gains 'with a path after it'; 'Tests'/'Verification' move to head 1d906476 (103 families, 103 exit 0, --ran 0 NOT-MEASURED). The seat rewrites it",
      "deviations": [
        "Two commits, not one: the dispatch ordered one (a5944470), and the seat's mid-round message ordered the second (1d906476) on top of it, with no amend.",
        "No WIP push before each minutes-long step (AGENTS.md Build & Test). Each commit was pushed right after it was made and before its gate run. A patch file in the scratchpad was the off-tree copy during the builds.",
        "The derived families were run under the build lock, as dispatched, although os-dev.md treats check:* gates as running outside the lock. The lock held 1032s and 1318s for the two runs. The lock printed its holder-side-starvation advisory for the first run.",
        "`git fetch origin main` moved the shared refs/remotes/origin/main (524befb1 → da7bcc8a) and wrote FETCH_HEAD, which dispatch-gates prescribes for the strongest reading. An earlier `git fetch origin BRANCH` also wrote FETCH_HEAD. No other ref moved.",
        "The local branch claude/issue-27610-adr-0066-d4-api-actions-door already existed at 7b404e6c from round 2, so the worktree was added on it (`git worktree add PATH BRANCH`) and not created with -b from origin. Its tip equalled origin's 7b404e6c."
      ],
      "open_questions": [
        {
          "question": "The ADR's recorded gap covers both MCP list_actions (the buildMcpBridge sys_* skip) and run_action, but #27681's body names only the two run_action guards. Should #27681 also name the listing filter, so that one card carries the whole gap the ADR cites it for?",
          "options": [
            "A — the seat adds one line to #27681: packages/runtime/src/domains/mcp.ts buildMcpBridge skips sys_* in list_actions, so opening run_action alone still leaves the action unlisted",
            "B — leave #27681 as is: whoever decides it will find the listing filter when they open run_action"
          ],
          "recommendation": "A, because the ADR now cites #27681 for the whole gap, and a run_action-only fix would close the card while list_actions still hides the action"
        }
      ],
      "out_of_scope_findings": [
        "none new. The ③ escalations in 6505129020 are for the seats named there, not for this diff: the Skills-docs coverage of content/docs/ai/connect-mcp.mdx and actions-as-tools.mdx is for #27649's ACCEPT or a docs card, and the two RelatedList restatements (ui/packages/plugin-detail/src/RelatedList.tsx L810 and its toolbarCapabilityGate-9782 test L14) are to be added to #27611's note. carrier: the seats named in 6505129020 · noted, not filed"
      ]
    }
  6. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU

    Seat pointer · 2026-10-29T19:54Z · PR #27664 is ACCEPTed on head 1d906476 (6506803986, verify-command correction 6506826047) after the at-tier delta record 6506713235 (PASS). Handed to GOVERNED_APPROVERS (Tier H) with needs-user-decision. Follow-ups: #27694 (now Blocked-by: #27649, 6506768233) and #27681 (listing fact, 6506838679).

  7. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:skills#1 · marchtian · session_017ihL2dPRZXfgE1A2Vct3FU

    Landed · 2026-10-29T20:50Z: PR #27664 MERGED through the merge queue as 86c9d536b4. os-zhuang readied and enqueued it. Contract review at CONTRACT_REVIEW_TIER by the triage seat: full 6505129020 (FAIL on 7b404e6c, M1 Set-Cookie), delta 6505934898 (PASS on a5944470), delta 6506713235 (PASS on the landing head 1d906476); seat ACCEPT 6506803986.

    Step 1 of ruling 6499781492 on #27506 is done: ADR-0066 D4 now brings a same-origin type: 'api' action under the platform action door, with the 2026-07 clause kept verbatim as superseded. The card closed via Fixes; the seat strips pm:dispatched. It ships with the next @objectstack/spec release (minor) and @objectstack/platform-objects (patch). Next: step 2 (#27649, runtime) and step 3 (#27611, console); the two time-bound sentences are carried by #27694 (Blocked-by: #27649), the MCP sys_* gap by #27681.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions