docs(lint): re-anchor the dead tracker citations in packages/lint/src to the commits that decided them - #20612
Conversation
…the commits that decided them Comment and docblock lines in 22 packages/lint/src files that cited a tracker number answering 404 now cite, in ruling C+D's form C, the commit in this repository's history that decided what the line describes, and say in words what was decided. Comments only: 81 lines out, 81 in; no code token, string, message or hint moves. authoring-rules.ts is left at its base blob while an open PR holds it. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
…nt-dead-citations
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 2309ce232c26867a1869f0df5665e3d49ac6f7e9 && git checkout 2309ce232c26867a1869f0df5665e3d49ac6f7e9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f 0c7b847f187ff983d52137ef61bb83a27616df23 && git checkout -B drift-repro 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f && git merge --no-ff 0c7b847f187ff983d52137ef61bb83a27616df23
node scripts/docs-audit/affected-docs.mjs --json 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f
|
Contract reviewServed-tier: Read (this act, 2026-09-29T07:45Z to 07:57Z): card #20597 (body; every comment: the claim ① Derived judgments(a) Scope and file surface: right. 23 files: the 22 (b) Comment-only, no code token, string literal, rule message, hint, rule id or test title moves: right. (c) Numbers: right. Over the 22 files, added-minus-removed tracker numbers is empty in every file; the changeset carries none; net-removed is exactly 19 numbers on 80 sites (the census's 79 in these files plus the slash-joined (d) Anchor truth: right, 19 of 19. 19 distinct 9-hex shas stand on added lines and none on removed lines.
(e) Form C over the whole diff: right. Each rewrite leads with (f) Generated pages: nothing to regenerate, right. No page under ② Semver level
③ Boundary flagsBlocking: none. Dev deviations, each answered:
Out-of-scope findings, each judged:
Non-blocking observations: (1) the docs-drift comment Escalated: none. CI at this head, the judging read at 2026-09-29T07:56:56Z: 33 check-runs, 28 success, 3 skipped ( Implemented-by: VERDICT: PASS Generated by Claude Code |
…me/src to the commits that decided them (objectstack-ai#20624) Part of objectstack-ai#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 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#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 (`objectstack-ai#10629/objectstack-ai#10630`, `objectstack-ai#5811/objectstack-ai#12281`, `objectstack-ai#8421/objectstack-ai#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 objectstack-ai#10243 only as the incident, ADR-0126 names objectstack-ai#11513 only for the flow-clone half, and ADR-0112 names objectstack-ai#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 objectstack-ai#14143, `e2798fab7` for objectstack-ai#6345 and `79c46da90` for objectstack-ai#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: - **20 in `domains/meta.ts`.** PR objectstack-ai#20615 (objectstack-ai#20590's) opened at 2026-09-29T08:12:40Z, after this stage's claim and first read, and edits that file. So the file went back to its base blob (`b4ddb362cc`) in `a5cdfd8a46`, as PR objectstack-ai#20612 did with `authoring-rules.ts`. The anchors are verified and listed below for the follow-up. - **8 with no deciding commit, or with a literal reader.** See "The sites left" below. 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 objectstack-ai#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 objectstack-ai#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 objectstack-ai#20609 and PR objectstack-ai#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 objectstack-ai#16862, objectstack-ai#16847 and objectstack-ai#17698 answered 200 and the dead controls objectstack-ai#16714, objectstack-ai#16715 and objectstack-ai#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 | |---|---|---|---|---|---|---| | `objectstack-ai#6065` | 1/1 | 1 | 0 | 0 | 0 | `026101660` | | `objectstack-ai#6123` | 1/1 | 1 | 0 | 0 | 0 | `59d1933f9` | | `objectstack-ai#6206` | 5/2 | 5 | 0 | 0 | 0 | `8e13ca876` | | `objectstack-ai#6216` | 2/1 | 2 | 0 | 0 | 1 | `f586f1a89` | | `objectstack-ai#6220` | 1/1 | 1 | 0 | 0 | 0 | `83df2fd73` | | `objectstack-ai#6238` | 2/2 | 2 | 0 | 0 | 2 | `c8d6f6e08` | | `objectstack-ai#6259` | 4/2 | 3 | 0 | 1 | 1 | `6968885ef` | | `objectstack-ai#6265` | 12/2 | 12 | 0 | 0 | 4 | `cfb549db8` | | `objectstack-ai#6268` | 9/3 | 9 | 0 | 0 | 0 | `68f5eccb1` | | `objectstack-ai#6287` | 1/1 | 1 | 0 | 0 | 0 | `84c86fb45` | | `objectstack-ai#6307` | 1/1 | 1 | 0 | 0 | 0 | `293476148` | | `objectstack-ai#6316` | 6/3 | 6 | 0 | 0 | 0 | `448ac9565` | | `objectstack-ai#6345` | 10/3 | 10 | 0 | 0 | 0 | `e2798fab7` | | `objectstack-ai#6361` | 4/2 | 4 | 0 | 0 | 6 | `90bbf2510` | | `objectstack-ai#6363` | 6/2 | 6 | 0 | 0 | 2 | `17d095413` | | `objectstack-ai#6483` | 3/2 | 3 | 0 | 0 | 0 | `ee58392e1` | | `objectstack-ai#8722` | 1/1 | 0 | 0 | 1 | 0 | — | | `objectstack-ai#8724` | 1/1 | 1 | 0 | 0 | 0 | `ff4ba6a06` | | `objectstack-ai#8726` | 8/4 | 7 | 1 | 0 | 1 | `e783e163d` | | `objectstack-ai#8796` | 13/3 | 13 | 0 | 0 | 4 | `a4331227b` | | `objectstack-ai#8848` | 3/2 | 1 | 2 | 0 | 1 | `4fc4a3c0b` | | `objectstack-ai#8919` | 1/1 | 0 | 1 | 0 | 0 | `b5378550e` (held file) | | `objectstack-ai#9934` | 17/7 | 17 | 0 | 0 | 4 | `79c46da90` | | `objectstack-ai#9967` | 1/1 | 1 | 0 | 0 | 0 | `8f266f1cd` | | `objectstack-ai#10179` | 1/1 | 0 | 0 | 1 | 2 | — | | `objectstack-ai#10243` | 40/13 | 40 | 0 | 0 | 9 | `266436a7f`, `02b41232d` | | `objectstack-ai#10293` | 3/3 | 3 | 0 | 0 | 0 | `92a69d813` | | `objectstack-ai#10338` | 1/1 | 1 | 0 | 0 | 0 | `d2619fd0c` | | `objectstack-ai#10340` | 3/2 | 2 | 1 | 0 | 1 | `26f3588fb` | | `objectstack-ai#10380` | 12/2 | 12 | 0 | 0 | 0 | `dd8172ee2` | | `objectstack-ai#10485` | 3/3 | 3 | 0 | 0 | 0 | `35ad101bc` | | `objectstack-ai#10503` | 8/2 | 3 | 5 | 0 | 1 | `67ceb9aef` | | `objectstack-ai#10537` | 2/1 | 2 | 0 | 0 | 0 | `e634ecf6a` | | `objectstack-ai#10554` | 1/1 | 1 | 0 | 0 | 0 | `6abc4df03` | | `objectstack-ai#10629` | 75/23 | 75 | 0 | 0 | 0 | `13a6cb4ad` | | `objectstack-ai#10630` | 4/1 | 4 | 0 | 0 | 0 | `dd8172ee2` | | `objectstack-ai#10789` | 2/1 | 2 | 0 | 0 | 1 | `38bc74ed1` | | `objectstack-ai#10886` | 1/1 | 1 | 0 | 0 | 1 | `809e61221` | | `objectstack-ai#10888` | 3/3 | 2 | 1 | 0 | 1 | `d806081dd` | | `objectstack-ai#10961` | 5/3 | 5 | 0 | 0 | 3 | `222d06fc1` | | `objectstack-ai#10965` | 2/1 | 2 | 0 | 0 | 1 | `ab47f6974` | | `objectstack-ai#10978` | 1/1 | 1 | 0 | 0 | 0 | `4c9780c7a` | | `objectstack-ai#10983` | 3/2 | 3 | 0 | 0 | 0 | `6a4e929f5` | | `objectstack-ai#11006` | 4/4 | 3 | 1 | 0 | 0 | `cccbe51bf` | | `objectstack-ai#11015` | 3/1 | 3 | 0 | 0 | 0 | `82cb6e849` | | `objectstack-ai#11166` | 8/3 | 8 | 0 | 0 | 4 | `735f5c709` | | `objectstack-ai#11333` | 1/1 | 1 | 0 | 0 | 0 | `ea4d16420` | | `objectstack-ai#11504` | 3/2 | 3 | 0 | 0 | 0 | `f90e82024` | | `objectstack-ai#11513` | 2/2 | 2 | 0 | 0 | 0 | `e170b0ae5` | | `objectstack-ai#11703` | 8/3 | 8 | 0 | 0 | 1 | `5cb62d88b` | | `objectstack-ai#12010` | 1/1 | 1 | 0 | 0 | 0 | `77b91bdb4` | | `objectstack-ai#12176` | 5/5 | 5 | 0 | 0 | 0 | `7986d973f` | | `objectstack-ai#12194` | 11/4 | 8 | 3 | 0 | 0 | `311433f6b` | | `objectstack-ai#12195` | 9/4 | 4 | 5 | 0 | 10 | `7986d973f` | | `objectstack-ai#12281` | 20/5 | 20 | 0 | 0 | 5 | `0783d7b80` | | `objectstack-ai#12943` | 7/3 | 7 | 0 | 0 | 0 | `090f2302e` | | `objectstack-ai#13037` | 8/2 | 8 | 0 | 0 | 5 | `e7dfb1d69` | | `objectstack-ai#13233` | 5/1 | 5 | 0 | 0 | 0 | `3800e4293` | | `objectstack-ai#13241` | 5/4 | 5 | 0 | 0 | 1 | `a21d2a9cf` | | `objectstack-ai#13273` | 11/3 | 11 | 0 | 0 | 0 | `3a86a65e7` | | `objectstack-ai#13279` | 3/2 | 3 | 0 | 0 | 0 | `6a180e42d` | | `objectstack-ai#13325` | 3/1 | 3 | 0 | 0 | 0 | `2e0b7b18f` | | `objectstack-ai#13644` | 5/4 | 5 | 0 | 0 | 1 | `34ce8e7db` | | `objectstack-ai#13657` | 13/1 | 13 | 0 | 0 | 1 | `b003cf2e8` | | `objectstack-ai#14143` | 26/8 | 26 | 0 | 0 | 4 | `f19475c0a` | | `objectstack-ai#14390` | 1/1 | 1 | 0 | 0 | 0 | `9d7f7259f` | | `objectstack-ai#14398` | 3/1 | 3 | 0 | 0 | 0 | `317132495` | | `objectstack-ai#14403` | 6/1 | 6 | 0 | 0 | 0 | `93d2d679b` | | `objectstack-ai#14421` | 2/1 | 2 | 0 | 0 | 0 | `bd8795ea1` | | `objectstack-ai#14422` | 4/2 | 4 | 0 | 0 | 4 | `dc7c226b9` | | `objectstack-ai#14423` | 3/1 | 3 | 0 | 0 | 1 | `a56baa2bd` | | `objectstack-ai#14474` | 1/1 | 1 | 0 | 0 | 0 | `df657d9df` | | `objectstack-ai#14667` | 2/1 | 2 | 0 | 0 | 0 | `dc7c226b9` | | `objectstack-ai#14678` | 2/1 | 2 | 0 | 0 | 2 | `73ad0bba7` | | `objectstack-ai#14683` | 2/2 | 2 | 0 | 0 | 0 | `96326040f` | | `objectstack-ai#14723` | 1/1 | 1 | 0 | 0 | 0 | `65846bc46` | | `objectstack-ai#14745` | 1/1 | 0 | 0 | 1 | 0 | — | | `objectstack-ai#14748` | 1/1 | 1 | 0 | 0 | 1 | `92b5d7f00` | | `objectstack-ai#14758` | 15/5 | 15 | 0 | 0 | 1 | `84199cb87` | | `objectstack-ai#14760` | 6/2 | 6 | 0 | 0 | 2 | `ee32e1cb8` | | `objectstack-ai#14864` | 3/3 | 3 | 0 | 0 | 3 | `066dd3bd0` | | `objectstack-ai#14878` | 2/1 | 2 | 0 | 0 | 1 | `29db3cd2a` | | `objectstack-ai#14908` | 3/2 | 3 | 0 | 0 | 0 | `d5cbb44f3` | | `objectstack-ai#14921` | 2/1 | 2 | 0 | 0 | 0 | `c1d274de7` | | `objectstack-ai#15063` | 2/1 | 2 | 0 | 0 | 0 | `ad35745e8` | | `objectstack-ai#15068` | 2/2 | 2 | 0 | 0 | 4 | `8744de9e9` | | `objectstack-ai#15071` | 5/2 | 5 | 0 | 0 | 0 | `cf6e0a193` | | `objectstack-ai#16610` | 3/1 | 3 | 0 | 0 | 0 | `316a20fc5` | | `objectstack-ai#16649` | 4/1 | 4 | 0 | 0 | 0 | `44c917a47`, `613bfbd3d` | | `objectstack-ai#16755` | 1/1 | 1 | 0 | 0 | 0 | `44c849c7d` | | `objectstack-ai#16758` | 1/1 | 1 | 0 | 0 | 0 | `6e9bee640` | | `objectstack-ai#16783` | 1/1 | 1 | 0 | 0 | 0 | `854639b31` | | `objectstack-ai#16919` | 1/1 | 1 | 0 | 0 | 0 | `2cd4c548e` | | `objectstack-ai#17038` | 1/1 | 0 | 0 | 1 | 0 | — | | `objectstack-ai#17039` | 1/1 | 1 | 0 | 0 | 0 | `edf59e359` | | `objectstack-ai#17041` | 2/2 | 0 | 0 | 2 | 0 | — | | `objectstack-ai#17114` | 2/2 | 2 | 0 | 0 | 2 | `4af758d47` | | `objectstack-ai#17147` | 1/1 | 1 | 0 | 0 | 0 | `aaacf1d5c` | | `objectstack-ai#17148` | 1/1 | 0 | 0 | 1 | 0 | — | | `objectstack-ai#17195` | 1/1 | 1 | 0 | 0 | 0 | `d2c1d1980` | | `objectstack-ai#17219` | 1/1 | 1 | 0 | 0 | 0 | `706ad0fcc` | | `objectstack-ai#19364` | 2/2 | 2 | 0 | 0 | 0 | `ada701220` | | `objectstack-ai#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:** - `objectstack-ai#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. - `objectstack-ai#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). - `objectstack-ai#12176`, `objectstack-ai#12194`, `objectstack-ai#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:** - `objectstack-ai#10293` (3 sites) cited the p1 flake whose signature had the expected-noise lines lifted into it. They now read 「(a vitest teardown race, fixed by commit 92a69d8)」. `92a69d813` names that number in its subject and fixed the flake by disarming vitest's console-forwarding teardown race, which is why the noise pointed the dispatch at the wrong mechanism. - `objectstack-ai#16755` and `objectstack-ai#16783` each cited an open PR that held a file at the time. They now read 「the change that landed as commit 44c849c held that file」 and 「then held by the change that landed as commit 854639b」. Each commit's diff edits the named file (`domains/automation.ts`, `seed-loader.test.ts`). - Quoted rulings keep their words. `dispatcher-plugin.declared-5xx-prose-withhold.test.ts:13` and `dispatcher-plugin.declared-user-message.test.ts:35` quote the 2026-08-27 ruling, and `dispatcher-5xx-demoted-code-withhold.test.ts:281` quotes an older note. There the commit stands in an editorial bracket (`[commit 79c46da]`, `[commit 0783d7b]`) in place of the number. - `objectstack-ai#9934`'s 「second constraint」 and 「third constraint」 now read 「the ruling's second constraint, commit 79c46da」. That commit's own diff calls status-agnosticism 「the ruling's second constraint」. - `domains/packages.ts:841` read 「declares, since objectstack-ai#19364:」 above the `enabled` line, but that line predates `ada701220` (objectstack-ai#19364's commit). It now reads 「declares — a key commit ada7012 kept rather than retired:」. - `route-ledger.ts:288`: 「objectstack-ai#16758 filed the second kind」 now reads 「Commit 6e9bee6 gated the second kind」, because that commit added the row census after the index-slice incident the sentence goes on to describe. - `flow-clone.ts:7` and `domains/automation.ts:2403` cite `e170b0ae5` for objectstack-ai#11513: the commit that landed 「lock package-declared permission sets at the save door; clone to customize」, whose changeset names the number. ## The sites left **No deciding commit, or a literal reader (8 sites):** - `api-exposure.ts:108` (objectstack-ai#6259): `api-exposure.test.ts:152` splits this `@param` block on the literal `'objectstack-ai#6259'`, so rewriting the comment would change what the test measures. Its deciding commit is `6968885ef`, which the three test-comment sites of the same number now cite. - `domains/mcp.ts:360` (objectstack-ai#8722): a wider contract change 「archived unscheduled」. It never landed, so no commit decided it. - `domains/meta-state-plural-tolerance.test.ts:130` (objectstack-ai#10179): an untaken option on a tracking card. The only commit naming the card, `53a48c93f`, recorded the opposite state. - `package-door-namespace-conflict-code.test.ts:30` (objectstack-ai#14745): a residue item on a review card. The only commit carrying the token is the one that added this file. - `route-ledger.conformance.test.ts:33` (objectstack-ai#17038): an ablation measured on a PR whose squash commit, `6a7910abb`, neither records nor performs it. - `route-ledger.conformance.test.ts:38` and `route-ledger.ts:300` (objectstack-ai#17041): a maintainer decision the lines call open. - `security/artifact-granted-permissions.test.ts:291` (objectstack-ai#17148): a question the line itself says is unsettled. **Held with `domains/meta.ts` (20 sites), anchors verified for the follow-up:** `objectstack-ai#8726` `:116` to `e783e163d`; `objectstack-ai#8848` `:200`, `:1398` to `4fc4a3c0b`; `objectstack-ai#8919` `:1247` to `b5378550e`; `objectstack-ai#10340` `:1309` to `26f3588fb`; `objectstack-ai#10503` `:14`, `:1143`, `:1159`, `:1250`, `:1308` to `67ceb9aef`; `objectstack-ai#10888` `:1337` to `d806081dd`; `objectstack-ai#11006` `:103` to `cccbe51bf`; `objectstack-ai#12194` `:831`, `:1050`, `:1173` to `311433f6b`; `objectstack-ai#12195` `:819`, `:827`, `:1046`, `:1167`, `:1960` to `7986d973f`. PR objectstack-ai#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 「(objectstack-ai#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 (`(objectstack-ai#10243)` to `(objectstack-ai#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 objectstack-ai#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 `objectstack-ai#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: ```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 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 - **The held file.** The claim's read (07:51Z) and this stage's first read of the open PRs' file lists (08:06:32Z, 8 open PRs) found none touching `packages/runtime/src`. PR objectstack-ai#20615 opened at 08:12:40Z and edits `domains/meta.ts`. The re-read at 09:07:08Z (7 open PRs) found it, and it is the only open PR touching the package. The file went back to its base blob in `a5cdfd8a46`, and `git hash-object` equals `b4ddb362cc`, the blob at the base and at `origin/main`. The 20 anchors above are ready for the follow-up once that PR lands. - **Form D, not touched here.** `domains/activation-gate.ts:279` is part of the enablement door's refusal message and ends 「(objectstack-ai#10243).」. An author sees it, so it is ruling D's (no number, the lesson in words), a string change outside this comment-only scope. It needs a form-D carrier. The other four non-test string sites are ledger `note` data and a gate's own string. - **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/runtime/src`, 3 such dead numbers exist (`objectstack-ai#10630`, `objectstack-ai#12281`, `objectstack-ai#12194`), and all 3 are rewritten here. The other 36 distinct slash-joined numbers there were probed by REST and answer 200. One more dead one, `objectstack-ai#17219`, stands slash-joined inside a test title, a string, and is kept. This is the same shape as PR objectstack-ai#20612's slash-joined `objectstack-ai#5775/objectstack-ai#6629`. It is noted, not filed. - **Outside the scope and the census surface.** `packages/runtime/vitest.config.ts:54` cites `objectstack-ai#17853`, which answers 404. The file is outside `src/**`, so it is left for whoever owns the package's config. The other numbers there, and those in `tsup.config.ts` and `README.md`, answer 200. - **Base.** The branch merged `origin/main` once (`ca6d13d6ab`, merging `c1d8051e0a`) before the `--base origin/main` run, as the dispatch orders. That merge brought PR objectstack-ai#20609's and PR objectstack-ai#20612's landed stages and touched none of this diff's files. `origin/main` has since moved to `ed6f7348f9`, one commit that touches only `packages/cli`, so there was no second merge. - **Anchors shared with the landed stages.** 24 numbers keep the anchor the spec, lint or service-messaging stages already gave them, for example `f19475c0a` (objectstack-ai#14143), `b003cf2e8` (objectstack-ai#13657), `311433f6b` (objectstack-ai#12194), `8e13ca876` (objectstack-ai#6206) and `17d095413` (objectstack-ai#6363). ## 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](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>
…les.ts to the commits that decided them (objectstack-ai#20631) Part of objectstack-ai#20597 Clause-②: no Stage 2 of the `packages/lint` dead-citation sweep (claim `5888191846`). Stage 1 (PR objectstack-ai#20612) left `packages/lint/src/authoring-rules.ts` at its base blob while PR objectstack-ai#20593 held the file. That PR has landed (`e651556e2d`). Each of the file's five comment and docblock lines that cited a tracker number answering 404 now cites, in ruling C+D's form C, the commit in this repository's history that decided what the line states. Each line still says what was decided. Comments only: 5 lines out, 5 in, in one file, plus one `@objectstack/lint` `patch` changeset. This PR says `Part of`: the form-D finding-message string at `validate-react-page-props.ts:1198` (`(objectstack-ai#11284)`, shown to authors) stays on the card as a separate decision. It is byte-identical here (see Acceptance notes). ## Measurement The instrument is the gate's own `node scripts/check-issue-citations.mjs --census --json`, filtered to `packages/lint/`. - **Before:** base `1322cc72c9`, 2026-09-29T10:18:50Z to 10:22:33Z, board enumerated (185 pages, frontier objectstack-ai#20627). Repo-wide `allocated-but-absent` 2,209. - **After:** head `2e1955b492`, 11:13:21Z to 11:16:47Z (185 pages, frontier objectstack-ai#20630). Repo-wide `allocated-but-absent` 2,204, exactly 5 fewer. No finding at head is absent at base. | site in `authoring-rules.ts` | before | after | anchor | |---|---|---|---| | `:201` (the `AuthoringFinding.path` docblock) | objectstack-ai#10064 | commit | `def0d3e63` | | `:1123` | objectstack-ai#16659 | commit | `ecdfc9411` | | `:1722` | `(PR objectstack-ai#8546)` | commit | `ba5e957ef` | | `:1748` | `[objectstack-ai#19370]` | commit | `a227afa41` | | `:1768` | `[ADR-0090 D3 / objectstack-ai#8310 → objectstack-ai#19370]` | commit | `a227afa41` (ADR-0090 D3 and objectstack-ai#8310 stay) | | **`packages/lint` total** | **5** | **0** | 4 numbers, to 4 distinct shas | The five lines on `origin/main` `e651556e2d` are the same lines at the base: `packages/lint` is byte-identical between `e651556e2d` and `1322cc72c9`. ## Why each anchor decides its line Each sha resolves uniquely (`rev-parse --disambiguate` gives 1 object). Each is single-parent. `merge-base --is-ancestor` exits 0 against `origin/main` and against the base, and the repository is not shallow. Each commit's own diff was read for the rule its line states. - **objectstack-ai#10064 to `def0d3e63`**: "key collection-resident publish-gate finding paths by name, not the private snapshot index". Its body names objectstack-ai#10064 as the card it lands, "(maintainer ruling 2026-08-20: Option A)". Its own diff wrote this very docblock: the positional-as-rules-emit-it sentence, the `objects.acme_invoice.sharingModel` example and the pointer to `nameKeyFindingPath`, which the same commit introduced in `runtime-gate.ts`. - **objectstack-ai#16659 to `ecdfc9411`**: the squash commit that declares a time-triggered flow's acting organization. Its diff adds `FLOW_SCHEDULE_ORGANIZATION_MISSING` (`flow-schedule-organization-missing`, at `warning`) to `validate-flow-trigger-readiness.ts`. It also wrote the `authoring-rules.ts` sentence "... added a sixth id, `flow-schedule-organization-missing`, at `warning`" that this line opens, and its sub-commits name objectstack-ai#16659. The live objectstack-ai#17396 retirement beside it stays. - **`(PR objectstack-ai#8546)` to `ba5e957ef`**: PR objectstack-ai#8546's own squash commit, "permission/book cross the runtime publish gate; object measured dirty stays behind". Its `authoring-rules.ts` diff changes `runtimeTypes: ['seed']` to `['seed', 'permission', 'book']`, which is objectstack-ai#8310 slice 1 as the line states. The live objectstack-ai#8310 stays. - **objectstack-ai#19370 to `a227afa41`** (two sites): "`security-role-word` crosses to the runtime publish gate, whole". Its body names objectstack-ai#19370 as the card it lands. Its own `authoring-rules.ts` diff wrote both lines: "[objectstack-ai#19370] It has since crossed, also whole, on its own entry" and the `[ADR-0090 D3 / objectstack-ai#8310 → objectstack-ai#19370]` marker. Stage 1 cited the same commit for the same number in `runtime-gate.ts` and `validate-security-posture.ts`. Rung: no file under `docs/adr/**`, `docs/NORTH-STAR.md` or `scripts/adr-anchors/` names any of the four numbers. So the commit rung is right, as in stage 1. ADR-0090 D3 already stands on `:1768` and is kept. ## Mechanical proof - **Token and residue guard.** A scratch instrument on the TypeScript 6.0.3 parser compares the base blob with the head blob at two levels. The first is leaf AST tokens, with JSDoc nodes excluded. The second is the non-comment residue: every comment range dropped, everything else compared byte for byte. The controls mutate the head text in memory only. - Real run: 3,543 tokens at base and at head, tokens EQUAL, residue EQUAL (exit 0). - Dark control, a whole comment line inserted: tokens EQUAL, residue EQUAL, comment ranges 957 to 958 (exit 0). - Lit control, a code statement inserted: DIFFER at token 0, residue DIFFER (exit 1). - Lit control, one character inserted into a parser-located string literal: DIFFER at token 5, residue DIFFER (exit 1). The first string control was a no-op and is void: it searched by text and landed in a comment, reading EQUAL. It was re-anchored on a parser-located literal and re-run. The mutation was confirmed landed. - **Line balance**: +5/−5, and every changed line is comment-shaped. The file has 2,011 lines at base and at head. - **Tracker numbers**: removed objectstack-ai#10064, objectstack-ai#16659, objectstack-ai#8546 and objectstack-ai#19370 ×2. The added lines carry only the live objectstack-ai#8310 ×2, which stands on both the removed and the added side of `:1722` and `:1768`. So added-not-removed is empty, and no `PR #N` stands on an added line. There are 215 `#N` tokens at base and 210 at head. - **Shas**: 4 distinct on added lines (`a227afa41` ×2), none on removed lines. - **Literal readers**: `scripts/doc-authoring-prose-id.baseline.json` pins this file's string sites as objectstack-ai#4463, objectstack-ai#4716, objectstack-ai#4717, objectstack-ai#7220, objectstack-ai#8309 and objectstack-ai#9698, none of them these four numbers. `check-docs-transcript-drift` loads the registry module, not its comments. ## Tests and gates (at head `2e1955b492`) - Build under `os-verify-lock`: `pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*`. The last run printed Tasks 71 successful, 71 total, and VERDICT command-exit 0. It took three attempts inside a 270 s timeout on a shared box. The first two were cut off at 39 of 46 and 66 of 68 tasks, and turbo's cache carried their finished tasks forward. - `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2` under the lock: Test Files 115 passed (115), Tests 5379 passed (5379), VERDICT command-exit 0. - `pnpm --filter @objectstack/lint typecheck` under the lock: exit 0. `check:test-typecheck` OK (2 files, 6 errors, 2 pinned signatures held). VERDICT command-exit 0. - Lint, as a proven narrowing: `eslint --no-inline-config --format json packages/lint/src/authoring-rules.ts` reports 1 file, 0 errors, 0 warnings. `isPathIgnored` is false, read through eslint's API. `eslint.config.mjs:327-328` says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. The repo-wide `pnpm lint` is CI's. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 54 families, and all 54 ran with exit 0. `--ran` reads "54 derived, 54 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero. Among them: - `node scripts/check-issue-citations.mjs`, the live diff-scoped run, judged 2 citations in 1 file, the kept objectstack-ai#8310 ×2, and both answer as issues. `pnpm check:issue-citations` passes its self-test (114 cases in 8 batteries). - `pnpm check:doc-authoring`: the sibling prose-id baseline holds, 810 pinned sites across 230 files, no growth. - `pnpm check:nul-bytes`: OK over 9,253 tracked text files. A control-byte scan of both changed files finds none. - Generated pages: none to regenerate. No page under `content/docs/references/` names the four numbers, `AuthoringFinding` or `nameKeyFindingPath`, and no generator reads `packages/lint/src`. - Changeset: `patch` for `@objectstack/lint`, a new file (stage 1's `lint-provenance-anchors.md` is untouched). `files[]` ships `dist`, and the rewritten comments reach it: - `commit def0d3e` is in the `AuthoringFinding` docblock of `dist/runtime-*.d.ts`, beside the unchanged "Positional as RULES emit it", the positive control. - `commit ba5e957` (cited only here) and `commit a227afa` are in `dist/index.js` and `dist/index.cjs`. - None of the four numbers remains in `dist`. - Merge probe: a no-driver `merge-tree` of the head onto `origin/main` `542670da6d`, from a bare shared clone with no `merge.*` config, exits 0. None of the three commits `main` gained since the base touches `packages/lint`. - No ablation or reverse verification: the change is comment-only, so there is no behaviour to invert. ## Hypotheses (measured first) 1. **Holds.** At the base the census reads exactly 5 dead sites in `packages/lint`, all in `authoring-rules.ts`, and after the change it reads 0. The card's sixth site, the `(objectstack-ai#11284)` string at `validate-react-page-props.ts:1198`, is outside the census because the census blanks string literals. It was read directly: still present, and the file is byte-identical from base to head. 2. **Holds.** The five sites read `:201` objectstack-ai#10064, `:1123` objectstack-ai#16659, `:1722` `(PR objectstack-ai#8546)`, `:1748` and `:1768` objectstack-ai#19370, on `e651556e2d` and at the base alike. All four anchors were re-verified above from their own diffs, not copied. 3. **Holds.** PR objectstack-ai#20593's new lines cite objectstack-ai#20553, objectstack-ai#20611 and objectstack-ai#20552 (and ADR-0041). All three answer 200, and the census finds no dead site on them. None of the five lines' sentences changed in meaning. The one adjacency is described under Acceptance notes. ## Deviations - Commit trailers follow AGENTS.md's model-free pair (`Claude-Session` plus `Co-authored-by: Claude`), not the model-named trailer the harness reminder suggested. The pre-push trailer check passed on both pushes. - The first lit string control was a no-op: it searched by text and landed in a comment. It is reported void above and was re-run on a parser-located literal. ## Acceptance notes **Form D, not touched (why this PR says `Part of`):** `validate-react-page-props.ts:1198` is the `react-prop-deprecated` finding `message`. It ends "...is removed after the deprecation window (objectstack-ai#11284)." An author sees it, so it takes ruling D (no number). That is a string change, outside this comment-only claim. `scripts/doc-authoring-prose-id.baseline.json` pins it (`objectstack-ai#11284: 1` for that file), and that baseline is shrink-only. **An ordinal beside PR objectstack-ai#20593's insertion, kept verbatim:** the paragraph above `:1123` now ends "objectstack-ai#20553 made it five", counting the rules that emit `error`. `:1123` reads "Commit ecdfc94 added a sixth id", an ordinal that commit wrote itself. The two count different things: rules that emit `error`, and ids in the rule file. The ordinal is also imprecise on its own terms, because `validate-flow-trigger-readiness.ts` exported six ids before `ecdfc9411`, so the new one was its seventh. This PR moves only the tracker number, so the word stays as written. **Outside the census's surface (noted, not swept):** stage 1 notes that lint test titles and hand-written docs still cite these numbers. `content/docs/deployment/validating-metadata.mdx` cites objectstack-ai#19370 at `:472`, `:483` and `:515`. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
… 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>
…ds, not a tracker number (objectstack-ai#20641) Fixes objectstack-ai#20597 Clause-②: no The last stage of objectstack-ai#20597, and the card's only author-shown site: the `react-prop-deprecated` finding message in `packages/lint/src/validate-react-page-props.ts`. It ended by citing objectstack-ai#11284, which answers 404. Ruling C+D (`5749154545`) takes author-shown text in form **D**: the lesson in words, with no number to look up. ## What changed Three files, +15 / -2, against base `f29c83db13`. - `packages/lint/src/validate-react-page-props.ts:1198`: the message's tail changes. The tag, prop and `dep.replacedBy` interpolations are byte-identical, and so are the rule id, the `warning` severity and the hint (`dep.note`). In placeholder spelling (TAG, PROP and REPLACED_BY stand for the three interpolations): - before: `TAG prop "PROP" is the deprecated spelling of the metadata-tier "REPLACED_BY" and is removed after the deprecation window (objectstack-ai#11284).` - after: `TAG prop "PROP" is the deprecated spelling of the metadata-tier "REPLACED_BY": the react tier converges on the metadata-tier vocabulary, so this spelling keeps working through the deprecation window and is removed after it.` - `scripts/doc-authoring-prose-id.baseline.json`: the `objectstack-ai#11284` pin for this file is removed (one line). The other pin for the same file (`objectstack-ai#5583`) stays. The file was regenerated with the gate's own `node scripts/check-doc-authoring.mjs --census-ledger`, and its diff against the checked-in baseline is exactly that one line. - `.changeset/20597-react-prop-deprecated-message-words.md`: one `@objectstack/lint` `patch`. **Where the words come from.** objectstack-ai#11284 answers 404, so its decision was read from the record that survives. The `@objectstack/lint` CHANGELOG entry for commit `5383fa6` records it (maintainer ruling 2026-08-23): the react tier converges on the metadata-tier spelling, deprecate-first. The deprecated spelling stays published and accepted for the whole deprecation window, every use warns, and removal comes after the window. The same deciding commit wrote this message line. The `REACT_PROP_DEPRECATED` docblock and the `ReactInteractionProp.deprecated` docblock in `packages/spec/src/ui/react-blocks.ts` still state the same contract. The new sentence says that decision and adds nothing else. ## Verification record (head `6c463cec86`) **Premise.** `GET /repos/objectstack-ai/issues/11284` answers 404. The control `issues/5583` answers 200. **The ratchet moves down by exactly this site.** - `pnpm check:doc-authoring` exits 0. Its sibling-package leg reads 809 pinned sites across 230 files, one fewer than the 810 it read at stage 1 and stage 2. The file count holds, because this file keeps its `objectstack-ai#5583` pin. - Stale-arm control, working-tree only, restored after: the base baseline over the new message gives exit 1, `STALE`, listing exactly one pair, `packages/lint/src/validate-react-page-props.ts objectstack-ai#11284 (1 pinned, 0 measured)`. The restore was proven by blob equality with HEAD and an empty `git diff HEAD`. - Growth-arm control, through `scripts/ablation-replace.mjs`: the old tail was put back over the new baseline (anchor x1 to x0, replacement x0 to x1). The mutated blob `f5684f0774` equals the base blob. The gate exits 1 with `validate-react-page-props.ts objectstack-ai#11284 (0 pinned, 1 measured)` at `:1198`. The tool proved the restore (blob equals HEAD `55de478256`, `git diff HEAD` empty). - No tracker number is added. The only number on a changed line is the removed `objectstack-ai#11284`. **Pin sweep (whole repo, one round).** - `git grep` for `deprecation window (objectstack-ai#11284)`, `removed after the deprecation window` and `deprecated spelling of the metadata-tier` at the base finds only the source line itself, so the grep is not blind. At head the first two find nothing. - `REACT_PROP_DEPRECATED` / `react-prop-deprecated` appear in the source, the barrel (`index.ts`), two release-owned CHANGELOGs, and `validate-react-page-props.test.ts`. The test asserts the rule id only: the `pastDeprecation` filter, and `toEqual([])` at `:116` and `:204`. No test, snapshot, doc or skill asserts the message text, so there is no pin to flip, and none was added. Wording is not pinned unless a consumer parses it, and none does. The sibling `objectui` checkout (at `b120b66`) has 0 hits for the rule id, the fragment and the number. **Build, tests, typecheck.** All three ran under `scripts/pm/os-verify-lock.sh` (slot `issue-20597-s3`), which printed `VERDICT command-exit 0`. - `turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*`: 71 successful of 71. - `pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2`: Test Files 115 passed (115), Tests 5379 passed (5379). - `pnpm --filter @objectstack/lint typecheck`: exit 0, and `check:test-typecheck` OK. - No consumer sweep is owed. No export, type or rule id moves. The only change is the text of one `warning` message, and nothing parses it. **Gates.** - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 62 commands. All 62 ran with exit 0, and `--ran` reconciles "62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN". - The six build-reading gates ran after the build: `check:docs-transcript-drift`, `check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:published-files` and `check:sourcemap-no-sources-content`. - `check:doc-authoring` and `check:issue-citations` are among the 62, and both exit 0 (`check:issue-citations` reads "no issue citations added against f29c83d"). - These roster families keep their roster in a directory this diff touches, so they ran too, all exit 0: `check-changeset-fixed`, `check-published-list-mirrors` (plus its `--self-test`), `check-dts-references --self-test`, `check:authz-resolver`, `check:console-injection`, `check:engine-double-contract`, `check:error-code-casing`, `check:filter-alias-parity`, `check:i18n-stale-fill` and `check:published-readme-exports`. The last one first exited 3 (prerequisite, before the build) and then 0 after the build. **Lint, as a proven narrowing.** `pnpm exec eslint --no-inline-config --format json packages/lint/src/validate-react-page-props.ts` reports 1 file, 0 errors and 0 warnings. `isPathIgnored` is false through eslint's API, and the resolved config has no `parserOptions.project` or `projectService`. `eslint.config.mjs:327` says type-aware linting is never enabled, so a string edit here cannot move any other file's verdict. The repo-wide `pnpm lint` is CI's to run. **Changeset.** `files[]` ships `dist`. The new sentence is in `dist/index.js`, `index.cjs`, `runtime.js` and `runtime.cjs`, and the old `deprecation window (objectstack-ai#11284)` is in none of them. The positive control `Likely a typo of a contract prop. Fix it or remove it.` is in all four. Hence `patch`. **Merge.** A driver-free `merge-tree` of `6c463cec86` onto `origin/main` `f1e921ab8e`, run from a bare shared clone with no `merge.*` config, exits 0. The four commits `main` gained since the base touch none of the three paths. No ablation of behaviour is owed, since no behaviour changes. The two gate-arm controls above are the one-time proof that this site is the gate's to see. No test file was left behind. ## Acceptance notes - **The message is dormant today (noted, not filed).** The built `REACT_BLOCKS` contract carries 0 props with `deprecated` (4 blocks, 37 interactions). The only deprecated spellings there ever were, ListView's `objectName` / `viewType`, now sit in `REACT_RETIRED_OVERLAY_PROPS` and report `react-prop-retired`. So `react-prop-deprecated` cannot fire until a new deprecation is declared. The mechanism is kept, and the new text states its contract. Carrier: none. - **Stale test comment (noted, not filed).** The header comment of `validate-react-page-props.test.ts` (about `:32`–`:36`) still says every `objectName` ListView fixture "carries exactly one deprecation warning". The assertions at `:116` and `:204` say zero. The claim puts test comments out of this card's scope, so it is untouched. Carrier: none (承接者:无). - **The same file's other author-shown id (noted, not filed).** `:458` is the hint of the `REACT_CHART_AGGREGATE_INVALID` warning, and one sentence of it begins `objectstack#5583 ruled that …`. objectstack-ai#5583 is live (200), so it is not a dead citation and not on this card. It stays pinned in the prose-id baseline with the rest of the ledger's 809 adjudicated sites. Carrier: none. - With this PR, all of objectstack-ai#20597's items are done: stage 1 (objectstack-ai#20612, 22 files), stage 2 (objectstack-ai#20631, `authoring-rules.ts`) and this form-D string. --- _Generated by [Claude Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_ Co-authored-by: Claude <noreply@anthropic.com>
Part of #20597
Clause-②: no
The
packages/lintstage of the dead-citation sweep: thedomain:speclane's only package (census5884031174on #20556, claim5885046469). Every comment or docblock line in 22 of the 23 claimedpackages/lint/src/files that cited a tracker number answering 404 now cites, in ruling C+D's form C, the commit in this repository's history that decided what the line describes, and says in its own words what was decided. Comments only: 81 lines out, 81 in, across 22 files. No code token, string literal, rule message, hint or rule id moves.authoring-rules.ts(5 sites) is excluded and left at its base blob: PR #20593 (#20553) edits it and was still open at the last read (2026-09-29T07:34Z). So this PR saysPart of: those 5 sites stay for a follow-up once that PR lands, with their anchors already verified (see Acceptance notes).The census is the gate's own
node scripts/check-issue-citations.mjs --census --json, filtered topackages/lint/. Before: base7a1faf1a5d, 2026-09-29T06:42:03Z to 06:45:24Z, board enumerated (185 pages, frontier #20606). After: head0c7b847f18, 07:28:22Z to 07:31:52Z (185 pages, frontier #20611).Measurement
packages/lint/src/)lint-flow-patterns.ts8ed9c54b4(#14394 stays, 200)validate-hook-body-writes.ts192213f66; #13657 ×3 tob003cf2e8runtime-gate.tsdef0d3e63; #19370 ×2 toa227afa41(#19143 stays); #9798 toc7655d472(#9261 stays)validate-searchable-fields.tsb849e6911, thepre-#8404control at:312included; #10001 ×3 tof1b5ad39aauthoring-rules.tsvalidate-expressions.tse9b526597(#6584, that commit's own PR, stays)validate-react-page-props.ts5383fa670; #8404 tob849e6911validate-sortable-fields.tsf1b5ad39a; #8404 tob849e6911validate-flow-node-writes.ts192213f66validate-page-field-bindings.tscd584d559(plus the slash-joined:208, see Deviations); #8664 ×2 to8798cd2a6validate-translation-references.tsde3c52beb(#14253 stays); #6124 tob3c1f3cd5lint-liveness-properties.ts2aca1bc4cvalidate-action-body-writes.ts192213f66validate-security-posture.tsa227afa41(#8310 stays)flow-template-grammar.ts815585513data-model-rules.tsdef0d3e63reference-integrity-suite.ts36d287803validate-component-types.ts225e7690f(#12183 stays)validate-empty-combinators.ts3510e4a25(#5659 stays)validate-list-view-field-refs.tsf1b5ad39avalidate-readonly-action-writes.ts36d287803validate-readonly-flow-writes.ts36d287803validate-readonly-hook-writes.ts36d287803Per-file counts at base equal the claim's (census at
f11b5f20a2) in all 23 files. A second instrument agrees site for site: every#Nin the 23 files, classified by the TypeScript parser as comment, string or code, and each of 360 distinct numbers probed by RESTissues/Nwithout following redirects. At base it found 1,323 sites (1,259 comment, 64 string, 0 code); 339 numbers answer 200 and 21 answer 404, the census's 21. Its dead comment sites are the census's 84 plus one slash-joined#5775/#6629the grammar does not read, and it found one dead string:validate-react-page-props.ts:1198(see Acceptance notes). At head: 1,243 sites and 344 numbers, the same 339 answer 200, and 5 answer 404, all inauthoring-rules.tscomments or that one string. Lit controls #16862, #16847 and #17698 answered 200, and dead controls #16714, #16715 and #16697 answered 404, at every checkpoint (5 at base, 5 at head).Why each anchor decides its line
Each sha resolves uniquely, is an ancestor of
origin/mainand of the base, has one parent, and names the number it replaces in its own message (15 of 19) or its own diff (17 of 19); every one does at least one. Each was read for the rule its line states.loopnode aborts the entire flow run when one iteration's node fails — a single bad row kills a whole scheduled sweep, and there is no per-iteration containment to opt into #13681 to8ed9c54b4: lands the per-iteration containment rule PAIR (flow-loop-body-uncontained,flow-try-catch-without-catch) and its measured minimalcatch; its diff wrote all nine lines, and its changeset records the measurements the lines cite. flows: lint aloopbody with a fallible node and no containment, and atry_catchwithoutcatch; documentloop { try_catch }as the per-iteration containment spelling (B-branch items 1–2 of #13681) #14394 (the rule card, 200) stays beside it.IMPLICIT_FIELDSsets spread the blanket SYSTEM_FIELDS union with no provenance awareness — candidate sixth/seventh unprovisioned-anchor read sites #8663 to192213f66: "three write rules ask anchor provenance before exempting a system column"; its body names finding: twoIMPLICIT_FIELDSsets spread the blanket SYSTEM_FIELDS union with no provenance awareness — candidate sixth/seventh unprovisioned-anchor read sites #8663 and its diff wrote the[#8663]lines in all three rule files.b003cf2e8: the post-hook half of the declared-field door, one envelope on every driver; its diff wrote the three lines.issues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064 todef0d3e63: name-keys collection-resident publish-gate finding paths; its body reads "maintainer ruling 2026-08-20: Option A" for finding: a publish refusal'sissues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064.app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370 toa227afa41:security-role-wordcrosses to the runtime publish gate, whole, per ruling batch chore(deps)(deps-dev): bump @vitejs/plugin-react from 4.7.0 to 5.1.2 #203 item 3 letter B; it mapsposition/appand writes the past-tense crossing lines.c7655d472: the change that carried The #4630 unscoped multi-write refusals on sys_comment never fire through the wired engine — per-row dispatch bypasses resolveTargetRows on both verbs #9798 to done (its body names it), restoring the sys_comment unscoped multi-delete refusal that could not fire through the wired engine, the declared-but-unenforced fail-open the line lists beside probeInstallOrganizations answers a failed sys_organization read with a memoised empty list, so system-insert organization resolution fails open #9261 and ADR-0110 D3.searchableFieldsis a FIFTH blanket-SYSTEM_FIELDS read site — a declared anchor on an external object becomes a$searchFieldsnarrowing over a column with no storage #8404 tob849e6911: warns whensearchableFieldsdeclares an unprovisioned injected anchor, adding the optional provenance index the lines describe; the SORT twin line names it as the SEARCH wiring.config.sort/config.searchableFieldsis judged by neither list-view field rule — the same runtime-door gap #9313 closed for the flattened overlay, one member over #10001 tof1b5ad39a: a standalone ViewItem record's nestedconfig.sort/config.searchableFieldsreach the runtime publish gate, the RECORD rung.e9b526597:current_userjoinsSCOPE_ROOTS, the field-level rejection becomes its own rule, and option-levelvisibleWhenis walked for the first time.:770quotesSCOPE_ROOTS' docblock inpackages/formula; the quote now stops at "the last one this list was missing", verbatim, with the commit outside the quotation.react-blocks.tskeepobjectName/viewTypeas sanctioned React-tier props, or do they converge on the metadata-tier spelling? — the carrier objectui#2890's last phase has never had #11284 to5383fa670: the ListView react-tier vocabulary converges on the metadata-tier spelling, deprecate-first; its changeset reads "([Decision] Doesreact-blocks.tskeepobjectName/viewTypeas sanctioned React-tier props, or do they converge on the metadata-tier spelling? — the carrier objectui#2890's last phase has never had #11284, maintainer ruling 2026-08-23)".validate-page-field-bindings的element:record_picker条目仍列着 #5775 已退役的displayField/searchFields#6629 tocd584d559: drops the retireddisplayField/searchFieldsfrom the record_picker entry and addscomponent-field-specs-liveness.test.ts.unprovisionedAnchorscomment namescheck-cross-package-test-inputsas the guard that would notice a caller dropping the index — that gate does not do this #8664 to8798cd2a6: names what actually guards theunprovisionedAnchorswiring; its diff wrote both lines.validate-translation-referencesbuilds its_validationsuniverse from top-level rules only, so a nestedconditionalbranch's legitimate bundle entry is reported as an orphan #14700 tode3c52beb: descends intoconditionalthen/otherwisewhen building the_validationsuniverse; its diff wrote all three lines._views翻译键改用运行时视图身份命名(#5164 第 1 棒 / cli 段) #6124 tob3c1f3cd5: the squash commit of fix(cli):_views翻译键改用运行时视图身份命名(#5164 第 1 棒 / cli 段) #6124 itself, leg 1 of the_viewskey ruling (the CLI i18n extractor keyed by the runtime view identity).getNested) has lost its warned subject a SECOND time — the ledger now has ZERO dotted warned entries #10262 to2aca1bc4c: adds the package-internal test seam forgetNested's array fan-out; its diff wrote all three lines.NOW()/TODAY()— every other identifier is rewritten tonull, so a computed money value can never be rounded to its field's declaredscale#11060 to815585513: its body records "Maintainer ruling on Flow field expressions can call no function butNOW()/TODAY()— every other identifier is rewritten tonull, so a computed money value can never be rounded to its field's declaredscale#11060 (2026-08-23): option A", the CEL-mirrored six with no second semantics, which the lines quote.readonly" has no author-time signal — the strip is silent, and six fields in the reference app carry a hand-written comment as the only protection against re-adding the flag #13653 to36d287803: gates a hook body'sctx.apiwrite to a readonly field, and sharesbuildReadonlyIndexfrom the flow rule, the export:118describes.global:search/global:notificationsdata-source readiness read → keep-or-retire per B; plus author-time rejection for unknownPageComponentSchema.typestrings #12950 to225e7690f: createdvalidate-component-types.ts, the author-time rejection for unknown component types in spec-reserved namespaces (stage 5's anchor for the same number).3510e4a25: the squash commit of refactor(spec,drivers,lint): one implementation of the filter identity reduction #6528 itself, one implementation of the filter identity reduction (maintainer ruling 2026-08-06, option 1). The line readPR #6528; it now names the commit.Rung: no ADR,
docs/NORTH-STAR.mdorscripts/adr-anchors/file records any of these 19 decisions (the one lint anchor file,data-model-rules.ts, pins ADR-0120, which none of these lines cites), so the commit rung is the right one, as in #20234's stages.Mechanical proof
tokcmp.mjs: TypeScript 6.0.3 leaf tokens, JSDoc kinds excluded, controls mutate the head text in memory only). The merge basec96beb2707against the head, 22 files, 54,508 base tokens (the 22 files are byte-identical at7a1faf1a5dand at the merge base):runtime-gate.ts): 0 (exit 0).validate-hook-body-writes.ts): DIFFER at token 216 (exit 1).StringLiteralinvalidate-react-page-props.ts): DIFFER at token 5 (exit 1).PR #Nstands on an added line. Net-removed: 80 sites (the census's 79 in these files plus the slash-joined one), 19 numbers. The numbers kept on added lines all answer 200:flow-multi-write-unfiltered不判空组合子:filter: { $and: [] }+multi: true是整表删除,却零告警 —— 身份归约在 producer 侧有三份,lint 侧不该再抄第四份 #5659, SDUI props 声明与 renderer 不一致:6 处「renderer 兑现但 ComponentPropsMap 未声明」+ 2 处「声明了没人读」(#5068 error 升级的 spec 侧前置) #5775, [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310, lint: view-filter / page-binding field checks resolve against the blanket SYSTEM_FIELDS union, so the #8116 unprovisioned-anchor warning cannot reach filter surfaces #8340, probeInstallOrganizations answers a failed sys_organization read with a memoised empty list, so system-insert organization resolution fails open #9261, [finding] Both field-resolution gates for a list view (searchableFieldsandsort) are CLI-only — the reference-integrity suite dispatches at the runtime publish gate forflowalone, so a Studio/MCPviewwrite gets neither #9313, Console rendersnav:menu/global:search/global:notifications/app:launcheras "Component Placeholder" — four spec-declared PageComponentType members with no renderer #12183, runtime publish gate: the snapshot collection set is hand-listed in five places and only one of them can go red #13390, i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253, flows: lint aloopbody with a fallible node and no containment, and atry_catchwithoutcatch; documentloop { try_catch }as the per-iteration containment spelling (B-branch items 1–2 of #13681) #14394, [finding] a runtime-createddatasetreaches ZERO author-time rules — the type declaresallowRuntimeCreate: truewhile nothing declares it inruntimeTypesandTYPE_TO_STACK_KEYhas no row #19143, and fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584 (a pull request, the anchor commit's own PR).rev-parse --disambiguateanswers 1 object for each;merge-base --is-ancestorexits 0 againstorigin/mainand against the base; each is single-parent; the repository is not shallow; the control lege9584681a4exits 0.validate-expressions.test.tsstrips comments before it matches, andvalidate-security-posture.runtime-surface.test.tscollects thestack.Xreads insidevalidateSecurityRoleWord, which no added line carries.Tests and gates (at head
0c7b847f18)pnpm exec turbo run build --concurrency=2 --filter=./packages/* --filter=./packages/*/*underos-verify-lock: Tasks 71 successful, 71 total, VERDICT command-exit 0.pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2under the lock: Test Files 115 passed (115), Tests 5363 passed (5363); thenpnpm --filter @objectstack/lint typecheck: exit 0,check:test-typecheckOK (2 files / 6 errors / 2 pinned signatures held). VERDICT command-exit 0. The same two runs passed with the same counts on the pre-merge head2ce32f6b48.eslint --no-inline-config --format jsonover the 22 touched.tsfiles gives 22 files, 0 errors, 0 warnings.isPathIgnoredis false for all 22, read through eslint's API.eslint.config.mjs:327-328says type-aware linting is never enabled, so a comment edit cannot move an untouched file's verdict. The repo-widepnpm lintis CI's.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: 54 families derived, all run, every one exit 0.--ranreads "54 derived, 54 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero (every line carries its exit code). Among them:node scripts/check-issue-citations.mjs(the live diff-scoped run) judged 18 citations across 22 files: 17 answer as issues and 1 answers as a pull request, the kept fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584;pnpm check:issue-citations(self-test, 114 cases in 8 batteries) passes.pnpm check:doc-authoring: the sibling prose-id baseline holds, 810 pinned sites across 230 files, no growth.pnpm check:nul-bytes: OK over 9,239 tracked text files; a control-byte scan of the 23 changed files finds none.content/docs/references/names any of the 19 numbers, and no generator readspackages/lint/src, so nothing was regenerated.patchfor@objectstack/lint.files[]shipsdist, and the rewritten comments reach it: 12 of the 19 shas appear in the builtdist(for example8ed9c54b4anddef0d3e63inindex.d.ts,b849e6911inindex.jsandindex.d.ts); the positive control, the unchanged sentence "the near-miss shape: atry_catchthat declares nocatch" of an exported docblock, is inindex.d.ts. Hence patch, notskip-changeset.merge-treeof the head ontoorigin/main0f6dcac5e9, from a bare shared clone with nomerge.*config, exits 0. The two commitsmaingained after the merge touch none of the 23 files.Hypotheses (measured first)
7a1faf1a5d, equal per file to the claim.validate-react-page-props.ts:1198, a findingmessage) stays byte-identical; no test or script reads a rewritten line by literal.authoring-rules.tsstays at its base blob. At that read, the 9 open PRs' full file lists and the newestClaim:on all 11pm:dispatchedcards name none of the other 23 paths.validate-searchable-fields.ts:312pre-#8404is listed dead before and is gone after.Deviations
validate-page-field-bindings.ts:208carried#5775/#6629, a slash-joined dead number the citation grammar does not read; it now reads "the same SDUI props 声明与 renderer 不一致:6 处「renderer 兑现但 ComponentPropsMap 未声明」+ 2 处「声明了没人读」(#5068 error 升级的 spec 侧前置) #5775 residue class (commit cd584d5)", stage 5's precedent for the slash-joined#9972.runtime-gate.ts:780is the other half of the rewritten:779sentence and held no number.origin/mainwas merged once (0c7b847f18, mergingc96beb2707): the first derivation read STALE TREE becausescripts/sdui-manifest.record.jsonchanged onmain. The merge was clean, no driver-routed path and no lockfile change, and it touches none of the 23 files; the build, tests and gates above ran after it.Claude-SessionplusCo-authored-by: Claude); the pre-push trailer check passed on every push.Acceptance notes
What stays for this card (why it says
Part of):authoring-rules.ts, 5 sites, excluded while PR #20593 holds it. Anchors, verified the same way, for whoever takes it after that PR lands::198#10064 todef0d3e63;:1117#16659 toecdfc9411(it addedflow-schedule-organization-missingto the registry);:1687"(PR #8546)" toba5e957ef, that PR's own squash commit;:1713and:1733#19370 toa227afa41.Form D, not touched:
validate-react-page-props.ts:1198is thereact-prop-deprecatedfindingmessage, which ends "...is removed after the deprecation window (#11284)." An author sees it, so it takes ruling D (no number), which is a string change and outside this comment-only scope.scripts/doc-authoring-prose-id.baseline.jsonpins it (#11284: 1for this file). It needs a form-D carrier.Outside the census's surface, which blanks strings and defers test files (noted, not swept here):
packages/lint/src/*.test.tstitles and comments still cite several of these dead numbers (finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290, lint:searchableFieldsis a FIFTH blanket-SYSTEM_FIELDS read site — a declared anchor on an external object becomes a$searchFieldsnarrowing over a column with no storage #8404, finding: twoIMPLICIT_FIELDSsets spread the blanket SYSTEM_FIELDS union with no provenance awareness — candidate sixth/seventh unprovisioned-anchor read sites #8663, A standalone ViewItem RECORD's nestedconfig.sort/config.searchableFieldsis judged by neither list-view field rule — the same runtime-door gap #9313 closed for the flattened overlay, one member over #10001, finding: a publish refusal'sissues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064, finding: the liveness author-lint's array fan-out (getNested) has lost its warned subject a SECOND time — the ledger now has ZERO dotted warned entries #10262, Aloopnode aborts the entire flow run when one iteration's node fails — a single bad row kills a whole scheduled sweep, and there is no per-iteration containment to opt into #13681, [Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370 and others).content/docs/automation/hook-bodies.mdx(finding: twoIMPLICIT_FIELDSsets spread the blanket SYSTEM_FIELDS union with no provenance awareness — candidate sixth/seventh unprovisioned-anchor read sites #8663, The undeclared-field door sits in FRONT of the hooks, so a key a beforeInsert hook writes has no door at all — and the drivers then disagree (memory stores it, SQL throws a raw statement error) #13657),content/docs/automation/flows.mdx(Flow field expressions can call no function butNOW()/TODAY()— every other identifier is rewritten tonull, so a computed money value can never be rounded to its field's declaredscale#11060) andcontent/docs/deployment/validating-metadata.mdx([Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370).packages/formula/src/cel-engine.tscites finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290 four times, including the docblockvalidate-expressions.ts:770quotes. It is in the census, in another lane's package.Wording, each true of its commit.
validate-expressions.ts:571keeps fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584 besidee9b526597: fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584 is that commit's own PR, so "arrived in commit e9b5265, and needed that same change (fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584) to be noticed" states the one act both old numbers named.runtime-gate.ts:362names the The #4630 unscoped multi-write refusals on sys_comment never fire through the wired engine — per-row dispatch bypasses resolveTargetRows on both verbs #9798 shape in words, as the fail-open that commit c7655d4 ended, next to probeInstallOrganizations answers a failed sys_organization read with a memoised empty list, so system-insert organization resolution fails open #9261 and ADR-0110 D3.lint-flow-patterns.ts:343reads "The measured case commit 8ed9c54 records, exactly: one row with a null owner killed the sweep"; that commit wrote the sentence, andc02f70e13later fixed the same shape in the showcase flow.Generated by Claude Code