docs(lint): re-anchor the five dead tracker citations in authoring-rules.ts to the commits that decided them - #20631
Conversation
…les.ts to the commits that decided them Ruling C+D form C, as the packages/lint stage applied it to the other 22 files: each comment or docblock line that cited a tracker number answering 404 now names the commit whose own diff decided what the line states. - :201 #10064 -> def0d3e (name-keyed wire paths; wrote the docblock) - :1123 #16659 -> ecdfc94 (added flow-schedule-organization-missing) - :1722 (PR #8546) -> ba5e957 (that PR's squash; runtimeTypes gains permission + book) - :1748, :1768 #19370 -> a227afa (security-role-word crosses whole) Comments only: 5 lines out, 5 in; no code token, string, message or hint moves. Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
…provenance anchors Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx Co-authored-by: Claude <noreply@anthropic.com>
📓 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:
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 341e0c67bcdab06c29d43d115ca09aa90b671993 && git checkout 341e0c67bcdab06c29d43d115ca09aa90b671993
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 542670da6dfc17d3a2ec919139afb7caae018d02 2e1955b492a89555e2d39d254ff16ef9d6c7449d && git checkout -B drift-repro 542670da6dfc17d3a2ec919139afb7caae018d02 && git merge --no-ff 2e1955b492a89555e2d39d254ff16ef9d6c7449d
node scripts/docs-audit/affected-docs.mjs --json 542670da6dfc17d3a2ec919139afb7caae018d02
|
Contract reviewServed-tier: Read (this act, stamped 2026-09-29T11:34Z): card #20597 (body; every one of its 7 comments: the stage-1 claim ① Derived judgments(a) Scope and file surface: right. Two files: (b) Comment-only, no code token, string literal, rule message, hint, rule id, (c) Numbers: right. Removed: #10064, #16659, #8546 (spelled (d) Anchor truth: right, 4 of 4. Each sha resolves to exactly one object under
(e) Form C and sense: 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: the claim's "stop on breach" clause was not tripped; no breach. Escalated: none. CI at this head, the judging read, taken once at 2026-09-29T11:25Z: 31 check-runs, 15 success, 3 skipped ( Implemented-by: VERDICT: PASS Generated by Claude Code |
… 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
Stage 2 of the
packages/lintdead-citation sweep (claim5888191846). Stage 1 (PR #20612) leftpackages/lint/src/authoring-rules.tsat its base blob while PR #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/lintpatchchangeset.This PR says
Part of: the form-D finding-message string atvalidate-react-page-props.ts:1198((#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 topackages/lint/.1322cc72c9, 2026-09-29T10:18:50Z to 10:22:33Z, board enumerated (185 pages, frontier [finding] release.yml: release-integrity reads "cli on npm" as "the publish is complete", so a landing during a publish starts the docker backfill before the fixed group is on npm, and it goes red #20627). Repo-wideallocated-but-absent2,209.2e1955b492, 11:13:21Z to 11:16:47Z (185 pages, frontier fix(spec): os migrate meta guidance for the remaining migration-entry families states each lesson in words, not tracker numbers (stage 9) #20630). Repo-wideallocated-but-absent2,204, exactly 5 fewer. No finding at head is absent at base.authoring-rules.ts:201(theAuthoringFinding.pathdocblock)def0d3e63:1123ecdfc9411:1722(PR #8546)ba5e957ef:1748[#19370]a227afa41:1768[ADR-0090 D3 / #8310 → #19370]a227afa41(ADR-0090 D3 and #8310 stay)packages/linttotalThe five lines on
origin/maine651556e2dare the same lines at the base:packages/lintis byte-identical betweene651556e2dand1322cc72c9.Why each anchor decides its line
Each sha resolves uniquely (
rev-parse --disambiguategives 1 object). Each is single-parent.merge-base --is-ancestorexits 0 againstorigin/mainand against the base, and the repository is not shallow. Each commit's own diff was read for the rule its line states.issues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064 todef0d3e63: "key collection-resident publish-gate finding paths by name, not the private snapshot index". Its body names finding: a publish refusal'sissues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #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, theobjects.acme_invoice.sharingModelexample and the pointer tonameKeyFindingPath, which the same commit introduced inruntime-gate.ts.notifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659 toecdfc9411: the squash commit that declares a time-triggered flow's acting organization. Its diff addsFLOW_SCHEDULE_ORGANIZATION_MISSING(flow-schedule-organization-missing, atwarning) tovalidate-flow-trigger-readiness.ts. It also wrote theauthoring-rules.tssentence "... added a sixth id,flow-schedule-organization-missing, atwarning" that this line opens, and its sub-commits name A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659. The live [Decision] 平台自带的四个定时示例流一个都声明不了组织 —— 而新规则要求它们必须声明 #17396 retirement beside it stays.(PR #8546)toba5e957ef: PR feat(lint): permission/book cross the runtime publish gate; object measured dirty stays behind; security-role-word splits to stay behind whole (#8310) #8546's own squash commit, "permission/book cross the runtime publish gate; object measured dirty stays behind". Itsauthoring-rules.tsdiff changesruntimeTypes: ['seed']to['seed', 'permission', 'book'], which is [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310 slice 1 as the line states. The live [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310 stays.app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370 toa227afa41(two sites): "security-role-wordcrosses to the runtime publish gate, whole". Its body names [Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370 as the card it lands. Its ownauthoring-rules.tsdiff wrote both lines: "[[Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370] It has since crossed, also whole, on its own entry" and the[ADR-0090 D3 / #8310 → #19370]marker. Stage 1 cited the same commit for the same number inruntime-gate.tsandvalidate-security-posture.ts.Rung: no file under
docs/adr/**,docs/NORTH-STAR.mdorscripts/adr-anchors/names any of the four numbers. So the commit rung is right, as in stage 1. ADR-0090 D3 already stands on:1768and is kept.Mechanical proof
issues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064, A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659, feat(lint): permission/book cross the runtime publish gate; object measured dirty stays behind; security-role-word splits to stay behind whole (#8310) #8546 and [Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370 ×2. The added lines carry only the live [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310 ×2, which stands on both the removed and the added side of:1722and:1768. So added-not-removed is empty, and noPR #Nstands on an added line. There are 215#Ntokens at base and 210 at head.a227afa41×2), none on removed lines.scripts/doc-authoring-prose-id.baseline.jsonpins this file's string sites as [runtime/metadata] 作者时规则只存在于 CLI:Studio/REST/MCP 的运行时授权面是第四扇门,26 条规则一条不跑——#4409 修完后最大的敞口 #4463, [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716, [P2/P3] 运行时授权门的 advisory findings 要进响应并被 Studio 渲染 + docs「the gate」补第四列(#4463 收尾) #4717, [finding] The fourviews[]visibility-predicate rules are CLI-only — a Studio/REST/MCPviewwrite bypasses all of them; if they move to runtime-publish, they must move together #7220, [2 of #7891]permission/bookwiring:TYPE_TO_STACK_KEYentries + the cross-collection snapshot the three comparison rules need #8309 and lint: a sharing rule declared on an object whose effective sharing model ispublicis statically detectable and unreported until boot #9698, none of them these four numbers.check-docs-transcript-driftloads the registry module, not its comments.Tests and gates (at head
2e1955b492)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=2under the lock: Test Files 115 passed (115), Tests 5379 passed (5379), VERDICT command-exit 0.pnpm --filter @objectstack/lint typecheckunder the lock: exit 0.check:test-typecheckOK (2 files, 6 errors, 2 pinned signatures held). VERDICT command-exit 0.eslint --no-inline-config --format json packages/lint/src/authoring-rules.tsreports 1 file, 0 errors, 0 warnings.isPathIgnoredis false, 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 --commandsderived 54 families, and all 54 ran with exit 0.--ranreads "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 [3 of #7891] Flip the registration:runtimeTypesgainsobject+permission/book— whole rule families cross the publish door #8310 ×2, and both answer as issues.pnpm check:issue-citationspasses 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.content/docs/references/names the four numbers,AuthoringFindingornameKeyFindingPath, and no generator readspackages/lint/src.patchfor@objectstack/lint, a new file (stage 1'slint-provenance-anchors.mdis untouched).files[]shipsdist, and the rewritten comments reach it:commit def0d3e63is in theAuthoringFindingdocblock ofdist/runtime-*.d.ts, beside the unchanged "Positional as RULES emit it", the positive control.commit ba5e957ef(cited only here) andcommit a227afa41are indist/index.jsanddist/index.cjs.dist.merge-treeof the head ontoorigin/main542670da6d, from a bare shared clone with nomerge.*config, exits 0. None of the three commitsmaingained since the base touchespackages/lint.Hypotheses (measured first)
packages/lint, all inauthoring-rules.ts, and after the change it reads 0. The card's sixth site, the(#11284)string atvalidate-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.:201finding: a publish refusal'sissues[].pathcarries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064,:1123A schedule-triggered flow'snotifydelivers nothing on a multi-organization install: the run carries no organization, so the tenant-scoped inbox/delivery writes are refused (#8844) while the run reads healthy #16659,:1722(PR #8546),:1748and:1768[Decision]app/position声明allowRuntimeCreate: true而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370, one651556e2dand at the base alike. All four anchors were re-verified above from their own diffs, not copied.apiflow with no secret:skills/objectstack-automationcalls it optional, andos validatepasses it #20553, Seam: the /meta save path runs the runtime authoring gate on the redacted body, before #20552's stored-secret carry-forward, so flow-api-trigger-secret-missing cannot run at the runtime surface #20611 and [security] a flow's inbound-hook secret (config.secreton the start node) is served in cleartext by the flow-definition read; after #20529 every armed hook carries one #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
Claude-SessionplusCo-authored-by: Claude), not the model-named trailer the harness reminder suggested. The pre-push trailer check passed on both pushes.Acceptance notes
Form D, not touched (why this PR says
Part of):validate-react-page-props.ts:1198is thereact-prop-deprecatedfindingmessage. It ends "...is removed after the deprecation window (#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.jsonpins it (#11284: 1for that file), and that baseline is shrink-only.An ordinal beside PR #20593's insertion, kept verbatim: the paragraph above
:1123now ends "#20553 made it five", counting the rules that emiterror.:1123reads "Commit ecdfc94 added a sixth id", an ordinal that commit wrote itself. The two count different things: rules that emiterror, and ids in the rule file. The ordinal is also imprecise on its own terms, becausevalidate-flow-trigger-readiness.tsexported six ids beforeecdfc9411, 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.mdxcites #19370 at:472,:483and:515.Generated by Claude Code