Skip to content

docs(lint): re-anchor the five dead tracker citations in authoring-rules.ts to the commits that decided them - #20631

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20597-lint-dead-citations-authoring-rules
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20597-lint-dead-citations-authoring-rules

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20597
Clause-②: no

Stage 2 of the packages/lint dead-citation sweep (claim 5888191846). Stage 1 (PR #20612) left packages/lint/src/authoring-rules.ts at 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/lint patch changeset.

This PR says Part of: the form-D finding-message string at validate-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 to packages/lint/.

site in authoring-rules.ts before after anchor
:201 (the AuthoringFinding.path docblock) #10064 commit def0d3e63
:1123 #16659 commit ecdfc9411
:1722 (PR #8546) commit ba5e957ef
:1748 [#19370] commit a227afa41
:1768 [ADR-0090 D3 / #8310 → #19370] commit a227afa41 (ADR-0090 D3 and #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.

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

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:
  • 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 def0d3e63 is in the AuthoringFinding docblock of dist/runtime-*.d.ts, beside the unchanged "Positional as RULES emit it", the positive control.
    • commit ba5e957ef (cited only here) and commit a227afa41 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 (#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 finding: a publish refusal's issues[].path carries an array index into the gate's private snapshot (objects[417].sharingModel) that no caller can resolve #10064, :1123 A schedule-triggered flow's notify delivers 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), :1748 and :1768 [Decision] app / position 声明 allowRuntimeCreate: true 而作者期规则零命中 —— 退役这个声明,还是把规则造出来? #19370, on e651556e2d and at the base alike. All four anchors were re-verified above from their own diffs, not copied.
  3. Holds. PR feat(lint): os validate refuses an api flow with no per-flow secret #20593's new lines cite After #20529, authoring surfaces still teach or pass an api flow with no secret: skills/objectstack-automation calls it optional, and os validate passes 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.secret on 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

  • 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 (#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 (#11284: 1 for that file), and that baseline is shrink-only.

An ordinal beside PR #20593's insertion, kept verbatim: the paragraph above :1123 now ends "#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 #19370 at :472, :483 and :515.


Generated by Claude Code

…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>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint, touching 2 documentable anchor(s).

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

  • content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES (symbol, a top-level const object))
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): node scripts/docs-audit/affected-docs.mjs --json 542670da6dfc17d3a2ec919139afb7caae018d02 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 341e0c67bcdab06c29d43d115ca09aa90b671993 — the merge of head 2e1955b492a89555e2d39d254ff16ef9d6c7449d into base 542670da6dfc17d3a2ec919139afb7caae018d02, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 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

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 542670da6dfc17d3a2ec919139afb7caae018d02 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2e1955b492a89555e2d39d254ff16ef9d6c7449d
Local-runs: none

Read (this act, stamped 2026-09-29T11:34Z): card #20597 (body; every one of its 7 comments: the stage-1 claim 5885046469, the stage-1 report 5885887024, the ACCEPT 5886175996, the landing and park record 5886592368, the unlock 5887336767, the stage-2 claim 5888191846 and the stage-2 os-dev-report 5889251130), ruling C+D 5749154545 on #19123, PR #20612 with its at-tier record 5886146186 as the worked example, PR #20631 (body, its one comment 5889210495, the 2-file list, both commits and the net diff against the merge base 1322cc72c9, read from the local ref at the head above, plus the base and head blobs of authoring-rules.ts), the four anchors (identity, parents, ancestry, message, stat, and each one's own diff of authoring-rules.ts; def0d3e63's diff of runtime-gate.ts; the exported ids of validate-flow-trigger-readiness.ts at ecdfc9411 and its parent), e651556e2d (PR #20593's squash) for what it inserted into the file, docs/adr/**, docs/NORTH-STAR.md, scripts/adr-anchors/, and the rest of docs/** and content/** on origin/main for the four numbers and the four rules by name, ADR-0090 D3 and ADR-0094's #8310 amendment, content/docs/references/** at the head, packages/lint/package.json files[] and tsup.config.ts, scripts/check-issue-citations.mjs (declared surfaces, CITATION_RE, the PR #N case), scripts/doc-authoring-prose-id.baseline.json, and the check-runs on the head, read once. Nothing was built, run or re-run: the two blobs were compared with a string-, template- and regex-aware comment stripper in scratch (a read of the diff, not a gate, as the stage-1 record did), the numbers were probed with REST GET issues/N against this repository (GitHub reads), and no gate, test or census was executed.

① Derived judgments

(a) Scope and file surface: right. Two files: packages/lint/src/authoring-rules.ts (+5/−5; 2,011 lines at base and head) and the new .changeset/20597-lint-authoring-rules-provenance-anchors.md (+11). The changed lines are exactly :201, :1123, :1722, :1748 and :1768, the claim's five sites at their e651556e2d positions: packages/lint is byte-identical from e651556e2d to the base 1322cc72c9 and from the base to origin/main 542670da6d (the three commits main gained, PR #20587, PR #20626 and PR #20624, touch nothing under packages/lint), so the net diff against current main is the diff judged here and no merge of main was owed. No other file, no test, no generated page, no gate, no release page, no governed surface. Head repository is the base repository; draft; assignee os-tesla; no auto-merge; mergeable: true, mergeable_state: blocked (draft); labels documentation, size/s, tooling (the auto-labelers'). First line Part of #20597, second line Clause-②: no. Two commits (rewrite, changeset), no merge commit, model-free trailers. validate-react-page-props.ts is not in the diff and its blob f5684f077 is the same on main and at the head, so the form-D finding message at :1198 ending "(#11284)" is byte-identical and stays on the card; Part of, not Fixes, is therefore right, and Part-of PR must not also close its card is success.

(b) Comment-only, no code token, string literal, rule message, hint, rule id, surfaceReason or export moves: right. All 10 changed lines lie inside comments on both sides: :201 inside the AuthoringFinding.path JSDoc block opened at :196 and closed at :203; the other four are // line comments. The non-comment residue (code plus string, template and regex literals kept; comments dropped) is byte-identical base to head, with 957 comment ranges on each side and every line count equal. Controls, mutated in memory only: a whole comment line inserted leaves the residue equal modulo the inserted line's whitespace; a code token inserted (pathX: string;) makes it differ; one character changed inside the runtimeTypes string literal of the validateSecurityPosture entry makes it differ. Each changed line differs from its base line in the replaced token and nothing else. No test file is in the diff, so no it/describe title moved.

(c) Numbers: right. Removed: #10064, #16659, #8546 (spelled (PR #8546)) and #19370 x2. The added lines carry only #8310 x2, on the same two lines (:1722, :1768) where it stood at base, so added-not-removed is empty; no PR #N stands on an added line; 4 distinct 9-hex shas stand on added lines (a227afa41 twice), none on removed lines. CITATION_RE never reads commit 9-hex, and the gate's own self-test holds that a PR #N head is a citation, so (PR #8546) was rightly a census site. REST GET issues/N, no redirects: #10064, #16659, #8546 and #19370 all answer 404; #8310 answers 200 (closed issue) and the context line's #17396 answers 200; #11284 answers 404 (form D, untouched); controls #20553 and #20552 200 (closed), #20611 200 (open). Total #N tokens in the file fall by exactly 5 (213 to 208 on this read's regex; the dev's 215 to 210 counts a wider digit class, same net).

(d) Anchor truth: right, 4 of 4. Each sha resolves to exactly one object under rev-parse --disambiguate, has one parent, and merge-base --is-ancestor exits 0 against origin/main and against the base; the checkout is not shallow.

(e) Form C and sense: right. Each rewrite leads with commit 9-hex, or keeps the marker form where a marker stood ([commit a227afa41], [ADR-0090 D3 / #8310 → commit a227afa41]), as stage 1 did, and each line still says what was decided. On the :1123 ordinal: the paragraph at :1105 to :1123 now reads "Four rules answer yes and emit error ... #20553 made it five ... flow-trigger-unknown-object deliberately stayed warning, as did flow-draft-status-ambiguous", then "Commit ecdfc94 added a sixth id, flow-schedule-organization-missing, at warning; #17396 RETIRED it". Two counts: PR #20593's "five" counts the error-emitting rules; "sixth" is ecdfc9411's own ordinal over the file's ids, and it was already imprecise when written (the file exported six ids before that commit, so the new one was its seventh, and the paragraph itself had named six). Judged: fine as two statements about different things, not misleading about the rule the line states, which is that the id was added at warning and later retired, both true, with the load-bearing retirement clause carrying its live #17396; the ordinal is decorative and read no better on main before this PR. Moving only the number keeps the diff a pure token swap and the five-site scope exact; rewriting "a sixth id" to "an id" would have been inside the site and is not owed. Non-blocking; the next edit of that paragraph should drop the ordinal.

(f) Generated pages: nothing to regenerate, right. No page under content/docs/references/ at the head names the four numbers, nameKeyFindingPath or AuthoringFinding, and the scripts that name authoring-rules.ts (check-system-context-census.mjs, check-docs-transcript-drift.mjs, which loads the registry module, and the prose-id baseline) are gates and a baseline, not page generators. The docs-drift comment 5889210495 lists content/docs/deployment/validating-metadata.mdx via the AUTHORING_RULES symbol, an advisory on a comment-only diff; Flag docs affected by code changes is success.

② Semver level

patch for @objectstack/lint is right and Clause-②: no is right. files[] is dist, README.md, CHANGELOG.md; tsup.config.ts emits esm and cjs for index and runtime with dts on (unless OS_SKIP_DTS). So the AuthoringFinding.path docblock, on an exported interface, reaches the shipped .d.ts, and the bundle carries the source's line comments, which is where the dev found commit ba5e957ef and commit a227afa41 in dist/index.js and dist/index.cjs. That is comment text the bundler preserved, not a runtime string: (b) shows no string, template or regex literal byte differs base to head, so nothing an author is shown or the runtime reads has changed, and no export, type, value, message, severity or behaviour moves; nothing widens or narrows. The tarball's bytes do change, so skip-changeset would be wrong and patch is the level, the one stage 1 and #20234's stages took for the same act. The changeset names one package, describes only the re-anchoring, and carries no tracker number and no model identifier; Check Changeset is success. The 20597- prefix is the changeset's filename, not shipped text (a227afa41's own 19370-role-word-crosses-whole.md is the precedent).

③ Boundary flags

Blocking: none.

Dev deviations, each answered:

  1. Model-free trailers (Claude-Session plus Co-authored-by: Claude) on both commits: confirmed; AGENTS.md's pair, as stage 1. Non-blocking.
  2. The first lit string control void and re-run on a parser-located literal: rightly reported; this read's own string control (one character inside the runtimeTypes literal) differs, agreeing with the re-run.
  3. The runner's 580 s timeout killing check:slot-lookup mid-run and its re-run to exit 0: not observable from here; the head's Lint & Repo Gates run is the run of record and is named below as not concluded.
  4. No merge of origin/main: right, judged in (a); packages/lint is unchanged from the base to main.
  5. The empty probe push, scratch and worktree cleanup: not observable from here; the PR shows exactly two commits and no force-push is visible.
    Open questions: the report lists none.

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 (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in): path-filtered or opt-in, not this diff's), 0 failure, 13 in progress. Success: Build Core, Governed Surface Queue Guard, Check Changeset, Type Check · source gates, Type Check · debt ledger, Dogfood Verify CLI, Check PR Size, Check Documentation Links, Flag docs affected by code changes, The card this PR closes must claim this branch, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Auto Label, filter. NOT concluded at that read and NOT presumed green here: Lint & Repo Gates (the run of record for the diff-scoped citation verdict, check:doc-authoring, pnpm lint and the check:* roster), Test Core 1/6 to 6/6 (the lint vitest suite rides these; the aggregate not yet created), Dogfood Regression Gate 1/3 to 3/3, Temporal Conformance (live PG + MySQL), Type Check · workspace and Type Check · consumer gates (the TypeScript Type Check aggregate not yet created). No red run exists to attribute. Of the dev's 54 derived families, the concluded runs answer the build, changeset, governed-surface, docs-links, PR-size and claim-guard families now; the citation, doc-authoring, lint, typecheck, test, dogfood and temporal families are the open runs above, which the owning seat reads concluded before this PR is armed. What the diff-scoped citation run will judge, read here: 2 citations on added lines, #8310 x2, both answering 200.

Implemented-by: claude/issue-20597-lint-dead-citations-authoring-rules
Reviewed-by: session_014EJ1ED8X4MMrT18BhVx4tx

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 11:40
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit f29c83d Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20597-lint-dead-citations-authoring-rules branch September 29, 2026 11:58
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
… to the commits that decided them (objectstack-ai#20632)

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

## What changed

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

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

## The sites left

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

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

## Mechanical guard: no code token moves

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

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

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

## Changeset

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

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

## Gates (head `93e4d69ba6`)

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

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

## Deviations

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

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

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants