Skip to content

docs(runtime): re-anchor the dead tracker citations in packages/runtime/src to the commits that decided them - #20624

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20594-runtime-dead-citations
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20594-runtime-dead-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20594
Clause-②: no

What changed

This is stage 1 of the domain:cli lane of the dead-citation sweep: packages/runtime/src/**, the lane's largest package. Every comment or docblock site in scope that cited a tracker number answering 404 now cites, in ruling C+D's form C (comment 5749154545 on #19123), the commit in this repository's history that decided what the line describes, and says in its own words what that commit decided. PR #20533 is the method and PR #20609 the closest sibling. Later stages cover rest, cli, types and the rest of the lane, so this PR says Part of and the card stays open.

That is 513 comment sites on 508 lines in 118 files, covering 96 numbers: 194 of the census's 217 sites, and 319 more in test comments, which the census defers. Three more sites carried a slash-joined dead number the citation grammar does not read (#10629/#10630, #5811/#12281, #8421/#12194), and they are rewritten too. Each rewritten line cites one of 95 distinct commits.

No ADR or ruling-record file in docs/adr/ or scripts/adr-anchors/ records the decision behind any of these numbers. ADR-0126 and ADR-0131 name #10243 only as the incident, ADR-0126 names #11513 only for the flow-clone half, and ADR-0112 names #12281 only as another card. So every anchor is a commit. The anchors the landed stages already gave the same numbers are reused (24 numbers, for example f19475c0a for #14143, e2798fab7 for #6345 and 79c46da90 for #9934), so each number carries one anchor across the tree.

Only comments changed. Every touched file keeps its line count (508 lines out, 508 in, over 118 files), so no line citation into these files moves. Seven of the 508 lines held no census site. Five are the other half of a sentence that had to change: action-governance-scope-divergence.test.ts:6 (「the card names」 to 「that diverged」, because line 4 no longer names the card), action-record-load-denied.test.ts:560, dispatcher-5xx-demoted-code-withhold.test.ts:45 (「that card's change」 to 「that commit's change」), hook-input-writeback-readonly-provenance.integration.test.ts:380 (「that card」 to 「that commit」) and standalone-stack-seeder-declaration-copy.test.ts:88 (a trailing 「PR」 whose number wrapped onto line 89). Two carry only a slash-joined number: dispatcher-plugin.ts:688 and meta-compound-arity-mint-door.test.ts:4. No code token moves (see the guard below).

No citation number is added. Every tracker number on an added line was already on the line it replaces. No PR number stands on an added line, and none of the 95 shas is on a removed line.

Twenty-eight dead comment sites are left on purpose:

One more file: a patch changeset for @objectstack/runtime, because the rewritten docblocks ship (see Changeset below).

Census: packages/runtime, before and after

Instrument. The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged, run with the fleet token. Its surface is comment prose in packages/**/src/**/*.ts with string literals blanked, and it defers *.test.ts. The count is its allocated-but-absent findings under packages/runtime/. Both runs enumerated the whole board (185 pages), so neither read a truncated board.

reading tree board whole-repo allocated-but-absent runtime sites lines files numbers
before base eb4b17c346, run 2026-09-29T07:55:51Z to 08:05:47Z enumerated, 185 pages, frontier #20614, 18,441 numbers 2,397 217 216 29 59
after head a5cdfd8a46, run 09:08:23Z to 09:14:11Z enumerated, 185 pages, frontier #20623, 18,450 numbers 2,027 23 23 4 12

The before count equals the card's 217 at f11b5f20a2. The 23 left are the 20 held domains/meta.ts sites and 3 deliberate ones (api-exposure.ts:108, domains/mcp.ts:360, route-ledger.ts:300). The whole-repo drop is 370: this diff's 194, plus the 97 and 79 of PR #20609 and PR #20612, which landed on main in between and came in with the merge.

Supplementary instrument, the whole scope. The census does not read test files or strings, and this stage's scope includes test comments. So a second reading runs the gate's own exported extractCitations (whole-file and comment-prose projections) and classifyCitation over every .ts file under packages/runtime/src (373 files), against a board probed by REST for every number cited there. The lit controls #16862, #16847 and #17698 answered 200 and the dead controls #16714, #16715 and #16697 answered 404 in both runs.

reading tree citations dead src comment test comment src string test string
before, 08:17:55Z eb4b17c346 5,364 641 217 324 5 95
after, 09:24:24Z a5cdfd8a46 4,851 128 23 5 5 95

Its src-comment column equals the census's 217 and 23, which is the control on the second instrument. The 4,499 resolving citations, the 195 that resolve as pull requests and the 29 cross-repo ones are the same in both readings. The drop is 513, exactly this diff's grammar-read sites.

Per-number table

Sites and files are the dead comment sites in scope at the base, tests included. held is domains/meta.ts (see above) and left is a site with no deciding commit or with a literal reader. strings kept counts string-literal sites, which are tokens and stay as they were. Every anchor was read in its message or its diff, not only in its subject: it is the commit that made the change the line describes, and its own message or diff names the number it replaces.

number comment sites / files rewritten held left strings kept anchor
#6065 1/1 1 0 0 0 026101660
#6123 1/1 1 0 0 0 59d1933f9
#6206 5/2 5 0 0 0 8e13ca876
#6216 2/1 2 0 0 1 f586f1a89
#6220 1/1 1 0 0 0 83df2fd73
#6238 2/2 2 0 0 2 c8d6f6e08
#6259 4/2 3 0 1 1 6968885ef
#6265 12/2 12 0 0 4 cfb549db8
#6268 9/3 9 0 0 0 68f5eccb1
#6287 1/1 1 0 0 0 84c86fb45
#6307 1/1 1 0 0 0 293476148
#6316 6/3 6 0 0 0 448ac9565
#6345 10/3 10 0 0 0 e2798fab7
#6361 4/2 4 0 0 6 90bbf2510
#6363 6/2 6 0 0 2 17d095413
#6483 3/2 3 0 0 0 ee58392e1
#8722 1/1 0 0 1 0 —
#8724 1/1 1 0 0 0 ff4ba6a06
#8726 8/4 7 1 0 1 e783e163d
#8796 13/3 13 0 0 4 a4331227b
#8848 3/2 1 2 0 1 4fc4a3c0b
#8919 1/1 0 1 0 0 b5378550e (held file)
#9934 17/7 17 0 0 4 79c46da90
#9967 1/1 1 0 0 0 8f266f1cd
#10179 1/1 0 0 1 2 —
#10243 40/13 40 0 0 9 266436a7f, 02b41232d
#10293 3/3 3 0 0 0 92a69d813
#10338 1/1 1 0 0 0 d2619fd0c
#10340 3/2 2 1 0 1 26f3588fb
#10380 12/2 12 0 0 0 dd8172ee2
#10485 3/3 3 0 0 0 35ad101bc
#10503 8/2 3 5 0 1 67ceb9aef
#10537 2/1 2 0 0 0 e634ecf6a
#10554 1/1 1 0 0 0 6abc4df03
#10629 75/23 75 0 0 0 13a6cb4ad
#10630 4/1 4 0 0 0 dd8172ee2
#10789 2/1 2 0 0 1 38bc74ed1
#10886 1/1 1 0 0 1 809e61221
#10888 3/3 2 1 0 1 d806081dd
#10961 5/3 5 0 0 3 222d06fc1
#10965 2/1 2 0 0 1 ab47f6974
#10978 1/1 1 0 0 0 4c9780c7a
#10983 3/2 3 0 0 0 6a4e929f5
#11006 4/4 3 1 0 0 cccbe51bf
#11015 3/1 3 0 0 0 82cb6e849
#11166 8/3 8 0 0 4 735f5c709
#11333 1/1 1 0 0 0 ea4d16420
#11504 3/2 3 0 0 0 f90e82024
#11513 2/2 2 0 0 0 e170b0ae5
#11703 8/3 8 0 0 1 5cb62d88b
#12010 1/1 1 0 0 0 77b91bdb4
#12176 5/5 5 0 0 0 7986d973f
#12194 11/4 8 3 0 0 311433f6b
#12195 9/4 4 5 0 10 7986d973f
#12281 20/5 20 0 0 5 0783d7b80
#12943 7/3 7 0 0 0 090f2302e
#13037 8/2 8 0 0 5 e7dfb1d69
#13233 5/1 5 0 0 0 3800e4293
#13241 5/4 5 0 0 1 a21d2a9cf
#13273 11/3 11 0 0 0 3a86a65e7
#13279 3/2 3 0 0 0 6a180e42d
#13325 3/1 3 0 0 0 2e0b7b18f
#13644 5/4 5 0 0 1 34ce8e7db
#13657 13/1 13 0 0 1 b003cf2e8
#14143 26/8 26 0 0 4 f19475c0a
#14390 1/1 1 0 0 0 9d7f7259f
#14398 3/1 3 0 0 0 317132495
#14403 6/1 6 0 0 0 93d2d679b
#14421 2/1 2 0 0 0 bd8795ea1
#14422 4/2 4 0 0 4 dc7c226b9
#14423 3/1 3 0 0 1 a56baa2bd
#14474 1/1 1 0 0 0 df657d9df
#14667 2/1 2 0 0 0 dc7c226b9
#14678 2/1 2 0 0 2 73ad0bba7
#14683 2/2 2 0 0 0 96326040f
#14723 1/1 1 0 0 0 65846bc46
#14745 1/1 0 0 1 0 —
#14748 1/1 1 0 0 1 92b5d7f00
#14758 15/5 15 0 0 1 84199cb87
#14760 6/2 6 0 0 2 ee32e1cb8
#14864 3/3 3 0 0 3 066dd3bd0
#14878 2/1 2 0 0 1 29db3cd2a
#14908 3/2 3 0 0 0 d5cbb44f3
#14921 2/1 2 0 0 0 c1d274de7
#15063 2/1 2 0 0 0 ad35745e8
#15068 2/2 2 0 0 4 8744de9e9
#15071 5/2 5 0 0 0 cf6e0a193
#16610 3/1 3 0 0 0 316a20fc5
#16649 4/1 4 0 0 0 44c917a47, 613bfbd3d
#16755 1/1 1 0 0 0 44c849c7d
#16758 1/1 1 0 0 0 6e9bee640
#16783 1/1 1 0 0 0 854639b31
#16919 1/1 1 0 0 0 2cd4c548e
#17038 1/1 0 0 1 0 —
#17039 1/1 1 0 0 0 edf59e359
#17041 2/2 0 0 2 0 —
#17114 2/2 2 0 0 2 4af758d47
#17147 1/1 1 0 0 0 aaacf1d5c
#17148 1/1 0 0 1 0 —
#17195 1/1 1 0 0 0 d2c1d1980
#17219 1/1 1 0 0 0 706ad0fcc
#19364 2/2 2 0 0 0 ada701220
#19394 5/2 5 0 0 0 0862063ba

Every cited sha matches exactly one object (git rev-parse --disambiguate, count 1 for each of the 95), is a commit, has one parent, and is an ancestor of the base (merge-base --is-ancestor, exit 0 for all 95). The checkout is not shallow (--is-shallow-repository false), and the control leg 13a6cb4ad exits 0 too.

Numbers with more than one anchor, by site:

  • #10243 (40 sites): 266436a7f for the 26 sites that describe the 2026-08-23 ruling it implements (the enablement door joins the manage_metadata write set, with the trigger exclusion), and 02b41232d for the 14 that name the leak itself (「the leak commit 02b4123 measured」). That commit recorded the measurement over HTTP and says it is part of that card.
  • #16649 (4 sites): 613bfbd3d for the first half (the fourteen remaining boot-refusal rows registered) and 44c917a47 for the second (the face refusal widened to every published package, and boot-refusal retired).
  • #12176, #12194, #12195: the stages of one ruled retirement. 311433f6b is stage 1 (the item-name grammar refused at the publish door) and 7986d973f is stage 3 (the compound arities un-mounted). These are the anchors the spec stages gave.

Wordings to check, each true of its commit:

The sites left

No deciding commit, or a literal reader (8 sites):

Held with domains/meta.ts (20 sites), anchors verified for the follow-up: #8726 :116 to e783e163d; #8848 :200, :1398 to 4fc4a3c0b; #8919 :1247 to b5378550e; #10340 :1309 to 26f3588fb; #10503 :14, :1143, :1159, :1250, :1308 to 67ceb9aef; #10888 :1337 to d806081dd; #11006 :103 to cccbe51bf; #12194 :831, :1050, :1173 to 311433f6b; #12195 :819, :827, :1046, :1167, :1960 to 7986d973f. PR #20615's one hunk there is at :1874, disjoint from these lines, but the rule is file-level.

String sites kept as tokens (100). 95 are test titles and test-code strings in 43 files. Five are non-test strings: the route-ledger.ts note fields at :435, :441 and :505, a string at dispatcher-error-vocabulary.ts:349, and the enablement door's refusal text at domains/activation-gate.ts:279, which ends 「(#10243).」 (see Acceptance notes).

Mechanical guard: no code token moves

The check compares the TypeScript parser's leaf tokens (TypeScript 6.0.3, JSDoc nodes excluded, so template literals are read in context) of each touched file at base eb4b17c346 against the working tree at a5cdfd8a46, over all 118 touched .ts files. Controls mutate the head text in memory only, so nothing on disk moved for them.

  • Real run: 301,081 base tokens, 0 files with a token change (exit 0).
  • Comment-insertion control (domains/activation-gate.ts): 0 files changed (exit 0).
  • Code-insertion positive control (a declaration in the same file): DIFFER at token 34 (exit 1).
  • String positive control ((#10243) to (#10244) inside the kept refusal string): DIFFER at token 339 (exit 1).

Line balance: every touched file is +N/−N (508/508), and every line count is equal at base and head. A raw scan of the 119 changed files for control bytes finds none.

Changeset

This change ships bytes, so a patch changeset for @objectstack/runtime is included, in PR #20609's form and level. It says only that the provenance comments were re-anchored.

Measured on the built package: files[] is dist, README.md and CHANGELOG.md. After pnpm --filter @objectstack/runtime build, the rewritten docblocks reach dist: for example e2798fab7 appears 3 times and 68f5eccb1 6 times in dist/index.d.ts, and f19475c0a 4 times in dist/index.js. The positive control, the unchanged sentence 「drags @libsql/client (native bindings included)」 of the same turso-driver-factory.ts docblock, is in dist/index.d.ts, and a negative control phrase appears nowhere. The only dead number left in dist is the kept refusal string's #10243.

Gates (head a5cdfd8a46)

This host has no flock, so os-verify-lock.sh ran in its declared unlocked mode. Its disclosure, verbatim, from each locked run at this head:

os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 5s · declare it in the PR body · pnpm --filter @objectstack/runtime build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 99s (1m39s) · declare it in the PR body · pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 6s · declare it in the PR body · pnpm --filter @objectstack/runtime exec vitest run --project repo --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 8s · declare it in the PR body · pnpm --filter @objectstack/runtime typecheck

The dependency closure and the whole workspace were built first, at the merge head ca6d13d6ab, the same way: turbo run build --filter='@objectstack/runtime...' (30 tasks, exit 0) and turbo run build --filter='./packages/*' --filter='./packages/*/*' (71 tasks, exit 0). a5cdfd8a46 differs from that head only in domains/meta.ts, which went back to base bytes, and @objectstack/runtime was rebuilt at a5cdfd8a46.

  • Tests: vitest run --project local: 288 files, 4,190 tests passed, 1 skipped. --project repo (which holds the touched action-owner-key-single-source.test.ts): 3 files, 727 tests passed. Together they cover every touched test file.
  • Typecheck: pnpm --filter @objectstack/runtime typecheck exits 0. tsc --listFiles counts 82 src files (no tests) under tsconfig.json and all 291 test files under tsconfig.test.json, which check:test-typecheck judges: 27 files, 190 errors, 68 pinned signatures held.
  • Lint: the repo-wide pnpm lint (eslint . --no-inline-config) exits 0 at a5cdfd8a46 (2026-09-29T09:23:06Z to 09:23:36Z). A narrowed run over the 118 touched .ts files through eslint's API agrees: 118 linted, 0 ignored, 0 errors, 0 warnings.
  • Citation judging: node scripts/check-issue-citations.mjs --base origin/main exits 0. The diff-scoped run judged 23 citations across 28 files, and all 23 resolve. These are the live numbers that stay on rewritten lines. It defers *.test.ts, so the added-minus-removed count over the whole diff covers the rest: 0 numbers added.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at a5cdfd8a46 derived 67 families, the same set as at the merge head. All 67 exit 0. --ran reads 「67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN」.
    • At the merge head, check:dual-build-cjs-loads and check:type-check-debt first exited 3 (PREREQUISITE NOT MET) on a partly built workspace. After the whole-workspace build both exited 0, and both exit 0 at the final head.
    • Among them: check:doc-authoring (the sibling prose-id baseline holds, 810 pinned sites, no growth), check:nul-bytes (9,250 files, no raw control bytes), check:route-ledger-census, check:dispatcher-error-vocabulary and check:issue-citations (self-test, 114 cases in 8 batteries).
  • Artifact rosters: 38 of the 41 non-self-test roster rows exit 0 at the merge head. The other three, check-closing-target-claim, check-partof-closing-keyword and check-single-claim-paths, answer 「NOT WIRED」 (exit 2) without a pull request's context, and are run against this PR and reported on the card.

Hypotheses (measured first)

  • H0 holds. The filtered census answers 217 dead sites at eb4b17c346 (29 files, 59 numbers), equal to the card's count at f11b5f20a2: no drift.
  • H1 holds, with the listed exceptions. After the rewrite the filtered census answers 23: the 20 sites held with domains/meta.ts for an open PR, and 3 deliberate ones (a literal reader, a card never landed, an open decision). The supplementary reading adds 5 test-comment sites of the same two kinds.
  • H2 holds, by the token guard. A comment-stripped comparison of every touched file (the parser's leaf tokens, JSDoc excluded) is empty, and its controls fire. The emitted dist is not byte-identical, because the docblocks ship, which is why the changeset is patch.

Acceptance notes

Deviations

  • Three changed lines hold only a slash-joined dead number, beyond the census's sites (see Acceptance notes). Five more are the other half of a rewritten sentence (listed under What changed).
  • Commit trailers are AGENTS.md's model-free pair (Claude-Session plus Co-authored-by: Claude), and the pre-push trailer check passed on every push. The merge commit carries git's default message.

Generated by Claude Code

hotlong and others added 4 commits September 29, 2026 16:33
…mits (first 24 numbers)

Comment and docblock lines under packages/runtime/src that cited a tracker
number answering 404 now cite the commit in this repository's history that
decided what the line describes, reusing the anchors the landed spec stages
gave the same numbers. Comments only; every touched file keeps its line count.

Claude-Session: https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289
Co-authored-by: Claude <noreply@anthropic.com>
…r deciding commits

Every other comment and docblock line under packages/runtime/src, tests
included, that cited a tracker number answering 404 now cites the commit in
this repository's history that decided what the line describes, and says in
its own words what was decided. Eight sites stay as they were: an open or
never-landed card has no deciding commit, and one comment is read literally
by a test. Comments only; every touched file keeps its line count.

Claude-Session: https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289
Co-authored-by: Claude <noreply@anthropic.com>
…pen PR edits it

An open pull request opened after this stage's claim edits
packages/runtime/src/domains/meta.ts, so this stage returns that file to the
bytes it has on main and lists its twenty sites, with their verified anchors,
for a follow-up once that pull request lands.

Claude-Session: https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/runtime, touching 46 documentable anchor(s). ⚠️ 7 changed file(s) yielded no anchor (packages/runtime/src/api-mapping.ts, packages/runtime/src/domains/activation-gate.ts, packages/runtime/src/domains/ui.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))
  • content/docs/api/declarative-endpoints.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))
  • content/docs/api/environment-routing.mdx (via createStandaloneStack (symbol, a top-level function))
  • content/docs/api/plugin-endpoints.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))
  • content/docs/automation/flows.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))
  • content/docs/automation/hook-bodies.mdx (via ScriptContext (symbol, a top-level interface), applyMutationsToInput (symbol, a top-level function))
  • content/docs/data-modeling/drivers.mdx (via createStandaloneStack (symbol, a top-level function))
  • content/docs/deployment/single-project-mode.mdx (via createStandaloneStack (symbol, a top-level function))
  • content/docs/permissions/authorization.mdx (via buildMcpBridge (symbol, a top-level function))
  • content/docs/permissions/system-context.mdx (via handleActionsRequest (symbol, a top-level function), handleAutomationRequest (symbol, a top-level function), handlePackagesRequest (symbol, a top-level function))
  • content/docs/plugins/index.mdx (via createStandaloneStack (symbol, a top-level function))
  • content/docs/protocol/kernel/http-protocol.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))
  • content/docs/protocol/kernel/index.mdx (via resolveExecutionContext (symbol, a top-level function))
  • content/docs/ui/actions.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-3.mdx (via FLOW_INPUT_SCHEMA_INVALID (literal, a string literal in a comment on a changed line), automation.toggle (sdk, the route ledger binds it to POST /automation/:name/toggle, selected by route anchor /:name/toggle), /:name/toggle (route, a path literal in a comment on a changed line), /automation/:name/toggle (route, a path literal in a comment in isFlowAuthoringWrite))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 7 changed file(s) yielded no anchor (packages/runtime/src/api-mapping.ts, packages/runtime/src/domains/activation-gate.ts, packages/runtime/src/domains/ui.ts, …) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 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 — 26 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 e651556e2df1713873ad72f87e673b8e60174e66 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from a43f9595157be6e584254bf22a6d9da57f17d0fb — the merge of head a5cdfd8a46bb2f37c952f73e7a5a51c2c778df27 into base e651556e2df1713873ad72f87e673b8e60174e66, 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 a43f9595157be6e584254bf22a6d9da57f17d0fb && git checkout a43f9595157be6e584254bf22a6d9da57f17d0fb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e651556e2df1713873ad72f87e673b8e60174e66 a5cdfd8a46bb2f37c952f73e7a5a51c2c778df27 && git checkout -B drift-repro e651556e2df1713873ad72f87e673b8e60174e66 && git merge --no-ff a5cdfd8a46bb2f37c952f73e7a5a51c2c778df27

node scripts/docs-audit/affected-docs.mjs --json e651556e2df1713873ad72f87e673b8e60174e66

⚠️ 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 e651556e2df1713873ad72f87e673b8e60174e66 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a5cdfd8a46bb2f37c952f73e7a5a51c2c778df27
Local-runs: none

Inputs read: card #20594 (body and both comments, 5886012266 claim and 5887542107 os-dev-report), ruling 5749154545 on #19123, PR #20624 (body, file list over pulls/20624/files?per_page=100 pages 1 to 3 — 100 + 19 + 0 = 119 entries, and the net diff — 4,739 lines, 119 diff --git headers), and the check-runs on a5cdfd8a46 (two reads, the second at 2026-09-29T09:59:45Z governs). Everything below was measured with gh api REST over the fetched diff and file list; nothing was checked out, built or run.

Sampled, exactly: the whole diff was machine-scanned line by line (every +/- line classified as comment or not; every #N and every 9-to-40-hex token on removed and added lines collected). Read by eye: the changeset, the whole of domains/activation-gate.ts's diff, the whole of api-exposure.test.ts, route-ledger.ts and domains/mcp.ts's diffs, sandbox/script-runner.ts's five hunks, and the removed/added pairs for 27 numbers (#10243, #10629, #14143, #6345, #14758, #6363, #14421, #10293, #16755, #16783, #9934, #19364, #19394, #16758, #11513, #12281, #8796, #10630, #10380, #14422, #14667, #14678, #16649, #13325, #14908, #14683, #6361, #6265, #12194, #12195, #16919, #17039, #8726) across 36 files. For those, 33 anchor commits were fetched (message, file list, patch).

① Derived judgments

Accept sets and public surfaces: none move — right. No route, error code, schema, export, type, identifier, string literal, error message or test assertion changes anywhere in the 118 .ts files. What changes in the shipped artefact is docblock prose in dist/index.d.ts and dist/index.js (tsup dts is on, files[] is dist, README.md, CHANGELOG.md), and that is a published-bytes change, not a surface change — which is what the patch changeset says (see ②).

(1) Comment-only: yes. Of the 508 removed and 508 added lines in the 118 runtime files, 504 pairs start with //, /*, * or */ after whitespace. The other 4 are two trailing // comments on code lines, and the code before the // is byte-identical on each side: action-governance-scope-divergence.test.ts:209 (lookupRegistryAction: () = followed by undefined,) and notifications.hono.integration.test.ts:436 (expect(before.data.unreadCount).toBe(TOTAL);). No code token, string literal, error message, identifier or test assertion moved. domains/activation-gate.ts's whole diff is five hunks (lines 28, 67, 96, 152, 249), each inside a docblock; ACTIVATION_DENY_CODE, the exported functions and the refusal text at :279 (which still ends (#10243).) are untouched. api-exposure.test.ts's three sites are // [#6259] comment lines; the '#6259' literal the test splits on is not on any changed line, and api-exposure.ts itself is not in the file list (its :108 site is kept, as the PR says). domains/mcp.ts:360 (#8722) is not on any changed line (the string 8722 does not occur in the diff). The PR's parser-token guard was not re-run (read-only); the line-level reading agrees with it.

(2) Anchors: 95 distinct shas on added lines, 0 on removed lines — all verified, all supported in the sample. Every one of the 95 exists as a commit (commits/SHA 200), has exactly one parent, and compare/SHA...main answers ahead with behind_by 0 for all 95. The sample of rewritten sites, each read against its commit's subject, message and diff (file:line is the head file):

No sampled anchor is wrong or unsupported. Where one sha stands for two numbers (dd8172ee2, dc7c226b9, 7986d973f, 13a6cb4ad) it is an issue and the PR that closed it, or one commit that two lines describe — right, not a collision.

(3) Live numbers kept, none added — right. 119 distinct numbers stand on removed lines, 23 on added lines. The 96 dropped all answer 404 by REST (96/96). The 23 kept (#3899, #5811, #5906, #6270, #6430, #6504, #6551, #7543, #8328, #8421, #9416, #10025, #10145, #12156, #12509, #13598, #13906, #13909, #14099, #15168, #17273, #17667, #18070) all answer 200, all closed issues, and no hunk drops one of them from its own removed lines. No number appears on an added line that was not on that hunk's removed lines: 0 added. No sha appears on a removed line.

(4) Line counts hold — right. All 118 files under packages/runtime/src have additions equal to deletions in the file list (total 508/508); the only unequal entry is the new .changeset/runtime-provenance-anchors.md (+11/−0). Non-runtime files in the diff: that changeset only.

Check-runs on a5cdfd8a46, read 2026-09-29T09:59:45Z (newest run per name): 34 names, 34 completed, 0 in progress. 31 success — Auto Label, Build Core, Check Changeset, Check Documentation Links, Check PR Size, Dogfood Regression Gate and its 3 shards, Dogfood Verify CLI, Flag docs affected by code changes, Governed Surface Queue Guard, Lint & Repo Gates, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Temporal Conformance (live PG + MySQL), Test Core and its 6 shards, The card this PR closes must claim this branch, Type Check · consumer gates / debt ledger / source gates / workspace, TypeScript Type Check, filter. 3 skipped — Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in). 0 failures. (At the first read, 09:37Z, 32 names with 6 still in progress; none of those turned red.) mergeable_state is clean.

② Semver level

.changeset/runtime-provenance-anchors.md: '@objectstack/runtime': patch, text says comments only, no route, error code, refusal text, type, export or runtime behaviour change. Right (5). The rewritten docblocks ship (dts on, files[] is dist), so skip-changeset would be wrong and a changeset is owed; nothing on the public surface moves, so patch is the ceiling and the floor. No skip-changeset label; Check Changeset is green.

Clause-②: no — right. No accept set moves anywhere in the diff, and ruling 5749154545 itself says «Clause-②: no for the line rewrites (prose only, no accept set moves)».

③ Boundary flags

open_questions: [] — nothing to answer.

(6) Part of #20594, no closing keyword — right. The body carries Part of #20594 and no Fixes/Closes/Resolves anywhere; the card names rest, cli, types and the rest of the lane as later stages, so it must stay open; «Part-of PR must not also close its card» is green. domains/meta.ts at its base blob — right. PR #20615 (claude/issue-20590-credential-positions-closeout, open, draft, created 2026-09-29T08:12:40Z, after the 07:51Z claim) edits packages/runtime/src/domains/meta.ts (+16/−2); meta.ts is absent from #20624's file list; «No other open PR may claim the same single-writer path» is green. The rule is file-level, the 20 sites and their anchors are listed on the PR body for the follow-up, and the card carries them.

Dev deviations, each answered:

  1. meta.ts held — right, above.
  2. Three slash-joined dead numbers (#10629/#10630, #5811/#12281, #8421/#12194) and five sentence-halves rewritten beyond the census — all comment lines inside the card's stated reach (comment prose in packages/runtime/src/**); the live halves #5811 and #8421 are kept on the added lines. Accepted.
  3. CI flake: @objectstack/example-showcase#test fails on mass sys_* Find operation failed + Acquire connection error: operation timed out — hit a merge-queue build and a PR run within 2h on unrelated prose-only diffs #10293 anchored to 92a69d813; feat(runtime): give the two operator run-lifecycle verbs a door (cancel, restore-suspension) #16755 / feat(engine)!: findOne, update and delete declare what they answer, and their hook seams are guarded (#16231) #16783 worded «the change that landed as commit X held that file» — each verified against the commit above. Accepted.
  4. Model-free trailer pair per AGENTS.md instead of the harness reminder — a process note outside the contract; the head's checks are green. Not escalated.
  5. First label-write call exited 3 and was rerun under the fleet token — no effect on the diff. Noted.
  6. Anchor research delegated to read-only subagents and reviewed — the sample above is the independent check; it holds.

Out-of-scope findings, each answered:

Implemented-by: claude/issue-20594-runtime-dead-citations
Reviewed-by: local_1d2a197c-c20e-4e90-9be8-413d4d432289

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 10:06
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit a186aea Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20594-runtime-dead-citations branch September 29, 2026 10:23
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
… to the commits that decided them (objectstack-ai#20632)

Part of objectstack-ai#20594
Clause-②: no

## What changed

This is stage 2 of the `domain:cli` lane of the dead-citation sweep:
`packages/rest/src/**`. Every comment or docblock site in scope that
cited a tracker number answering 404 now cites, in ruling C+D's form C
(comment 5749154545 on objectstack-ai#19123), the commit in this repository's history
that decided what the line describes, and says in its own words what
that commit decided. PR objectstack-ai#20533 is the method and PR objectstack-ai#20624 (stage 1,
`packages/runtime`) the precedent this follows line for line. Later
stages cover `cli`, `types` and the rest of the lane, so this PR says
`Part of` and the card stays open.

That is **457 comment sites on 445 lines in 85 files, covering 74
numbers**: the census's 191 sites, 256 more in test comments (which the
census defers), and 10 sites whose dead number is the second half of a
slash-joined pair the citation grammar does not read (`objectstack-ai#3984/objectstack-ai#6241`,
`objectstack-ai#9901/objectstack-ai#10255` four times, `objectstack-ai#10993/objectstack-ai#11235/objectstack-ai#11292`, `objectstack-ai#11235/objectstack-ai#11242`
twice, `objectstack-ai#10993/objectstack-ai#11242`, `objectstack-ai#7543/objectstack-ai#15071`). Each rewritten line cites one
of **70 distinct commits**.

ADR-0076 D11 is the only ADR that records any of these numbers, and it
records objectstack-ai#8850 only as the extraction it names as landed in `8664a2c99`,
so that commit is the anchor there. No other ADR or ruling-record file
in `docs/adr/` or `scripts/adr-anchors/` records the decision behind any
of these numbers, so every anchor is a commit. The anchors the landed
stages already gave the same numbers are reused where the rest sites
describe the same decision (30 numbers, for example `79c46da90` for
objectstack-ai#9934, `7986d973f` / `311433f6b` for the compound-name retirement,
`6a180e42d` for objectstack-ai#13279 and `cf6e0a193` for objectstack-ai#15071), so each number
carries one anchor across the tree.

Only comments changed. Every touched file keeps its line count (451
lines out, 451 in, over 85 files), so no line citation into these files
moves. Six of the 451 lines held no dead site; each is the other half of
a sentence that had to change:
- `discovery-schema-conformance.test.ts:343` (「(reaffirmed by」 to
「(which commits」, because line 344 now names the two commits that landed
the ruling),
- `package-door-16019-raw-statement-fault-code.test.ts:51` and
`error-response.ts:1485` (a trailing 「PR」 whose number wrapped onto the
next line),
- `error-response-structured-arm-door-parity.test.ts:463` (「That card
added the limb」 to 「That commit」, because line 459's tag now names the
commit),
- `rest-hook-script-fault-envelope.test.ts:331` (「both sides of that
card」 to 「that fix」),
- `rest-server.ts:908` (「(objectstack-ai#14409, landed」 to 「(landed as commit」, the
sha `3ecb7dc1a` already standing on line 909).

**No citation number is added.** Every tracker number on an added line
was already on the line it replaces. No PR number stands on an added
line. One of the 70 shas is on a removed line, and it was there before:
`rest-14078-invalid-date-total-arm.test.ts:19` read 「PR objectstack-ai#14409 (landed
`3ecb7dc1a`)」 and now reads 「Commit 3ecb7dc drove」. No code token
moves (see the guard below).

Three dead comment sites are left on purpose, listed under "The sites
left". One more file: a `patch` changeset for `@objectstack/rest`,
because the rewritten docblocks ship (see Changeset below).

## Census: `packages/rest`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run with the fleet token. Its
surface is comment prose in `packages/**/src/**/*.ts` with string
literals blanked, and it defers `*.test.ts`. The count is its
`allocated-but-absent` findings under `packages/rest/`. Both runs
enumerated the whole board (185 pages), so neither read a truncated
board.

| reading | tree | board | whole-repo `allocated-but-absent` | rest
sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `a186aea996`, run 2026-09-29T10:28:18Z to 10:36:06Z |
enumerated, 185 pages, frontier objectstack-ai#20628, 18,455 numbers | 2,015 | **191**
| 186 | 14 | 51 |
| after | head `93e4d69ba6`, run 11:11:30Z to 11:17:37Z | enumerated,
185 pages, frontier objectstack-ai#20630, 18,457 numbers | 1,764 | **0** | 0 | 0 | 0 |

The before count equals the card's 191 at `f11b5f20a2`. The whole-repo
drop is 251: this diff's 191, plus the 60 of PR objectstack-ai#20626
(`packages/plugins/plugin-sharing`, 63 to 3), which landed on `main` in
between and came in with the merge. No other package moved.

**Supplementary instrument, the whole scope.** The census does not read
test files or strings, and this stage's scope includes test comments. So
a second reading runs the gate's own exported `extractCitations`
(whole-file and comment-prose projections) and `classifyCitation` over
every `.ts` file under `packages/rest/src` (256 files), against the
board enumerated through the gate's own `enumerateBoard`. The lit
controls objectstack-ai#20594, objectstack-ai#19123 and objectstack-ai#20624 answered 200 and are on both boards;
the dead controls objectstack-ai#13214, objectstack-ai#14541 and objectstack-ai#15071 answered 404 and are on
neither.

| reading | tree | board | citations | dead | src comment | test comment
| src string | test string |
|---|---|---|---|---|---|---|---|---|
| before, 10:29Z | `a186aea996` | 185 pages, frontier objectstack-ai#20628 | 4,620 |
**577** | 191 | 259 | 1 | 126 |
| after, 11:21Z | `93e4d69ba6` | 185 pages, frontier objectstack-ai#20631 | 4,174 |
**130** | 0 | 3 | 1 | 126 |

Its src-comment column equals the census's 191 and 0, which is the
control on the second instrument, and a site-by-site comparison of the
two before-readings is identical. Resolving comment citations move by
one (1,364 to 1,365 in src): `(objectstack-ai#10993/objectstack-ai#11235/objectstack-ai#11292)` became `(objectstack-ai#10993,
commit 376c70f, objectstack-ai#11292)`, so the grammar now reads the live `objectstack-ai#11292`
that the slash hid. The drop is 447 grammar-read sites; the other 10
rewritten sites are the slash-joined ones the grammar never read.

Separately, every one of the 77 numbers was probed on its web endpoint:
76 answer 404 (deleted) and one, #14026, answers 302 to
objectstack-ai/objectui#10102 (transferred), which is why it is left
(see below).

## Per-number table

Sites and files are the dead comment sites in scope at the base, tests
and slash-joined halves included. `left` is a site with no deciding
commit (see below). `strings kept` counts string-literal sites, which
are tokens and stay as they were. Every anchor was read in its message
or its diff, not only in its subject: it is the commit that made the
change the line describes, and its own message or diff names the number
it replaces or adds the citation the line carries.

| number | comment sites / files | rewritten | left | strings kept |
anchor |
|---|---|---|---|---|---|
| `objectstack-ai#6037` | 5/3 | 5 | 0 | 0 | `18189983d` |
| `objectstack-ai#6122` | 2/2 | 2 | 0 | 0 | `64cd01082` |
| `objectstack-ai#6206` | 1/1 | 1 | 0 | 0 | `8e13ca876` |
| `objectstack-ai#6216` | 6/2 | 6 | 0 | 2 | `f586f1a89` |
| `objectstack-ai#6241` | 10/3 (1 slash-joined) | 10 | 0 | 1 | `83a3b1f2e` |
| `objectstack-ai#6259` | 2/1 | 2 | 0 | 0 | `6968885ef` |
| `objectstack-ai#6303` | 1/1 | 1 | 0 | 0 | `465c5fc14` |
| `objectstack-ai#6306` | 9/5 | 9 | 0 | 3 | `fec784863` |
| `objectstack-ai#6307` | 4/2 | 4 | 0 | 0 | `293476148` |
| `objectstack-ai#6349` | 4/2 | 4 | 0 | 4 | `2443bb4c4` |
| `objectstack-ai#6474` | 1/1 | 1 | 0 | 0 | `18189983d` |
| `objectstack-ai#6535` | 3/2 | 3 | 0 | 0 | `a92b1793c` |
| `objectstack-ai#6640` | 1/1 | 1 | 0 | 1 | `2ab1257c9` |
| `objectstack-ai#6704` | 5/1 | 5 | 0 | 1 | `c3f491626` |
| `objectstack-ai#8641` | 1/1 | 0 | 1 | 0 | — |
| `objectstack-ai#8850` | 3/3 | 3 | 0 | 0 | `8664a2c99` |
| `objectstack-ai#8885` | 6/3 | 6 | 0 | 3 | `30b1c636a` |
| `objectstack-ai#8919` | 7/3 | 7 | 0 | 7 | `b5378550e` |
| `objectstack-ai#9741` | 12/1 | 12 | 0 | 0 | `2a29caa53` |
| `objectstack-ai#9805` | 1/1 | 1 | 0 | 0 | `45862a53d` |
| `objectstack-ai#9934` | 19/10 | 19 | 0 | 4 | `79c46da90` |
| `objectstack-ai#9967` | 2/2 | 2 | 0 | 4 | `8f266f1cd` |
| `objectstack-ai#10063` | 2/2 | 2 | 0 | 1 | `9e04c3e35` |
| `objectstack-ai#10178` | 1/1 | 1 | 0 | 0 | `38cf397ea` |
| `objectstack-ai#10179` | 0/0 | 0 | 0 | 1 |  |
| `objectstack-ai#10255` | 18/4 (4 slash-joined) | 18 | 0 | 2 | `6ce58a735` |
| `objectstack-ai#10340` | 13/3 | 13 | 0 | 2 | `26f3588fb` |
| `objectstack-ai#10345` | 13/6 | 13 | 0 | 6 | `cad8b42f0` |
| `objectstack-ai#10350` | 1/1 | 1 | 0 | 0 | `490879ad0` |
| `objectstack-ai#10485` | 2/1 | 2 | 0 | 1 | `35ad101bc` |
| `objectstack-ai#10537` | 9/3 | 9 | 0 | 1 | `e634ecf6a` |
| `objectstack-ai#10888` | 2/2 | 2 | 0 | 0 | `d806081dd` |
| `objectstack-ai#11006` | 3/1 | 3 | 0 | 0 | `cccbe51bf` |
| `objectstack-ai#11130` | 1/1 | 1 | 0 | 0 | `851909530` |
| `objectstack-ai#11235` | 4/2 (1 slash-joined) | 4 | 0 | 0 | `376c70f98` |
| `objectstack-ai#11242` | 3/2 (3 slash-joined) | 3 | 0 | 0 | `98ea3443f` |
| `objectstack-ai#12144` | 1/1 | 1 | 0 | 0 | `3a04b0125` |
| `objectstack-ai#12176` | 11/7 | 11 | 0 | 2 | `7986d973f` |
| `objectstack-ai#12194` | 15/5 | 15 | 0 | 4 | `311433f6b` |
| `objectstack-ai#12195` | 35/16 | 35 | 0 | 7 | `7986d973f` |
| `objectstack-ai#13182` | 2/2 | 2 | 0 | 0 | `5b3ff63cc` |
| `objectstack-ai#13197` | 1/1 | 1 | 0 | 0 | `56c093c4d` |
| `objectstack-ai#13213` | 2/1 | 2 | 0 | 0 | `4801296e7` |
| `objectstack-ai#13214` | 18/6 | 18 | 0 | 14 | `cc837dbfe`, `889ec5b42`, `3d10755f0`
|
| `objectstack-ai#13244` | 5/2 | 5 | 0 | 1 | `889ec5b42` |
| `objectstack-ai#13255` | 4/1 | 4 | 0 | 6 | `43028a8f8` |
| `objectstack-ai#13258` | 1/1 | 1 | 0 | 0 | `3d10755f0` |
| `objectstack-ai#13279` | 23/5 | 23 | 0 | 5 | `6a180e42d` |
| `objectstack-ai#13280` | 13/4 | 13 | 0 | 2 | `add6a1b1c` |
| `objectstack-ai#13282` | 1/1 | 1 | 0 | 0 | `43028a8f8` |
| `objectstack-ai#13377` | 3/2 | 3 | 0 | 0 | `e10cf3444` |
| `objectstack-ai#13378` | 2/1 | 2 | 0 | 0 | `82faea03f` |
| `objectstack-ai#13454` | 1/1 | 1 | 0 | 0 | `7ad57e17a` |
| `#14026` | 1/1 | 0 | 1 | 0 | — |
| `objectstack-ai#14365` | 1/1 | 0 | 1 | 0 | — |
| `objectstack-ai#14366` | 14/4 | 14 | 0 | 2 | `53cbad9f7` |
| `objectstack-ai#14369` | 3/2 | 3 | 0 | 0 | `a3d5724c8`, `53cbad9f7` |
| `objectstack-ai#14389` | 7/3 | 7 | 0 | 7 | `10220a7bf` |
| `objectstack-ai#14390` | 1/1 | 1 | 0 | 0 | `9d7f7259f` |
| `objectstack-ai#14409` | 2/2 | 2 | 0 | 0 | `3ecb7dc1a` |
| `objectstack-ai#14541` | 27/4 | 27 | 0 | 5 | `6d178a408` |
| `objectstack-ai#14613` | 2/2 | 2 | 0 | 0 | `81208086a` |
| `objectstack-ai#14677` | 1/1 | 1 | 0 | 0 | `a4e4d2d78` |
| `objectstack-ai#14683` | 8/2 | 8 | 0 | 0 | `96326040f` |
| `objectstack-ai#14691` | 15/2 | 15 | 0 | 2 | `b3a63d32c` |
| `objectstack-ai#14704` | 9/3 | 9 | 0 | 2 | `1c7adc73d` |
| `objectstack-ai#14723` | 7/4 | 7 | 0 | 4 | `65846bc46` |
| `objectstack-ai#14725` | 3/3 | 3 | 0 | 2 | `f5cc78b63` |
| `objectstack-ai#14849` | 3/1 | 3 | 0 | 0 | `226e72443` |
| `objectstack-ai#14907` | 1/1 | 1 | 0 | 0 | `e1d4f9e3f` |
| `objectstack-ai#14908` | 1/1 | 1 | 0 | 0 | `d5cbb44f3` |
| `objectstack-ai#15021` | 2/1 | 2 | 0 | 8 | `cc238db8b` |
| `objectstack-ai#15034` | 6/2 | 6 | 0 | 0 | `abf9101f1` |
| `objectstack-ai#15065` | 1/1 | 1 | 0 | 0 | `1c7adc73d` |
| `objectstack-ai#15071` | 23/4 (1 slash-joined) | 23 | 0 | 3 | `cf6e0a193` |
| `objectstack-ai#16650` | 1/1 | 1 | 0 | 0 | `001a83b04` |
| `objectstack-ai#17058` | 3/1 | 3 | 0 | 4 | `94c930248` |
| `objectstack-ai#18546` | 3/2 | 3 | 0 | 3 | `58f60e37e` |
| **total** | **460** | **457** | **3** | **127** | **70 distinct
commits** |

Every cited sha matches exactly one object (`git rev-parse
--disambiguate`, count 1 for each of the 70), is a commit, has one
parent, and is an ancestor of the base (`merge-base --is-ancestor`, exit
0 for all 70). The checkout is not shallow (`--is-shallow-repository`
false); the control leg `13a6cb4ad` exits 0 and the negative control
(this branch's first WIP commit, not on `main`) exits 1. Several numbers
are the PR number of their own anchor commit (objectstack-ai#6122, objectstack-ai#6303, objectstack-ai#6474,
objectstack-ai#11242, objectstack-ai#13213, objectstack-ai#13244, objectstack-ai#13258, objectstack-ai#13282, objectstack-ai#14409, objectstack-ai#14677, objectstack-ai#14908, objectstack-ai#15065,
objectstack-ai#16650), so the sha is the same object the number named.

**Numbers with more than one anchor, by site:**
- `objectstack-ai#13214` (18 sites) was one card with three commits. `cc837dbfe` (the
ownership gate, the 2026-08-30 ruling) for the 11 sites that describe
the gate; `889ec5b42` for the 5 in
`ui-view-route-identity.measurement.test.ts`, the identity measurement
it created; `3d10755f0` for the tenancy file's header, the measurement
it created; and `rest-server.ts:2247`, 「Driven and reported on objectstack-ai#13214
(PRs objectstack-ai#13244, objectstack-ai#13258)」, now reads 「Measured in commits 889ec5b
(identity) and 3d10755 (tenancy)」: those PRs are exactly those two
commits.
- `objectstack-ai#14369` (3 sites): `a3d5724c8` (the liveness census it recorded) for
`rest-server.ts:1172` and `rest-sub-config-parse-not-cast.test.ts:48`.
`rest-server.ts:4092` said the zero read sites of `api.documentation` /
`api.responseFormat` came from 「the objectstack-ai#14369 census」, but `a3d5724c8`
explicitly left `api` out of that census; the zero was measured by
`53cbad9f7` (its changeset: no other read site for either key), which is
the anchor there.
- `objectstack-ai#11235` / `objectstack-ai#11242` / `objectstack-ai#10993`: `376c70f98` derives the discovery
`version` in metadata-protocol (objectstack-ai#11235), and `98ea3443f` is objectstack-ai#11242's own
squash, which landed the objectstack-ai#10993 ruling on `/health` and the dispatcher's
`/discovery`. So 「the objectstack-ai#10993 ruling … reaffirmed by objectstack-ai#11235/objectstack-ai#11242」 now
reads 「the objectstack-ai#10993 ruling, landed by commits 98ea344 and 376c70f」
(`rest-server.ts:4528`, `discovery-schema-conformance.test.ts:343-344`).
`objectstack-ai#10993`, `objectstack-ai#11292` and `objectstack-ai#11297` answer 200 and stay.
- `objectstack-ai#6037` / `objectstack-ai#6474`: one commit, `18189983d` (objectstack-ai#6474 is its PR number),
so 「(objectstack-ai#6037 / PR objectstack-ai#6474)」 became 「(commit 1818998)」.

**Wordings to check, each true of its commit:**
- A commit does not rule. Where a line said a number ruled, it now says
what the commit did with the ruling: 「the ruling commit 79c46da landed
says it does」, 「the ruling commit cf6e0a1 implemented fences it」, 「the
ruling commit 10220a7 implemented」, 「the 2026-08-20 ruling, landed as
commit 6ce58a7」, 「recorded in commit 6ce58a7's message (option A)」
(its message reads 「Ruled on objectstack-ai#10255 (2026-08-20, option A)」), and
「question was ruled on 2026-08-20 and landed as commit 6ce58a7」 where
the line said 「filed as objectstack-ai#10255」.
- `objectstack-ai#14541`'s contract review: 「the objectstack-ai#14541 contract review (condition N)」
now reads 「the contract review of commit 6d178a4 (condition N)」; that
commit's message lists the conditions it carries. 「objectstack-ai#14541's §4」 and
「objectstack-ai#14541 §5」 in
`error-response-generic-passthrough-object-parity.test.ts` are sections
of `error-response-structured-arm-door-parity.test.ts` (the file
`6d178a408` created), so they now name that file. 「measured on the
objectstack-ai#14541 branch」 reads 「on the branch that landed as commit 6d178a4」.
- A line that named a DEFECT by its number now says so: 「Before commit
9e04c3e the draft→active promotion door could not…」, 「Before commit
26f3588 the `/meta` doors decided ORGANIZATION SCOPE from the RAW
url」, 「the defect commit 2443bb4 fixed」 and 「would be the defect
commit 26f3588 fixed」.
- `objectstack-ai#13255`: 「As written for objectstack-ai#13255 this file repaired nothing」 reads 「As
first written (commit 43028a8)」, the commit that created the file and
answered the measurement; 「CONTEXT-LOST family (objectstack-ai#13255), still unruled」
reads 「first measured by commit 43028a8」 (the ruling on that family
never landed, which the line still says).
- `objectstack-ai#13214` in the identity file: 「the half objectstack-ai#13214 marks UNMEASURED」
reads 「the half left UNMEASURED until commit 889ec5b」, and 「objectstack-ai#13214
asks for an INDEPENDENT reproduction」 reads 「commit 889ec5b is an
INDEPENDENT reproduction」.
- 「the objectstack-ai#8885 sweep」 reads 「the sweep behind commit 30b1c63」, the
commit that registered the 9 codes the sweep found; 「objectstack-ai#14849 predicted」
reads 「The card behind commit 226e724 predicted」; 「the hazard objectstack-ai#13377
names」 reads 「the hazard commit e10cf34 was written to remove」; 「The
concrete harm objectstack-ai#6704 names」 reads 「removed」.
- Quoted ruling: `error-response-sandbox-arm-message.test.ts:340` sits
inside a verbatim ruling quote, so the commit stands in an editorial
bracket (「not from [commit 1c7adc7]'s list」), as PR objectstack-ai#20624 did.
- Two markdown tables in comments
(`meta-state-route-engine-outage.test.ts:76`,
`objectql-slot-consumer-census.test.ts:43`): the rewritten cell is wider
than its column, and its padding is reduced rather than widening the
four sibling rows.

## The sites left

**No deciding commit (3 sites, all in test files, so the census does not
see them):**
- `meta-object-owd-gate.test.ts:516` (objectstack-ai#8641): 「whether it should stay is
objectstack-ai#8641's question」, an open decision. The commit that added the citation
calls it a pointer to the open decision card, and no commit decides it.
- `rest-sub-config-parse-not-cast.test.ts:321` (objectstack-ai#14365): the
`z.partialRecord` question 「deferred to objectstack-ai#14365」 was never taken (`git
log -S partialRecord`); `b3a63d32c` made it moot by retiring the record,
which the other half of the same line now cites.
- `import-integration.test.ts:1043` (#14026): not deleted, TRANSFERRED.
The web endpoint answers 302 to objectstack-ai/objectui#10102, the REST
read follows the redirect, and the board enumeration does not list it,
so the census and the supplementary reading both class it
`allocated-but-absent`. The line says how an issue was raised; no commit
decides that, so form C has nothing to cite.

**String sites kept as tokens (127).** 126 are test titles and test-code
strings in 41 files. One is a non-test string: the `note` field of the
REST route ledger's `GET /api/v1/meta/object/:name/state/:field` row at
`rest-route-ledger.ts:290`, which ends 「(objectstack-ai#10179)」 (see Acceptance
notes).

## Mechanical guard: no code token moves

The check compares the TypeScript parser's leaf tokens (TypeScript
6.0.3, JSDoc nodes excluded, comments being trivia) of each touched file
at base `a186aea996` against the working tree at `93e4d69ba6`, over all
85 touched `.ts` files. Controls mutate the head text in memory only, so
nothing on disk moved for them.

- Real run: 272,653 base tokens, **0 files with a token change** (exit
0).
- Comment-insertion control (`error-response.ts`): 0 files changed (exit
0).
- Code-insertion positive control (a declaration in the same file):
DIFFER at token 0 (exit 1).
- String positive control (the first string literal past offset 2000 of
the same file, one character added inside it): DIFFER at token 26 (exit
1).

Line balance: every touched file is +N/−N (451/451), and every line
count is equal at base and head. A raw scan of the 86 changed files for
control bytes finds none (its positive control on a scratch file with a
U+0001 byte matches).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/rest`
is included, in PR objectstack-ai#20624's form and level. It says only that the
provenance comments were re-anchored.

Measured on the built package: `files[]` is `dist`, `README.md` and
`CHANGELOG.md`. After `pnpm --filter @objectstack/rest build`, the
rewritten docblocks reach `dist`: for example `53cbad9f7` appears 4
times in `dist/index.d.ts`, and `26f3588fb` 8 times and `b3a63d32c` 5
times in `dist/index.js`. The positive control, the unchanged sentence
「It was VALIDATE-ONLY from objectstack-ai#11637」 of the same `rest-server.ts` docblock
whose first line now reads 「[commit 53cbad9] The parsed output is
CONSUMED」, is in `dist/index.d.ts` beside it; a negative control phrase
appears nowhere.

## Gates (head `93e4d69ba6`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each locked run at this
head:

```text
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 47s · declare it in the PR body · pnpm --filter '@objectstack/rest...' build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 102s (1m42s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=4
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 76s (1m16s) · declare it in the PR body · pnpm --filter @objectstack/rest exec vitest run --project local --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/rest exec vitest run --project repo --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 9s · declare it in the PR body · pnpm --filter @objectstack/rest typecheck
```

The branch merged `origin/main` once (`93e4d69ba6`, merging
`542670da6d`) before these runs, as the dispatch orders; `origin/main`
has not moved since (read at 11:19Z). The merge brought PR objectstack-ai#20626 and PR
objectstack-ai#20587 and touched none of this diff's files. The dependency closure was
built first (`pnpm --filter '@objectstack/rest...' build`, 26 packages),
then the whole workspace (`turbo run build --filter='./packages/*'
--filter='./packages/*/*'`, 71 tasks, 71 successful).

- **Tests:** `vitest run --project local`: 227 files, 4,382 tests
passed, 50 skipped. `--project repo` (which holds the touched
`meta-state-route-doc-spelling.test.ts`): 1 file, 8 tests passed.
Together they are all 228 test files of the package, so every touched
test file ran.
- **Typecheck:** `pnpm --filter @objectstack/rest typecheck` exits 0.
`tsc --listFiles` counts 28 `src` files (no tests) under `tsconfig.json`
and all 228 test files under `tsconfig.test.json`, which
`check:test-typecheck` judges: 0 files, 0 errors, 0 pinned signatures in
the ledger.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at `93e4d69ba6` (2026-09-29T11:19:30Z to 11:20:00Z). Not
narrowed.
- **Citation judging:** `node scripts/check-issue-citations.mjs --base
origin/main` exits 0: 19 citations judged across 14 files (18 resolve, 1
resolves as a pull request). These are the live numbers that stay on
rewritten lines. It defers `*.test.ts`, so the added-minus-removed count
over the whole diff covers the rest: 0 numbers added.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `93e4d69ba6` derived 68
families. All 68 exit 0, and `--ran` over a record carrying each exit
code reads 「68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived
zero).
- `check:dual-build-cjs-loads` and `check:type-check-debt` first exited
3 (PREREQUISITE NOT MET, nothing measured) on the closure-only build;
after the whole-workspace build both exited 0.
- Among them: `check:doc-authoring`, `check:nul-bytes`,
`check:rest-log-declared`, `check:route-envelope`,
`check:system-context-census` (106 elevation read sites, the page's 102
symbols held) and `check:issue-citations` (self-test).
- **Artifact rosters:** 33 of the 36 non-self-test roster rows exit 0 at
`93e4d69ba6`, `check-changeset-fixed` (the one whose roster sits under
`.changeset/`) and `check:route-ledger-census` among them. The other
three, `check-closing-target-claim`, `check-partof-closing-keyword` and
`check-single-claim-paths`, answer 「NOT WIRED」 (exit 2) without a pull
request's context; they are run against this PR once it exists and
reported on the card.

## Hypotheses (measured first)

- **H0 holds.** The filtered census answers 191 dead sites at
`a186aea996` (186 lines, 14 files, 51 numbers), equal to the card's
count at `f11b5f20a2`: no net drift, although PR objectstack-ai#20601 (merged as
`eb4b17c346`, before this base) touches four files in `packages/rest`.
- **H1 holds.** After the rewrite the filtered census answers 0. The
supplementary reading leaves 3 test-comment sites, the three listed
above: an open decision, an untaken option and a transferred issue, none
with a deciding commit. No site was held for an open PR: the claim's
read and this stage's two reads of the open PRs' file lists (10:27:35Z,
7 open PRs; 11:30:34Z, 8 open PRs) found none touching `packages/rest`.
- **H2 holds, by the token guard.** A comment-stripped comparison of
every touched file (the parser's leaf tokens, JSDoc excluded) is empty,
and its code and string controls fire. The emitted `dist` is not
byte-identical, because the docblocks ship, which is why the changeset
is `patch`.

## Acceptance notes

- **Form D, not touched here.** 127 dead numbers stand inside string
literals: 126 in test titles and test-code strings, and one in the
`note` of the REST route ledger's legal-next-state row
(`rest-route-ledger.ts:290`, 「(objectstack-ai#10179)」), which is ledger data, not an
author-shown refusal. Ruling D (no number, the lesson in words) is a
string change outside this comment-only scope; the card already carries
a form-D stage for the lane.
- **A transferred issue among the 404s.** #14026 answers 302 to
objectstack-ai/objectui#10102 on its web endpoint. The census classes it
`allocated-but-absent` (deleted and transferred are only told apart
under `--probe-cause`), and `scripts/check-issue-citations.mjs`'s header
says the `transferred` arm has no positive specimen on this tree; this
is one. Noted, not filed.
- **The grammar does not read a slash-joined number.** `CITATION_RE`
refuses a `#` preceded by `/`, so the second number of `#A/#B` is never
judged. In `packages/rest/src` six such dead numbers stood at 10 comment
sites, all rewritten here; one more, `objectstack-ai#14389` in `objectstack-ai#14095/objectstack-ai#14389`, stands
inside a string
(`error-response-structured-arm-door-parity.test.ts:187`) and is kept.
The same shape PR objectstack-ai#20624 and PR objectstack-ai#20612 reported. Noted, not filed.
- **Outside the scope and the census surface.**
`packages/rest/vitest.config.ts:21` cites objectstack-ai#17853, which answers 404;
`packages/rest/test-typecheck-debt.json`, written by
`gen:test-typecheck-debt`, carries objectstack-ai#13470, objectstack-ai#13454, objectstack-ai#13377 and objectstack-ai#13378 in
its prose, all 404. Neither is under `src/**`. The other numbers in
`vitest.config.ts`, `tsconfig.json` and `tsconfig.test.json` answer 200.
- **Two comments stale on their own, not touched.** The anchor research
found `rest-server.ts`'s `api` docblock near `:1115` and the 「zero read
sites」 sentence at `:4092` both overtaken by `80153f5a4`, whose own
acceptance notes record it. This PR re-anchors their citations and
leaves their claims alone.
- **An attribution corrected by the anchor.** `rest-server.ts:4092`
credited its zero-read-site count to 「the objectstack-ai#14369 census」, which
(`a3d5724c8`) excluded `api`; it now cites `53cbad9f7`, the commit that
measured it.
- **Base.** One merge of `origin/main` (`93e4d69ba6`) before the `--base
origin/main` run, as the dispatch orders.

## Deviations

- Ten sites beyond the census's read grammar carry a slash-joined dead
number and are rewritten; six more lines are the other half of a
rewritten sentence (listed under What changed).
- The whole-workspace build ran with `--concurrency=4`, not 2, to stay
inside the ten-minute foreground cap on this host; it took 1m42s.
- Anchor research for 33 of the 77 numbers ran in three read-only
research subagents; every proposal was verified here against the
commit's message or diff, and the wording of each changed line was
reviewed and corrected by hand in a second pass.
- Commit trailers are AGENTS.md's model-free pair (`Claude-Session` plus
`Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The merge commit carries git's default message.

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…o the commits that decided them, and the source-hashes header at its producer (objectstack-ai#20656)

Part of objectstack-ai#20594
Clause-②: no

## What changed

This is stage 3 of the `domain:cli` lane of the dead-citation sweep:
`packages/cli/src/**`, plus the generated-header producer the
`domain:services` pointer on the card hands this lane. Every comment or
docblock site in scope that cited a tracker number answering 404 now
cites, in ruling C+D's form C (comment 5749154545 on objectstack-ai#19123), the commit
in this repository's history that decided what the line describes, and
says in its own words what that commit decided. PR objectstack-ai#20533 is the method;
PR objectstack-ai#20624 (stage 1, `packages/runtime`) and PR objectstack-ai#20632 (stage 2,
`packages/rest`) are the precedents this follows. Later stages cover
`types` and the rest of the lane, so this PR says `Part of` and the card
stays open.

That is **313 comment sites on 304 lines in 64 files, covering 63
numbers**: the census's 170 rewritable sites (of its 174), 142 more in
test comments (which the census defers), and one site whose dead number
is the second half of a slash-joined pair the citation grammar does not
read (`serve.ts:1173`, `objectstack-ai#10943/objectstack-ai#11157`). Each rewritten line cites one
of **62 distinct commits**, except the six `objectstack-ai#15041` sites, which cite
ADR-0104's 2026-09-05 addendum: that ADR records the maintainer ruling
the lines describe, and the ruling allows the ADR to be cited instead of
a commit.

Only comments changed in `packages/cli/src`, apart from the two string
literals this stage declares (next section). Every touched file keeps
its line count (319 lines out, 319 in, over 65 files), so no line
citation into these files moves. Thirteen of the 319 lines held no dead
site; each is the other half of a sentence that had to change:
`create.ts:326`, `doctor-organizations-message-spelling.test.ts:11`,
`environments.test.ts:162`, `generate.ts:153`,
`serve-cluster-host-resolution.test.ts:816`,
`serve-host-fallback-base.test.ts:5`, `validate.ts:336`,
`validate.ts:813`, `validate.ts:815`, `hook-body-lowering.test.ts:10`,
`format.ts:1132`, `format.ts:1435` and `i18n-extract.ts:34` (mostly
「that card」 to 「that commit」 once the antecedent became a commit).

**No citation number is added.** Every tracker number on an added line
was already on the line it replaces. One PR number stands on an added
line, and it was there before:
`doctor-organizations-message-spelling.test.ts:9` read 「PR objectstack-ai#12463
(objectstack-ai#12151) single-sourced」 and now reads 「Commit 27b6902 (PR objectstack-ai#12463)
single-sourced」, keeping the live PR as a convenience link beside the
commit, as the ruling allows (objectstack-ai#12463 is that commit's own PR). No code
token moves (see the guard below).

Four dead comment sites are left on purpose, listed under "The sites
left". Two more files outside `packages/cli`: a `patch` changeset for
`@objectstack/cli`, and the shrink-only `check:doc-authoring` prose-id
ledger (see Deviations).

## The source-hashes producer and its 27 generated companions

The claim declares these as the only strings this stage moves, and the
regeneration of the files they write.

- **The producer.** `packages/cli/src/utils/i18n-extract.ts:2294` is the
string literal `renderSourceHashModule` writes as line 8 of every
`LOCALE.source-hashes.generated.ts`. It read 「bundles (objectstack-ai#11671,
maintainer ruling objectstack-ai#12069 Option A, extending objectstack-ai#8765 Option B).」 and now
reads 「bundles (commit 09b4f4e, maintainer ruling objectstack-ai#12069 Option A,
extending objectstack-ai#8765 Option B).」. `09b4f4e4e` is the commit that extended the
objectstack-ai#8765 Option B source-hash mechanism to the generated bundles per
maintainer ruling objectstack-ai#12069 Option A; its message says exactly that, and it
is the anchor stages 1 and 2 of objectstack-ai#20596 gave `objectstack-ai#11671`. objectstack-ai#12069 and objectstack-ai#8765
answer 200 (REST and web) and stay. The comment at `:249` beside
`previousSourceHashes` cites the same commit.
- **The flag's help text**
(`packages/cli/src/commands/i18n/extract.ts:227`, author-shown CLI
help), in form D: the lesson in words, no number. It read 「the
provenance companion that lets a stale fill be told from a translation
(objectstack-ai#11671).」 and now reads 「the provenance companion that records which
source revision each generated leaf is still a copy of, so a stale fill
can be told from a translation.」. The rest of the description is
unchanged.
- **The regeneration.** `node scripts/check-i18n-bundles.mjs --write`,
which runs each package's documented `os i18n extract` command from its
`i18n-extract.config.ts` (every one of the nine carries the
source-hashes flag), on a CLI built from `47241dd80e`. Before it, the
bundle check reported the nine packages DRIFTED, 3 bundles each, against
the new producer. After it:
- exactly 27 files changed, `+27 −27`: 3 locales in
`packages/platform-objects/src/apps/translations`,
`plugins/plugin-{approvals,audit,security,sharing,webhooks}` and
`services/service-{messaging,realtime,storage}`;
- every hunk is `@@ -8 +8 @@`, and the one removed and one added line
are the same in all 27 files;
- each file with line 8 deleted hashes identically at base and head (27
of 27), so every hash entry is byte-identical;
- `git status` shows nothing else in the nine packages, tracked or
untracked;
- `node scripts/check-i18n-bundles.mjs` then reads all nine packages in
sync (7 bundles each), and `check:i18n-stale-fill` serves 27 of 27
companions with 0 stale fills.
- **H3 holds.** `git grep -n "objectstack-ai#11671" -- '*.source-hashes.generated.ts'`
answers 0 hits at head; the same grep at `04b202e5cb` answers 27 (the
positive control).
- **Not published by the nine.** The header is a comment their bundlers
strip: after the build, none of the nine packages' `dist` holds
`09b4f4e4e` or the header's own phrase 「Each entry is the digest of the
SOURCE REVISION」, while the export name `…GeneratedSourceHashes` (the
positive control) is in each `dist`. So the regeneration changes no
published byte of those packages, and no changeset is owed for them. The
other lanes' stages can keep leaving their generated copies alone, as
the pointer asked.

## Held files

`packages/cli/src/utils/sdui-manifest.ts` and `sdui-manifest.test.ts`
stay at their base blobs (`41c4549ffe` and `3a242b54e0`, equal at base
and head), because PR objectstack-ai#20589 edits them. They carry four citations
(`objectstack-ai#4409` once, `objectstack-ai#20113` three times), and all four answer 200, so no
dead site is held and there is no anchor to list for a follow-up.

## Census: `packages/cli`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run with the fleet token. Its
surface is comment prose in `packages/**/src/**/*.ts` with string
literals blanked, and it defers `*.test.ts`. The count is its
`allocated-but-absent` findings under `packages/cli/`. Every run
enumerated the whole board (185 pages), so none read a truncated board.

| reading | tree | board | whole-repo `allocated-but-absent` | cli sites
| lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `04b202e5cb`, run 2026-09-29T12:35:39Z to 12:43:03Z |
enumerated, 185 pages, frontier objectstack-ai#20642, 18,469 numbers | 1,707 | **174**
| 167 | 30 | 50 |
| after | head `6bb4d3b531`, run 13:46:09Z to 13:54:25Z | enumerated,
185 pages, frontier objectstack-ai#20652, 18,479 numbers | 1,510 | **4** | 4 | 3 | 2 |

The before count equals the card's 174 at `f11b5f20a2`. The 4 left are
the deliberate sites below. The whole-repo drop is 197: this diff's 170
cli sites plus the 27 generated headers (3 in each of the nine packages;
nothing else moved in any of them). The two merges of `origin/main`
moved no count. An earlier after-run at `d1e09a7eed` (13:19:18Z to
13:29:41Z, frontier objectstack-ai#20649) read the same 4 and 1,510; `packages/cli`
and the 27 companions are byte-identical between the two heads. One more
attempt at `6bb4d3b531` (13:35:15Z) exited 3, PREREQUISITE NOT MET, on a
malformed board page, and measured nothing; the run in the table is its
retry.

**Supplementary instrument, the whole scope.** The census does not read
test files or strings, and this stage's scope includes test comments. So
a second reading runs the gate's own exported `extractCitations`
(whole-file and comment-prose projections) and `classifyCitation` over
every `.ts`/`.tsx` file under `packages/cli/src` (298 files), against a
board enumerated through the gate's own `enumerateBoard`. The lit
controls objectstack-ai#20594, objectstack-ai#19123 and objectstack-ai#20632 answered 200 and are on both boards;
the dead controls objectstack-ai#11671, objectstack-ai#10514 and objectstack-ai#14828 answered 404 and are on
neither.

| reading | tree | board | citations | dead | src comment | test comment
| src string | test string |
|---|---|---|---|---|---|---|---|---|
| before, 12:43Z | `04b202e5cb` | 185 pages, frontier objectstack-ai#20642 | 3,029 |
**368** | 174 | 142 | 4 | 48 |
| after, 13:39Z | `6bb4d3b531` | 185 pages, frontier objectstack-ai#20650 | 2,715 |
**54** | 4 | 0 | 2 | 48 |

Its src-comment column equals the census's 174 and 4, which is the
control on the second instrument. The resolving citations (1,468 and 755
in comments, 50 and 53 as pull requests, 32 cross-repo) are the same in
both readings, so no live citation was lost; the drop of 314 is exactly
the dead sites removed (312 grammar-read comment sites and the two
`objectstack-ai#11671` strings). The one slash-joined dead number the grammar never
reads (`objectstack-ai#11157` in `objectstack-ai#10943/objectstack-ai#11157`) is gone too: a separate scan for
dead `#N` tokens the grammar skips answers 1 before and 0 after.

## Per-number table

Sites and files are the dead comment sites in scope at the base, test
sites counted in brackets. `left` is a site with no deciding commit (see
below). `strings kept` counts string-literal sites, which are tokens and
stay as they were. Every anchor was read in its message or its diff, not
only in its subject: it is the commit that made the change the line
describes, and its own message or diff names the number it replaces or
adds the citation the line carries.

| number | comment sites / files | rewritten | left | strings kept |
anchor |
|---|---|---|---|---|---|
| `objectstack-ai#6217` | 12/8 (1 test) | 12 | 0 | 0 | `2b641ddd4` |
| `objectstack-ai#6238` | 2/1 (2 test) | 2 | 0 | 2 | `c8d6f6e08` |
| `objectstack-ai#6265` | 1/1 (1 test) | 1 | 0 | 0 | `cfb549db8` |
| `objectstack-ai#6268` | 6/2 (2 test) | 6 | 0 | 1 | `68f5eccb1` |
| `objectstack-ai#6293` | 2/2 (1 test) | 2 | 0 | 0 | `c39a911ae` |
| `objectstack-ai#6344` | 2/2 (1 test) | 2 | 0 | 0 | `cfb549db8` |
| `objectstack-ai#6345` | 21/6 (11 test) | 21 | 0 | 4 | `e2798fab7` |
| `objectstack-ai#6535` | 1/1 | 1 | 0 | 0 | `a92b1793c` |
| `objectstack-ai#8692` | 5/2 (3 test) | 5 | 0 | 3 | `712e185db` |
| `objectstack-ai#10326` | 1/1 | 1 | 0 | 0 | `675ab574e` |
| `objectstack-ai#10359` | 2/2 | 2 | 0 | 0 | `15b63e85a` |
| `objectstack-ai#10398` | 1/1 (1 test) | 1 | 0 | 0 | `0681a76b8` |
| `objectstack-ai#10485` | 1/1 | 1 | 0 | 0 | `35ad101bc` |
| `objectstack-ai#10499` | 1/1 | 1 | 0 | 0 | `6d441e41f` |
| `objectstack-ai#10504` | 8/1 | 8 | 0 | 0 | `ff5733e03`, `0d4bd93e7` |
| `objectstack-ai#10514` | 16/2 (16 test) | 16 | 0 | 2 | `5359a9b4c` |
| `objectstack-ai#10763` | 1/1 (1 test) | 1 | 0 | 0 | `c2b97c2a1` |
| `objectstack-ai#10769` | 9/2 (6 test) | 9 | 0 | 0 | `3d7deb700` |
| `objectstack-ai#10908` | 10/3 (6 test) | 10 | 0 | 5 | `9cc6777d3` |
| `objectstack-ai#10909` | 2/1 | 2 | 0 | 0 | `5a90c56d1` |
| `objectstack-ai#10917` | 1/1 | 1 | 0 | 0 | `7940de5e0` |
| `objectstack-ai#10926` | 1/1 | 1 | 0 | 0 | `d173125fb` |
| `objectstack-ai#10943` | 5/3 (2 test) | 5 | 0 | 0 | `46d34ab7c` |
| `objectstack-ai#10944` | 9/3 (6 test) | 9 | 0 | 3 | `e598b1cbc` |
| `objectstack-ai#10952` | 5/1 | 5 | 0 | 0 | `0d4bd93e7`, `ff5733e03` |
| `objectstack-ai#10953` | 1/1 (1 test) | 1 | 0 | 0 | `be7262e72` |
| `objectstack-ai#10967` | 6/2 (6 test) | 6 | 0 | 1 | `e4a71d418` |
| `objectstack-ai#11022` | 1/1 (1 test) | 1 | 0 | 0 | `21756b325` |
| `objectstack-ai#11025` | 3/2 | 3 | 0 | 0 | `1c3a46f87` |
| `objectstack-ai#11048` | 1/1 | 0 | 1 | 0 | — |
| `objectstack-ai#11071` | 3/2 | 3 | 0 | 0 | `50fb191dc` |
| `objectstack-ai#11157` | 15/4 (10 test) (1 slash-joined) | 15 | 0 | 2 | `a4cb7817f`
|
| `objectstack-ai#11172` | 5/1 | 5 | 0 | 0 | `05181e8cc` |
| `objectstack-ai#11174` | 2/1 (2 test) | 2 | 0 | 1 | `ab23c67ab` |
| `objectstack-ai#11221` | 3/1 (3 test) | 3 | 0 | 1 | `e278a2970` |
| `objectstack-ai#11331` | 3/2 | 0 | 3 | 0 | — |
| `objectstack-ai#11671` | 1/1 | 1 | 0 | 0 (2 moved) | `09b4f4e4e` |
| `objectstack-ai#12125` | 11/3 | 11 | 0 | 0 | `79cf692b0` |
| `objectstack-ai#12151` | 3/2 (3 test) | 3 | 0 | 3 | `27b690272` |
| `objectstack-ai#12162` | 2/1 (2 test) | 2 | 0 | 0 | `c0f5e8f21` |
| `objectstack-ai#12181` | 4/2 (3 test) | 4 | 0 | 0 | `cf71d73f8` |
| `objectstack-ai#12297` | 3/1 | 3 | 0 | 0 | `9fd45a952` |
| `objectstack-ai#12943` | 2/1 (2 test) | 2 | 0 | 0 | `090f2302e` |
| `objectstack-ai#12961` | 1/1 | 1 | 0 | 0 | `901355c3b` |
| `objectstack-ai#13109` | 2/1 | 2 | 0 | 0 | `8b236c826` |
| `objectstack-ai#13193` | 6/2 (3 test) | 6 | 0 | 0 | `faff497fd` |
| `objectstack-ai#13218` | 1/1 | 1 | 0 | 0 | `c45d8e6b4` |
| `objectstack-ai#13347` | 3/3 (2 test) | 3 | 0 | 3 | `098a08ffa` |
| `objectstack-ai#13651` | 12/7 (4 test) | 12 | 0 | 0 | `ada3834ad` |
| `objectstack-ai#14192` | 2/2 | 2 | 0 | 0 | `4d0d9445a` |
| `objectstack-ai#14336` | 4/1 | 4 | 0 | 0 | `79c71d29d` |
| `objectstack-ai#14397` | 1/1 | 1 | 0 | 1 | `957f7bb45` |
| `objectstack-ai#14657` | 20/2 (7 test) | 20 | 0 | 1 | `431979e67` |
| `objectstack-ai#14667` | 1/1 | 1 | 0 | 0 | `dc7c226b9` |
| `objectstack-ai#14824` | 3/1 | 3 | 0 | 0 | `cf6b67164` |
| `objectstack-ai#14828` | 22/3 (7 test) | 22 | 0 | 3 | `08706f0e0` |
| `objectstack-ai#14829` | 9/3 (6 test) | 9 | 0 | 3 | `ee370d318` |
| `objectstack-ai#14902` | 1/1 | 1 | 0 | 0 | `61821e54c` |
| `objectstack-ai#15040` | 6/3 (3 test) | 6 | 0 | 2 | `8644d1d33` |
| `objectstack-ai#15041` | 6/5 (4 test) | 6 | 0 | 2 | ADR-0104 (2026-09-05 addendum,
landed as 932acc3) |
| `objectstack-ai#15045` | 2/2 (1 test) | 2 | 0 | 0 | `288fe9c34` |
| `objectstack-ai#16887` | 2/1 (2 test) | 2 | 0 | 0 | `9cdffbe36` |
| `objectstack-ai#17080` | 1/1 (1 test) | 1 | 0 | 0 | `8b4890343` |
| `objectstack-ai#17081` | 8/3 (4 test) | 8 | 0 | 3 | `f721ef0ff`, `24d622b94` |
| `objectstack-ai#17883` | 10/3 (5 test) | 10 | 0 | 3 | `b06b2db5c` |
| **total** | **317** | **313** | **4** | **49** | **62 distinct commits
+ ADR-0104** |

Every cited sha matches exactly one object (`git rev-parse
--disambiguate`, count 1 for each of the 62), is a commit, has one
parent, and is an ancestor of the base (`merge-base --is-ancestor`, exit
0 for all 62). The checkout is not shallow (`--is-shallow-repository`
false); the control leg `13a6cb4ad` exits 0 and the negative control
(this branch's own `47241dd80e`, not on `main`) exits 1. ADR-0104's
2026-09-05 addendum landed as `932acc3df`, which passes the same four
checks. Where an earlier stage gave a number an anchor and the cli site
describes the same decision, the same anchor is reused (17 numbers,
objectstack-ai#17081 for its application half only; for example `e2798fab7` for objectstack-ai#6345,
`68f5eccb1` for objectstack-ai#6268, `35ad101bc` for objectstack-ai#10485, `09b4f4e4e` for objectstack-ai#11671
and `61821e54c` for objectstack-ai#14902), so each number carries one anchor across
the tree. Several numbers are the PR number of their own anchor commit
(objectstack-ai#6344, objectstack-ai#10398, objectstack-ai#14667, objectstack-ai#16887), so the sha is the same object the
number named.

**Numbers with more than one anchor, by site:**
- `objectstack-ai#10504` / `objectstack-ai#10952` (`format.ts`): `ff5733e03` added the opt-in zero
row for `UI:` alone and `0d4bd93e7` made it required for every section,
so lines naming both now name both commits. `format.ts:1573` read
「(objectstack-ai#10504, objectstack-ai#10952, objectstack-ai#11172)」 and now reads 「(commits ff5733e, 0d4bd93,
05181e8)」.
- `objectstack-ai#10943` / `objectstack-ai#11157` (`serve.ts`): `46d34ab7c` made the host importer's
fallback base a caller-supplied parameter, and `a4cb7817f` made `serve`
pass its own base and collapsed `importConfigPlugin` from three branches
to two. The slash-joined `serve.ts:1173` 「(objectstack-ai#10943/objectstack-ai#11157)」 now reads
「(commits 46d34ab and a4cb781)」; `serve.ts:1459` 「(objectstack-ai#10908 → objectstack-ai#11157)」
reads 「(commits 9cc6777 → a4cb781)」.
- `objectstack-ai#17081` (8 sites): one card with two halves. `f721ef0ff` took the
platform's half (the `Dev admin` banner line says what the account sees)
for 7 sites; `format.ts:1132` describes the application half and now
reads 「[objectstack-ai#17556 — commit 24d622b]」, the spelling the spec stage used at
`dev-login.zod.ts:10`, with `format.ts:1133` naming objectstack-ai#17081 in words
(「its parent card」).
- `objectstack-ai#15041` (6 sites): the decision is a maintainer ruling recorded
verbatim in ADR-0104's 2026-09-05 addendum, whose Sequencing section
names the three steps the lines cite. So the lines cite the addendum
(「The ruling in ADR-0104's 2026-09-05 addendum decided it」, 「sequencing
step 2 of ADR-0104's 2026-09-05 addendum」), not a commit.

**Wordings to check, each true of its commit:**
- A commit does not rule. Where a line said a number ruled, it now says
what the commit did with the ruling: 「semantics by the ruling commit
68f5ecc landed」, 「Ruled at triage, landed as commit e598b1c」, 「the
triage commit 9cc6777 landed requires this text be CHOSEN」, 「Rulings
objectstack-ai#5728 and objectstack-ai#14412, and the ruling commit d173125 landed,」, and
`resync.ts:96` keeps the ruling's date from `712e185db`'s message:
「commit 712e185 (the 2026-08-15 ruling)」.
- A line that named a DEFECT by its number now says so: 「the defect
commit 79cf692 fixed」, 「the defect commit 9cc6777 fixed」, 「the exact
defect commit 08706f0 closed」, 「the hard-failure class of the `22P02`
commit 8644d1d fixed」, 「Before commit 5359a9b (raw)」 / 「Since commit
5359a9b (masked)」.
- **A stale claim, corrected by its anchor.** `validate.ts:813-815` said
「`ManifestSchema` is not `.strict()` and drops unknown keys with nothing
said (objectstack-ai#14192)」, but `ManifestSchema` has been `strictObject` since
`4d0d9445a`, which landed before the commit that wrote the sentence.
Citing that commit in a present-tense sentence would contradict itself,
so the three lines move to the past tense: 「was not `.strict()` and
dropped unknown keys with nothing said until commit 4d0d944, so acting
on that inference produced a manifest that looked fine」.
- **Anchors found by diff, not by subject.** `objectstack-ai#10326` has no commit
message naming it; `675ab574e` took the 1.7.1 measurement the line
cites, and its own changeset and test name objectstack-ai#10326. `objectstack-ai#12162` is named by
no message either; `c0f5e8f21` is the only commit that ever added the
number, and its message states the point the lines make. `objectstack-ai#14397`'s
citation was added by `957f7bb45`'s own diff, which is the change the
heading describes. `objectstack-ai#10763` is added three times by `c2b97c2a1`'s diff.
- `objectstack-ai#10499` (`init.ts:978`): the line uses the earlier drift between the
two scaffold paths as precedent; that drift was about pnpm build
approvals, and `6d441e41f` gated the two paths against each other, so
the line reads 「already drifted once (closed by commit 6d441e4)」.
- `objectstack-ai#6293`: `c39a911ae` is the commit that found the 「headless husk」
`JSON.stringify(stack)` leaves where a declaration was; `bf4ebe2f3`,
which wrote the cli lines, only cites it.
- Quoted text: `generate-field-type-vocabulary.pin.test.ts:92` sits
inside a verbatim quotation of the file's former clause, so the commit
stands in an editorial bracket (「([commit ee370d3]'s pin argues this
in full)」), as PR objectstack-ai#20624 did.
- Headings with a dash rule (`serve.ts:1125`, `:1459`, `:1521`, `:3276`,
`serve-cluster-host-resolution.test.ts:831`,
`generate-field-type-vocabulary.pin.test.ts:260`,
`files-to-references.ts:274`) trim their trailing dashes to hold the
width; `serve-cluster-host-resolution.test.ts:893` is a trailing comment
whose code part is byte-identical.

## The sites left

**No deciding commit (4 sites, all visible to the census):**
- `init.ts:267` (objectstack-ai#11048): 「whether to admit that band at all is objectstack-ai#11048」
names an open support decision (pnpm 10.0 to 10.4). The only commit
naming it, `568de194e`, files it unassigned; no later commit decides it,
and the floor is still `>=10.15` at the base.
- `plugin/publish.ts:118`, `osplugin.ts:21`, `osplugin.ts:49` (objectstack-ai#11331):
the parenthetical points at the unpack-time integrity re-verification
leg, which was never built (`b60f48b52`: 「The enforce leg points at
objectstack-ai#11331」). No commit decides it; the ownership clause on the same lines
comes from `f89812e4d`, but the number is not about that clause.

**String sites kept as tokens (49).** 48 are test titles and test-code
strings in 22 files. One is a non-test string: the `os meta resync` skip
explanation at `commands/meta/resync.ts:71`, 「on installs from before
objectstack-ai#8692, the platform's own seeded defaults carry that same stamp」, which
an operator reads (see Acceptance notes).

## Mechanical guard: no code token moves, and exactly two string
literals do

The check compares the TypeScript parser's leaf tokens (TypeScript
6.0.3, JSDoc nodes excluded, comments being trivia) of each touched file
at base `04b202e5cb` against the working tree, over all 65 touched files
in `packages/cli/src`, and lists EVERY differing token, not only the
first. Controls mutate the head text in memory only, so nothing on disk
moved for them.

- Real run: 156,633 base tokens, token counts equal in every file,
**exactly 2 differing tokens**, both `StringLiteral`:
`commands/i18n/extract.ts:227` (the help text) and
`utils/i18n-extract.ts:2294` (the header line). No other token in any
file differs.
- Comment-insertion control (`serve.ts`): still exactly those 2 (exit 1,
no new difference).
- Code-insertion positive control (a declaration in `serve.ts`): the
count differs (17,815 to 17,820) and a third difference appears at token
0.
- String positive control (one character added inside the first string
literal past offset 2000 of `serve.ts`): a third difference appears, a
`StringLiteral` at token 145.
- The 27 generated companions: 2,940 base tokens, 0 differing tokens
(their only change is a JSDoc line).

Line balance: every touched file is +N/−N, and every line count is equal
at base and head (94 files). A raw scan of the 92 changed source and
generated files for control bytes finds none (its positive control, a
scratch file holding a U+0001 byte, matches).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/cli`
is included, in PR objectstack-ai#20632's form and level. Unlike stage 2's, it names
the two strings, because 「Comments only」 would not be true here.

Measured on the built package: `files[]` is `dist`, `README.md` and
`CHANGELOG.md`. After the build, the rewritten docblocks reach `dist`:
142 `commit SHA` citations in 39 of its `.js` / `.d.ts` files. For
example `storage-driver.ts:91`'s rewritten line 「(commit 68f5ecc).
These are the」 is in both `dist/utils/storage-driver.d.ts` and `.js`,
beside the unchanged next line of the same docblock 「runtime's
declarations, not copies of them — in particular」 (the positive
control); a negative control phrase appears nowhere. The new help text
is in `dist/commands/i18n/extract.js`, the header literal with
`09b4f4e4e` is in `dist/utils/i18n-extract.js`, and `objectstack-ai#11671` appears
nowhere in the package's `dist`.

## Gates (head `6bb4d3b531`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each locked run at this
head:

```text
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 110s (1m50s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=4
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 12s · declare it in the PR body · pnpm --filter @objectstack/cli typecheck
os-verify-lock: VERDICT command-exit 1 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 150s (2m30s) · declare it in the PR body · pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 5s · declare it in the PR body · pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 test/published-subpath-console.pin.test.ts test/published-subpath-hook-body.pin.test.ts
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 9s · declare it in the PR body · pnpm --filter @objectstack/cli exec vitest run --project integration --maxWorkers=2 src/commands/generate-declared-column-default.pin.test.ts src/commands/generate-string-family-width.pin.test.ts src/commands/meta/delete-reset-carriers.test.ts 
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 30s · declare it in the PR body · pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 src/commands/validate-json-strict-exit.e2e.test.ts
```

The regeneration ran earlier against the same unlocked lock, on a
closure build at `47241dd80e`:

```text
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 102s (1m42s) · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/cli...' --filter '@objectstack/platform-objects...' --filter '@objectstack/plugin-approvals...' --filter '@objectstack/plugin-audit...' --filter '@objectstack/plugin-security...' --filter '@objectstack/plugin-sharing...' --filter '@objectstack/plugin-webhooks...' --filter '@objectstack/service-messaging...' --filter '@objectstack/service-realtime...' --filter '@objectstack/service-storage...' build
```

The branch merged `origin/main` twice, as the dispatch orders
(`d1e09a7eed` merging `cd901d7a5f`, and `6bb4d3b531` merging
`0cb72cfc72`); neither touched `packages/cli`, a translations directory
or the prose-id ledger. After each merge: `pnpm install
--frozen-lockfile`, then the whole workspace (`turbo run build
--filter='./packages/*' --filter='./packages/*/*'`, 71 tasks, 71
successful).

- **Tests** (unit tier, then every touched file outside it):
- `vitest run --project unit`: 234 files, 3,342 tests; **232 files and
3,337 tests pass, 5 tests in 2 untouched files fail on this host**:
`test/published-subpath-console.pin.test.ts` and
`test/published-subpath-hook-body.pin.test.ts` compare a path under
`os.tmpdir()` with the realpath the resolver answers, and on macOS
`/var` is a symlink to `/private/var`. With `TMPDIR` set to its realpath
the same two files pass, 29 of 29 (the fourth line above). Neither file
is in this diff; the cli change is comments and two strings.
- The unit tier holds 32 of the 36 touched test files. The other four
ran by name: the three integration-tier files (`--project integration`:
3 files, 60 tests pass) and the nightly-tier
`validate-json-strict-exit.e2e.test.ts` (`OS_TEST_TIERS=nightly`: 1
file, 7 tests pass). So every touched test file ran.
- The producer's own tests and the companions' readers:
`test/i18n-extract-source-hashes.test.ts`,
`test/i18n-extract-companion-orphan.test.ts` and
`test/i18n-extract-generated-apps-leaf-provenance.test.ts` (3 files, 25
tests); `@objectstack/platform-objects`'s `src/apps/translations` (24
files, 430 tests); `@objectstack/plugin-sharing`'s `src/translations` (3
files, 13 tests). All pass, at `d71ab0e27e`, whose `packages/cli` and
companions are byte-identical to this head.
- **Typecheck:** `pnpm --filter @objectstack/cli typecheck` exits 0.
`tsc --listFiles` counts 298 `src` files under `tsconfig.json`, all 158
`src` test files among them, so every touched test file is type-checked;
`check:test-typecheck` holds its ledger (3 files, 28 errors, 6 pinned
signatures).
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at `6bb4d3b531` (2026-09-29T13:44:40Z to 13:45:11Z). Not
narrowed.
- **Citation judging:** `node scripts/check-issue-citations.mjs --base
origin/main` exits 0 after the second merge: 67 citations judged across
56 files (66 resolve, 1 cross-repo). These are the live numbers that
stay on rewritten lines, 54 of them the objectstack-ai#12069 and objectstack-ai#8765 pair in the 27
headers. It defers `*.test.ts`, so the added-minus-removed count over
the whole diff covers the rest: 0 numbers added.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `6bb4d3b531` derived 76
families (68 before the prose-id ledger commit put a `scripts/` path in
the change set). All 76 ran, and `--ran` over a record carrying each
exit code reads 「76 derived, 76 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived
zero).
- 75 exit 0. One exits 1 for this host, not for this diff: `pnpm
check:bash32-floor` runs its self-test first, and 7 of its 179 cases
assert that each bash-4 probe is shell THIS host can parse; the only
bash here is `/bin/bash` 3.2.57, which cannot. The gate's real-tree
half, run alone (`node scripts/check-bash32-floor.mjs`), exits 0: 32
tracked shell files, 0 findings. The self-test half is NOT MEASURED
here, reason: no bash 4+ on this host; CI's bash measures it. This diff
touches no shell file.
- Among them: `check:doc-authoring` (809 pinned sibling prose-id sites,
no growth, no unrecorded burn-down), `check:i18n` (9 packages in sync),
`check:i18n-coverage` (13 configs, none new), `check:i18n-stale-fill`
(27 of 27 companions served, 0 stale), `check:i18n-walk-parity`,
`check:nul-bytes` (9,283 files, no raw control bytes),
`check:published-files`, `check:cli-command-ids` and
`check:issue-citations` (self-test).
- **Artifact rosters:** 35 of the 38 non-self-test roster rows exit 0 at
`6bb4d3b531`, `check-changeset-fixed` (its roster sits under
`.changeset/`), `check:error-code-casing`, `check:filter-alias-parity`
and `check:authz-resolver` (the four whose rosters share a directory
with this diff) among them. The other three,
`check-closing-target-claim`, `check-partof-closing-keyword` and
`check-single-claim-paths`, need a pull request's context; they are run
against this PR once it exists and reported on the card. The 17
checker-health-only rows were not run.

## Hypotheses (measured first)

- **H0 holds.** The filtered census answers 174 dead sites at
`04b202e5cb` (167 lines, 30 files, 50 numbers), equal to the card's
count at `f11b5f20a2`: no drift.
- **H1 holds, with the listed exceptions.** After the rewrite the
filtered census answers 4, all for an unfound anchor: `objectstack-ai#11048` (an open
support decision) and `objectstack-ai#11331` three times (an enforce leg never built).
No site is held for an open PR: the two held files carry no dead
citation. The claim's read and this stage's two reads of the open PRs'
file lists (12:38:15Z, 7 open PRs; 13:58:56Z, 9 open PRs) found only PR
objectstack-ai#20589 in `packages/cli/src` and none touching a companion or the
prose-id ledger. PR objectstack-ai#20652, opened after the claim, edits
`packages/platform-objects`'s three `LOCALE.objects.generated.ts`
bundles beside the companions: no file overlap.
- **H2 holds, by the token guard.** A comment-stripped comparison of
every touched file (the parser's leaf tokens, JSDoc excluded, every
difference listed) finds exactly the two declared `StringLiteral` tokens
and nothing else, and its code and string controls each add a
difference. The emitted `dist` is not byte-identical, because docblocks
and the two strings ship, which is why the changeset is `patch`.
- **H3 holds.** `git grep -n "objectstack-ai#11671" --
'*.source-hashes.generated.ts'`: 0 hits at head, 27 at `04b202e5cb`.

## Acceptance notes

- **Form D, not touched here.** 49 dead numbers stand inside string
literals: 48 in test titles and test-code strings (22 files, 21
numbers), and one an operator reads: the `os meta resync` skip
explanation at `commands/meta/resync.ts:71`, 「on installs from before
objectstack-ai#8692, the platform's own seeded defaults carry that same stamp」. The
comments beside it (`resync.ts:55` and `:96`) now cite `712e185db`.
Ruling D (no number, the lesson in words) is a string change outside
this stage's two declared strings; the card already carries a form-D
stage for the lane (ACCEPT 5888034755), and this string is its
author-shown first.
- **`objectstack-ai#11671` outside this stage's surface.** The number still stands in
other lanes' files: the nine `scripts/i18n-extract.config.ts` docstrings
(outside the census surface), six `src/translations/index.ts` files
(`plugin-approvals`, `plugin-audit`, `plugin-security`,
`plugin-webhooks`, `service-realtime`, `service-storage`), six sites in
`packages/platform-objects/src` (`source-hash.ts` three times,
`setup.translation.ts`, `metadata-translations/index.ts`,
`source-hash.test.ts`),
`packages/cli/test/i18n-extract-source-hashes.test.ts:3`, and 13 in
`scripts/**` and `.github/workflows/lint.yml`. The anchor for all of
them is `09b4f4e4e`. Noted for those lanes' stages, not touched.
- **Outside the scope and the census surface.** `packages/cli` outside
`src/**` holds 242 dead citations: `test/` 185, `scripts/` 27, `bin/`
10, `vitest.config.ts` 15, `vitest-tiers.ts` 2,
`vitest-tiers.fixtures.ts`, `tsconfig.test.json` and
`test-typecheck-debt.json` 1 each (whole-file projection, the before
board). They stay for a later stage of this card.
- **A host-dependent pin.** `test/published-subpath-console.pin.test.ts`
and `test/published-subpath-hook-body.pin.test.ts` fail 5 tests on
macOS, where `os.tmpdir()` is a symlink, and pass with a realpath
`TMPDIR`. CI's Linux runners do not see it. Noted, not filed.
- **A hex colour in the whole-file reading.** `serve.ts:5621` holds the
CSS colour `#141417` in a string. The census blanks strings, so it never
sees it; the supplementary whole-file projection reads it as a citation
beyond the frontier (`never-issued`). It is not a citation; it is the
second of the two `src string` sites left in the supplementary table,
beside `objectstack-ai#8692`.
- **The slash-joined grammar gap, again.** `CITATION_RE` refuses a `#`
preceded by `/`, so the second number of `#A/#B` is never judged. In
`packages/cli/src` one such dead number stood (`serve.ts:1173`,
rewritten here). The same shape PR objectstack-ai#20624 and PR objectstack-ai#20632 reported, for
the grammar family PR objectstack-ai#20533 names.

## Deviations

- **One file outside the claim's surface.**
`scripts/doc-authoring-prose-id.baseline.json`, the shrink-only ledger
of `check:doc-authoring`'s sibling-package prose-id leg, pinned the two
`objectstack-ai#11671` string sites this stage removes, so the gate went red (「the
prose-id baseline is STALE — pinned entries exceed the tree」) and
prescribed regenerating it in the same PR. It was regenerated with its
own command (`node scripts/check-doc-authoring.mjs --census-ledger`,
which refuses to grow the ledger): 4 lines removed, the two `objectstack-ai#11671`
pairs and nothing else. The claim's file surface did not name this file;
it is the gate's own remedy for the two strings the claim does name.
- One site beyond the census's read grammar (the slash-joined `objectstack-ai#11157`)
is rewritten, and thirteen more lines are the other half of a rewritten
sentence (listed under What changed).
- `validate.ts:813-815` moved to the past tense, because the claim they
carried was false before this change (see Wordings to check).
- Anchor research for 64 of the 65 numbers ran in four read-only
research subagents; every proposal was checked here against the commit's
message or diff and every changed line was reviewed, and eight were
reworded by hand (the four lines two subagents shared, 「that commit's
to」, the kept PR link, and two companions).
- Commit trailers are AGENTS.md's model-free pair (`Claude-Session` plus
`Co-authored-by: Claude`), and the pre-push trailer check passed on
every push; the harness's attribution reminder asked for a model-named
trailer, which AGENTS.md overrides. The two merge commits carry git's
default message.

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…rc to the commits that decided them (objectstack-ai#20673)

Part of objectstack-ai#20594
Clause-②: no

## What changed

This is stage 4 of the `domain:cli` lane of the dead-citation sweep:
`packages/types/src/**`. Every comment or docblock site in scope that
cited a tracker number answering 404 now cites, in ruling C+D's form C
(comment 5749154545 on objectstack-ai#19123), the commit in this repository's history
that decided what the line describes, and says in its own words what
that commit decided. PR objectstack-ai#20533 is the method; PR objectstack-ai#20624 (`runtime`), PR
objectstack-ai#20632 (`rest`) and PR objectstack-ai#20656 (`cli`) are the landed stages this
follows. The card stays open for the form-D stage and the rest of the
lane, so this PR says `Part of`.

That is **83 comment sites on 83 lines in 17 files, covering 12
numbers**: the census's 52 (all of them) and 31 more in test comments,
which the census defers. Each rewritten line cites one of **12 distinct
commits**. No ADR or ruling-record file records any of these twelve
decisions, so every anchor is a commit.

Only comments changed. Every touched file keeps its line count (94 lines
out, 94 in, over 17 files), so no line citation into these files moves.
Eleven of the 94 lines held no dead site; each is the other half of a
sentence that had to change: `thrown-http-error.ts:316-319`,
`node.ts:1103`, `:1429`, `:1447`, `:1452`, `:1476`, and
`node.test.ts:449`, `:2420` (see "Wordings to check").

**No citation number is added.** Over the 94 line pairs, every tracker
number on an added line was already on the line it replaces (per-pair
check: 0 added), and no PR number stands on an added line. No code token
moves (see the guard below). No site was left: no dead comment site in
scope lacked a deciding commit, and no open PR touches
`packages/types/src`.

One file outside `packages/types/src`: a `patch` changeset for
`@objectstack/types`, in PR objectstack-ai#20632's form and level.

## Census: `packages/types`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run with the fleet token. Its
surface is comment prose in `packages/**/src/**/*.ts` with string
literals blanked, and it defers `*.test.ts`. The count is its
`allocated-but-absent` findings under `packages/types/`. Both runs
enumerated the whole board (185 pages), so neither read a truncated
board.

| reading | tree | board | whole-repo `allocated-but-absent` | types
sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `6bff748bbd`, run 2026-09-29T15:31:59Z to 15:41:36Z |
enumerated, 185 pages, frontier objectstack-ai#20663, 18,490 numbers | 1,510 | **52**
| 52 | 7 | 11 |
| after | head `686a4c60cb`, run 2026-09-29T16:04:17Z to 16:12:39Z |
enumerated, 185 pages, frontier objectstack-ai#20671, 18,498 numbers | 1,458 | **0** |
0 | 0 | 0 |

The before count equals the card's 52 at `f11b5f20a2`: no drift. The
whole-repo drop is 52, exactly this diff's 52 sites, and the whole-repo
resolving count rises by one (32,882 to 32,883): the live objectstack-ai#12751 that
`index.ts:4` now spells so the grammar reads it. Both runs read this
worktree, the base and then the base plus this one commit, so no other
change entered either count.

**Supplementary instrument, the whole scope.** The census does not read
test files or strings, and this stage's scope includes test comments. So
a second reading runs the gate's own exported `extractCitations`
(whole-file and comment-prose projections) and `classifyCitation` over
every `.ts` file under `packages/types/src` (42 files), against a board
from the gate's own `probeBoard`. The lit controls objectstack-ai#20594, objectstack-ai#19123 and
objectstack-ai#20656 answered 200 and are on both boards; the dead controls objectstack-ai#11671,
objectstack-ai#10514 and objectstack-ai#14828 answered 404 and are on neither.

| reading | tree | board | citations | dead | src comment | test comment
| src string | test string |
|---|---|---|---|---|---|---|---|---|
| before, 15:34Z | `6bff748bbd` | probed, frontier objectstack-ai#20661 | 622 |
**101** | 52 | 31 | 1 | 17 |
| after, 16:04Z | `686a4c60cb` | probed, frontier objectstack-ai#20668 | 540 | **18**
| 0 | 0 | 1 | 17 |

Its src-comment column equals the census's 52, site for site (the two
site lists are identical), which is the control on the second
instrument. The drop of 82 citations is the 83 dead sites removed plus
one live number the grammar now reads: `index.ts:4` spelled
`[objectstack-ai#11343/objectstack-ai#12751]`, whose second half the grammar skips after a slash,
and now reads `[commit c0714eb / objectstack-ai#12751]` like its module doc, so the
live objectstack-ai#12751 is judged (resolving src comments 273 to 274). Resolving
pull requests (20), cross-repo citations (14) and the other resolving
counts are unchanged. A separate scan for slash-joined pairs in
`packages/types/src` found six (`objectstack-ai#11343/objectstack-ai#12751`, `objectstack-ai#3878/objectstack-ai#3899`,
`objectstack-ai#7525/objectstack-ai#8016`, `objectstack-ai#4728/objectstack-ai#4825`, `objectstack-ai#8621/objectstack-ai#8622`, `objectstack-ai#5352/objectstack-ai#5367`); every
second half answers 200, so no dead number hid behind a slash here.

## Per-number table

Sites and files are the dead comment sites in scope at the base, test
sites counted in brackets. `strings kept` counts string-literal sites,
which are tokens and stay as they were. Every anchor was read in its
message or its diff, not only its subject: it is the commit that made
the change the line describes.

| number | comment sites / files | rewritten | strings kept | anchor |
|---|---|---|---|---|
| `objectstack-ai#8824` | 1/1 (1 test) | 1 | 0 | `8ac232306` |
| `objectstack-ai#9934` | 5/4 (2 test) | 5 | 2 | `79c46da90` |
| `objectstack-ai#10943` | 10/2 (4 test) | 10 | 2 | `46d34ab7c` |
| `objectstack-ai#10944` | 1/1 | 1 | 0 | `e598b1cbc` |
| `objectstack-ai#11343` | 3/3 (1 test) | 3 | 1 | `c0714eb5d` |
| `objectstack-ai#12281` | 1/1 | 1 | 0 | `0783d7b80` |
| `objectstack-ai#13197` | 5/2 (2 test) | 5 | 2 | `56c093c4d` |
| `objectstack-ai#13279` | 8/5 (2 test) | 8 | 0 | `6a180e42d` |
| `objectstack-ai#13324` | 15/3 (7 test) | 15 | 5 | `4cda78c9b` |
| `objectstack-ai#15044` | 8/2 (3 test) | 8 | 1 | `088f761e5` |
| `objectstack-ai#15045` | 21/2 (8 test) | 21 | 1 | `288fe9c34` |
| `objectstack-ai#16657` | 5/2 (1 test) | 5 | 4 | `5a95b0e93` |
| **total** | **83** | **83** | **18** | **12 distinct commits** |

Every cited sha matches exactly one object (`git rev-parse
--disambiguate`, count 1 for each of the 12), is a commit, has one
parent, and is an ancestor of the base (`merge-base --is-ancestor`, exit
0 for all 12). The checkout is not shallow (`--is-shallow-repository`
false); the control leg `f5a9bc2f3` (2026-08-10, older than the oldest
anchor, `8ac232306` of 2026-08-15) exits 0 and the negative control
(this branch's own `686a4c60cb`, not on `main`) exits 1.

**Anchors reused from earlier stages**, so each number carries one
anchor across the tree: `79c46da90` for objectstack-ai#9934 (stages 1 and 2, the spec
lane), `46d34ab7c` for objectstack-ai#10943, `e598b1cbc` for objectstack-ai#10944 and `288fe9c34`
for objectstack-ai#15045 (stage 3), `0783d7b80` for objectstack-ai#12281 (stage 1), `56c093c4d` for
objectstack-ai#13197 (stage 2, the spec lane), `6a180e42d` for objectstack-ai#13279 (stages 1 and 2,
`plugin-sharing`) and `c0714eb5d` for objectstack-ai#11343 (`plugin-auth`).

**New anchors, and how each was found:**
- `objectstack-ai#8824` → `8ac232306`: objectstack-ai#8824 is that commit's own PR number (its
subject ends `(objectstack-ai#8824)`), so the sha is the object the number named.
`error-leak.test.ts:180` read 「PR objectstack-ai#8824 corrected the」 and now reads
「Commit 8ac2323 corrected the」.
- `objectstack-ai#13324` → `4cda78c9b`: its subject does not name the number, but its
changeset heading does (「require a missing-table error to name the table
that was READ (objectstack-ai#13324)」), and its diff adds `readObject` and every
`[objectstack-ai#13324]` marker this module carries. It landed in
`packages/metadata/src/utils/schema-sync-errors.ts`, the file
`6a180e42d` then moved here (a rename at 86 percent similarity).
- `objectstack-ai#15044` → `088f761e5`: 「Part of objectstack-ai#15044」, the only commit whose
message names the number; it made the objectstack-ai#13330 succeeding leg recognise
the package root by the name the declaration promises, and added the
`BOUNDARY` pin at `node.test.ts:1863` that `:2175` and `:2419` point at.
- `objectstack-ai#16657` → `5a95b0e93`: it added `operatorFacingErrorText`,
`DECLARED_DATABASE_FAULT_CODE` and the raw-path fragment, and its
message calls itself the fourth prose round on objectstack-ai#16657.

## Wordings to check

- **A stale future tense, corrected by its anchor.**
`thrown-http-error.ts:315-320` said objectstack-ai#12281 「is a separate card with its
own measurement-first step, so nothing here applies it; this function is
the shape it will read」. `a81aa9dd5` wrote that on 2026-08-29;
`0783d7b80` landed the next day and its message says 「the door now reads
`serverFaultProvenance`」. Citing the commit in the future tense would
contradict itself, so the six lines now read 「Commit 0783d7b — the
prose axis of the same 2026-08-27 ruling — reads the `'declared'` limb
of this same function … It landed separately, after its own
measurement-first step, so nothing here applies it; this function is the
shape it reads rather than a second copy it would have had to grow.」
- **An open question named by a number that had already landed.**
`node.ts:1447-1452` called where a relative specifier should resolve
from 「an open policy question owned by objectstack-ai#10944」 and ended with 「Answering
half of another card's undecided question」. `e598b1cbc` (objectstack-ai#10944's
landing) had merged 40 minutes before `46d34ab7c` wrote those lines, and
it refuses the relative spelling. The lines now read 「the policy
question commit e598b1c settled for `serve` (it refuses a relative
`plugins: [...]` entry rather than silently re-basing it)」 and
「Answering half of another change's question」.
- **「the card」 once the antecedent became a commit.**
`node.ts:1102-1103` 「objectstack-ai#15045 is the card about telling an operator which
one was measured」 now reads 「commit 288fe9c is the change that tells
an operator which one was measured」. `node.ts:1476` 「the second
verification axis the card holds open」 now reads 「the second
verification axis that commit left unbuilt」, which is what `288fe9c34`'s
message says (「deliberately not built here」). `node.test.ts:449` 「The
card's own 4-row matrix」 now reads 「The 4-row matrix behind that
commit」.
- **Headings that named a defect by its number now say so.**
`node.test.ts:1566` reads 「Fixed by commit 088f761: the SUCCEEDING leg
recognised the package by the DECLARATION KEY」 and `:1881` reads
「Reworded by commit 288fe9c: the location sub-case REFUSES correctly
and EXPLAINED itself wrongly」 (`288fe9c34` changed the wording and kept
the refusal). The dash-rule headings trim trailing dashes:
`node.ts:1381`, `node.test.ts:1566`.
- **A quoted triage.** `node.test.ts:2160` quoted 「objectstack-ai#15045's triage」; it
now reads 「quoted from the triage commit 288fe9c landed」. That
commit's changeset records the same decision in its own words: the key
stays the expectation 「because widening it would accept any directory
sitting at the key and trade a wrong REMEDY for a wrong LOAD」.
`node.test.ts:2053` said objectstack-ai#15045 「asked for this sentence」; it now says
`288fe9c34` 「wrote this sentence」, and that commit's own test comment
says the card asked for it.
- **A defect that proved a point.**
`driver-error-classification.callers.test.ts:24-25` said the omission is
the shape 「objectstack-ai#13324 existed to close」 and that prose 「is exactly what
objectstack-ai#13324 proved insufficient」; it now reads 「the … shape commit 4cda78c
closed」 and 「prose is exactly what that commit's defect proved
insufficient」.
- **「pre-#N」 spellings** (the card's control sites
`driver-error-classification.ts:608` and `node.ts:1428`, plus
`callers.test.ts:20` and `node.test.ts:594`) now say 「before commit X」.

## Mechanical guard: no code token moves

The check compares the TypeScript parser's leaf tokens (TypeScript
6.0.3, JSDoc nodes excluded, comments being trivia) of each touched file
at base `6bff748bbd` against the working tree at `686a4c60cb`, over all
17 touched files, and lists EVERY differing token, not only the first.
Controls mutate the head text in memory only, so nothing on disk moved
for them.

- Real run: 31,911 base tokens, token counts equal in every file, **0
differing tokens** (exit 0).
- Comment-insertion control (a new line comment in `node.ts`): 0
differing tokens (exit 0).
- Code-insertion positive control (a declaration prepended to
`node.ts`): the count differs and a difference appears at token 0 (exit
1).
- String positive control (one character changed inside the kept
`undeclaredMessage` literal at `node.ts:383`): exactly 1 differing
token, a `StringLiteral` at token 672 (exit 1).

So H2 holds by the token guard. The emitted `dist` is not
byte-identical, because the docblocks ship, which is why the changeset
is `patch`. Line balance: every touched file is +N/−N and every line
count is equal at base and head (17 files). A raw scan of the 18 changed
files for control bytes finds none (its positive control, a scratch file
holding a U+0001 byte, matches).

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/types`
is included, in PR objectstack-ai#20632's form and level: 「Comments only: no error
code, refusal text, type, export or runtime behaviour changes.」

Measured on the built package: `files[]` is `dist`, `README.md` and
`CHANGELOG.md`. After the build, the rewritten docblocks reach `dist`:
`0783d7b80`, `79c46da90`, `5a95b0e93` and `c0714eb5d` are in
`dist/index.d.ts` and `index.d.mts`, `4cda78c9b` in all four `index`
files, `6a180e42d` in `index.js` and `index.mjs`, and `46d34ab7c` and
`288fe9c34` in `dist/node.d.ts` and `node.d.mts`. The positive control,
the unchanged sentence 「sanitisation REGIME is the condition, not one of
its two outcomes」 of the `0783d7b80` docblock, is in `dist/index.d.ts`
beside it; a negative control phrase appears nowhere. Of the twelve dead
numbers, only objectstack-ai#10943 remains in `dist`, twice, and both are the kept
operator-facing string at `node.ts:383` (see Acceptance notes).

## Gates (head `686a4c60cb`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each locked run at this
head:

```text
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/types exec vitest run --project repo --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 5s · declare it in the PR body · pnpm --filter @objectstack/types exec vitest run --project local --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/types typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 28s · declare it in the PR body · pnpm --filter '@objectstack/types...' build
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 46s · declare it in the PR body · pnpm lint
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 93s (1m33s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=4
```

`origin/main` did not move after the branch was cut: `git merge
origin/main` answered 「Already up to date」 at `6bff748bbd`, so the base
is the merge base and nothing needed rebuilding. `origin/main` has since
moved to `6c11ef9ecb` (PR objectstack-ai#20663: two pages under
`content/docs/automation`, read 16:13Z). It touches nothing this diff or
its gates read, so the branch was not merged again and every reading
here stays at `686a4c60cb`.

- **Build:** the dependency closure (`@objectstack/types...`: `spec`
then `types`) and then the whole workspace (71 tasks, 71 successful).
`check-dts-emitted` finds 2 of 2 declared declaration files. The build
left the tree clean.
- **Tests:** `--project local`: 22 files, 685 tests pass. `--project
repo`: 1 file (`driver-error-classification.callers.test.ts`, touched
here), 7 tests pass. 22 + 1 is all 23 test files in the package, so
every touched test file ran.
- **Typecheck:** `pnpm --filter @objectstack/types typecheck` exits 0.
`tsc --listFiles` counts 42 `src` files under `tsconfig.json`, all 23
test files among them, so every touched test file is type-checked.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at `686a4c60cb` (2026-09-29T16:01:23Z to 16:02:09Z). Not
narrowed.
- **Citation judging:** `node scripts/check-issue-citations.mjs --base
origin/main` exits 0: 5 citations judged across 7 files (4 resolve, 1
cross-repo). These are the live numbers that stay on rewritten non-test
lines. It defers `*.test.ts`, so the per-pair count over the whole diff
covers the rest: 0 numbers added.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` at `686a4c60cb` derived 61
families. All 61 ran and exit 0, and `--ran` over a record carrying each
exit code reads 「61 derived, 61 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived
zero). Among them: `check:doc-authoring`, `check:nul-bytes`,
`check:issue-citations` (self-test), `check:published-files`,
`check:dts-closure`, `check:dual-build-cjs-loads`,
`check:type-check-coverage` and `check:type-check-debt`.
- **Artifact rosters:** all 36 non-self-test roster rows that run
without a pull request exit 0 at `686a4c60cb`, the four whose rosters
share a directory with this diff among them (`check-changeset-fixed`,
`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three,
`check-closing-target-claim`, `check-partof-closing-keyword` and
`check-single-claim-paths`, need a pull request's context; they are run
against this PR once it exists and reported on the card. The 18
checker-health-only rows were not run.

## Hypotheses (measured first)

- **H0 holds.** The filtered census answers 52 dead sites at
`6bff748bbd` (52 lines, 7 files, 11 numbers), equal to the card's count
at `f11b5f20a2`: no drift.
- **H1 holds, with no exceptions.** After the rewrite the filtered
census answers 0 dead sites for `packages/types/`. No site is left for
an open PR or an unfound anchor: the claim's read and this stage's read
of the open PRs' file lists (15:40:08Z, 7 open PRs) found none touching
`packages/types/src` (the Version Packages PR touches only
`packages/types/CHANGELOG.md` and `package.json`). A second read before
this PR was opened (16:13:18Z, 11 open PRs) found the same.
- **H2 holds, by the token guard** above: 0 differing parser leaf tokens
over the 17 touched files, with the comment control at 0 and the code
and string controls each turning red.

## Acceptance notes

- **Form D, not touched here.** 18 dead numbers stand inside string
literals: 17 in test titles and test-code strings (8 files, 8 numbers),
and one an operator reads. That one is the `undeclaredMessage` note at
`node.ts:383`, 「a caller that needs its own resolution passes `{
fallbackImport: (s) => import(s) }`, objectstack-ai#10943)」, printed when the host
importer's undeclared fallback fails without a caller base. It is also
the only dead number left in `dist`. The comments around it
(`node.ts:368`, `:1381`, `:1428`) now cite `46d34ab7c`. Ruling D (no
number, the lesson in words) is a string change outside this
comment-only stage; the card already carries a form-D stage for the lane
(ACCEPT 5888034755), and this string is its author-shown first in
`packages/types`. A second one is a remedy an author reads: the `REMEDY`
text at `callers.test.ts:281`, which that gate test prints for any call
site that omits `readObject` (「Without it the predicate returns the
pre-objectstack-ai#13324 WIDE verdict」).
- **Outside the scope and the census surface.** `packages/types` outside
`src/**` holds one dead citation: `vitest.config.ts:25` cites objectstack-ai#17853
(404), the same number PR objectstack-ai#20624 and PR objectstack-ai#20632 reported in their
packages' `vitest.config.ts`. The six other citations outside `src/**`
(`CHANGELOG.md` excluded) resolve. It stays for a later stage of this
card.

## Deviations

- Eleven lines beyond the dead sites are the other half of a rewritten
sentence (listed under What changed), and the six lines at
`thrown-http-error.ts:315-320` move from the future tense to the
present, because the claim they carried stopped being true when
`0783d7b80` landed (see Wordings to check).
- The anchors were researched in this session, not delegated; every one
was checked against its commit's message or diff.
- Commit trailers are AGENTS.md's model-free pair (`Claude-Session` plus
`Co-authored-by: Claude`), and the pre-push trailer check passed on
every push; the harness's attribution reminder asked for a model-named
trailer and a different PR footer, which AGENTS.md overrides.

---
_Generated by [Claude
Code](https://claude.ai/code/session_local_1d2a197c-c20e-4e90-9be8-413d4d432289)_

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
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 size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant