Repository navigation
Commit 3986f9f
docs(adr): ADR-0066 D4 cites isSameOriginApiTarget by symbol anchor, and the requiredPermissions docblock drops its door clause (#27965)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DYV6Kpbtvq8fY9nDbSbzkLe)_
Part of #27694
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:ADR-0066 D4 对 `isSameOriginApiTarget` 的裸名引用改成符号锚
`packages/runtime/src/action-execution.ts#isSameOriginApiTarget`,由
`check:adr-symbol-anchors` 守住;同时删掉那句「符号上 main 后由 #27694
改写」的待办。`requiredPermissions` 文档注释的「Landing」段删掉「门禁仍拒收 `type:
'api'`」这一条款,控制台条款保留。
- 为什么改:#27649 已合并,平台动作门禁现在会派发同源 `type: 'api'` 动作。随 `@objectstack/spec` 的
`.d.ts` 发出去的注释还说门禁拒收,这对作者和 agent 是错话;ADR 用裸名引用,代码改名也没有门禁会发现。
- 风险与回滚:两个文件 +5/−7,只动 ADR 正文和一段 JSDoc 注释,不改
`.describe()`、不改代码、不发布(`skip-changeset`);回滚即 revert 这一个提交。
- 先看:`packages/spec/src/ui/action.zod.ts` 的 `requiredPermissions`
注释末段(Landing 段),再看 `docs/adr/0066-unified-authorization-model.md` D4
第二段的那处引用。
- 两分钟验证:`pnpm check:adr-symbol-anchors`,预期 exit 0,计数为 611 个符号锚(585
个声明锚),比 main 上的 610 多一个,多出的就是这条锚,此后符号改名或挪走会转红。
- 需要你做的:暂无。contract review owed(契约复审未完成,席位复审后再求批);本 PR 改 ADR 与
`packages/spec/src`,落地为 Tier H,由受管批准人批准。
## What changes
Two files, +5 / −7, on base `ca54eaca`.
- **`docs/adr/0066-unified-authorization-model.md`, D4 "Scope of the
server half".** The same-origin definition cited the predicate by bare
name, "as the server card's `isSameOriginApiTarget` judges it", followed
by a parenthetical that scheduled this very rewrite ("once the symbol is
on `main`, card #27694 rewrites this citation into a symbol anchor…").
The citation is now the symbol anchor
`packages/runtime/src/action-execution.ts#isSameOriginApiTarget`, and
the scheduling clause is gone. The rest of the parenthetical stays: "PR
#27649; the door holds the interpolated path to the same test before it
forwards".
- **`packages/spec/src/ui/action.zod.ts`, the `requiredPermissions`
docblock, paragraph "Landing, as of 2026-10-29".** The door clause goes:
"Until it lands the door still refuses `type: 'api'`
(`isHeadlessInvokableAction` admits `script`, `flow` and the declarative
update only)". It is false since #27649. The paragraph now says the
server half (step 2, #27506) is on `main` (#27649). The console clause
stays, because #27611 is still open: "Until step 3 (#27611) lands the
console fetches `target` directly; card #27694 removes this paragraph
when it does."
No `.describe()` changes, no code, no generated file.
## Why
- **The door clause is false on `main`.** `isHeadlessInvokableAction`
admits `type: 'api'` when `isSameOriginApiTarget(action?.target)` holds,
and `headlessActionTypeError` answers `null` for a same-origin api
action. Both are in `packages/runtime/src/action-execution.ts`, and
#27649 (`481ade61c`, an ancestor of the base) put them there. The
docblock ships in `@objectstack/spec`'s `.d.ts`, so authors and agents
were told the door refuses a call it now runs.
- **The console clause is still true.** `apiHandler` in
`ui/packages/app-shell/src/hooks/useConsoleActionRuntime.tsx` still
`authFetch`es the resolved `target`, and #27611 is open.
- **The ADR prose around the anchor still reads true.**
`isSameOriginApiTarget` returns `false` for a non-string, for a string
shorter than 2, for one not starting with `/`, and when the second
character is `/` or a backslash. That is the ADR's "starts with exactly
one `/` and goes on past it (`/` alone, `//host` and `/\host` are not)".
The forward re-tests the interpolated path (`if
(!isSameOriginApiTarget(path))` in `buildApiActionRequest`, which
assembles the request the forward sends), which is "the door holds the
interpolated path to the same test before it forwards".
## Deviations from the dispatch order
- **The Landing paragraph is reworded around the deleted clause, not
only cut.** Its door and console clauses shared one sentence ("Until it
lands the door still refuses … and the console fetches `target`
directly"), where "it" was the server half. Deleting the door half alone
would leave "Until it [the server half] lands the console fetches
`target` directly". That is false: the server half has landed and the
console still fetches. The last sentence scheduled two removals, and one
of them is this PR. So the paragraph keeps the console clause, ties it
to step 3, and keeps the pointer to #27694 for the second pass. The date
"as of 2026-10-29" is kept: #27649 merged 2026-10-29T21:32Z, so every
statement in the paragraph was already true on that date.
- **No dated amendment note in the ADR.** House form, measured: 53 ADRs
carry `path#symbol` anchors after the #13556 line→symbol migration, and
only one carries an "Anchor note" (ADR-0113). That note exists because
its re-anchoring dropped to file level and the row it anchors is stale.
ADR-0097's and ADR-0099's dated notes record changes of meaning. This
rewrite changes no meaning. It also carries out a step the 2026-10-29
amendment itself scheduled, under the paragraph's existing "api clause
amended 2026-10-29, Ruling-ref: 6499781492" label. So it follows the
no-note precedent of the anchor migration.
## Acceptance notes
These go stale with #27649 but are outside this card's file surface or
its ruling, so they are left as is:
- ADR-0066's **Status line** still ends "The server half lands under
#27506; see the note under D4." It is present tense, and the server half
has landed (#27649).
- The D4 **amendment note** (dated 2026-10-29) ends "Until step 2 lands
the door still refuses `type: 'api'` … so no agent can invoke a `type:
'api'` action today". It is a dated record and its condition ("until
step 2 lands") is spent. It is not edited: an amendment note is a record
of its date. Carrier: #27694's second pass, which the maintainer can
widen if wanted.
## Verification
All readings are on head `701d5499` (base `ca54eaca`).
<details><summary>dispatch-gates: 74 derived families, 74 exit
0</summary>
`node scripts/pm/dispatch-gates.mjs --commands` was run with no paths,
on the committed diff against merge base `ca54eaca`. It derives 74
families, and all 74 were run with exit codes captured before any pipe.
Prerequisites were `pnpm install --frozen-lockfile --ignore-scripts` and
`turbo run build --filter=@objectstack/spec
--filter=@objectstack/formula --filter=@objectstack/lint`. Two families
first exited 3 (PREREQUISITE NOT MET: `check:dual-build-cjs-loads`,
`check:lean-entry-closure`, both needing built dist outside spec). After
a whole-tree `turbo run build` (lock VERDICT command-exit 0), both
exited 0: 113 published require entry points across 70 packages load,
and the objectql/core admitted set holds exactly. `check:dts-closure`,
`check:sourcemap-no-sources-content` and `check:published-files` were
re-run over the whole built tree: exit 0, 115 / 108 / 74 packages swept.
`--ran`: `74 derived famil(ies) accounted for — 74 run, 0 NOT-MEASURED`.
Re-derived after the run, the list was identical.
The ADR gates in the list: `check:adr-symbol-anchors`,
`check:adr-anchors`, `check:adr-index`, `check-adr-links.mjs` (+
`--self-test`), `check-adr-0087-registration.mjs`, all exit 0.
`check:adr-symbol-anchors` counts 611 symbol anchors (585 declaration)
and 2161 file-level on this head, against 610 (584) and 2162 on
`ca54eaca`. The bare-path citation of `action-execution.ts` became a
declaration anchor, so the new anchor is in the gated population.
Spec-docblock gates: `check:spec-docblock-symbol-anchors`, `check:docs`,
`check:api-surface`, `check:authorable-surface`, all exit 0.
`check:nul-bytes` exit 0.
</details>
<details><summary>@objectstack/spec typecheck and tests</summary>
- `pnpm --filter @objectstack/spec run typecheck`: lock VERDICT
command-exit 0.
- `pnpm --filter @objectstack/spec run test`: `Test Files 756 passed
(756)`, `Tests 22481 passed | 1 todo (22482)`, lock VERDICT command-exit
0.
- No ablation: the diff changes prose and a comment, not tested
behaviour.
</details>
`git merge-tree` against `origin/main` `bacb3e6a` is clean, and no
commit since the base touched either file. `--tier` answers MANDATORY
`claude-fable-5-1` for `docs/adr/**`. The claim records the proactive
low-headroom downgrade, compensated by the contract-tier review. Changed
lines: 12 (+5 / −7), under the 3000 threshold.
Contract review owed: governed `docs/adr/**` and `packages/spec/src/**`,
at `CONTRACT_REVIEW_TIER`. That review also answers the Clause-② path
leg on `packages/spec/src/**`. Landing is Tier H.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(a JSDoc comment only)
- [x] 2. Every name I added uses the casing for its kind. (no names
added)
- [x] 3. No formatter ran; `pnpm lint` passes (prose only; the full run
is CI's).
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset. It changes a
comment only, no `.describe()` and no code, so `skip-changeset`. Both
declaration lines are above.
- [x] 6. `packages/spec` touched: nothing to regenerate. The change is a
JSDoc comment. After the spec build and all 74 gate runs, the tree was
clean (`git status` empty), and `check:docs` exited 0.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 is the
sanctioned Chinese channel).
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4d1LtiXudcBaRgY2xRg)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent b6db369 commit 3986f9f
2 files changed
Lines changed: 5 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
| 74 | + | |
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1611 | 1611 | | |
1612 | 1612 | | |
1613 | 1613 | | |
1614 | | - | |
1615 | | - | |
1616 | | - | |
1617 | | - | |
1618 | | - | |
1619 | | - | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
1620 | 1618 | | |
1621 | 1619 | | |
1622 | 1620 | | |
| |||
0 commit comments