Skip to content

docs(spec): re-anchor the dead tracker citations in data/ to the commits that decided them (stage 3) - #20533

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20234-dead-citations-data
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-20234-dead-citations-data

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20234
Clause-②: no

What changed

This is stage 3 of the staged sweep. It covers packages/spec/src/data/** and nothing else. It leaves out the files an open PR or an in-flight claim holds: data-engine.zod.ts, data-engine.test.ts, hook.form.ts, analytics*.ts, cube-member-inner-name-retirement.test.ts, driver/turso.zod.ts and filter-subtree-provenance.ts, as the claim names them. It also leaves out four files that open PRs started editing after the claim: driver/turso.test.ts (PR #20504, #20437's, opened 2026-09-28T20:08Z), object.form.ts (PR #20519, #20432's, 21:55Z), object.zod.ts (PR #20521, #20494's, 22:10Z) and filter-logic-conformance.ts (PR #20523, #20444's, 22:39Z). See Acceptance notes. Later stages cover the other areas, so this PR says Part of.

Every comment or docblock site in scope that cited a tracker number answering 404 has been rewritten in ruling C+D's form C (comment 5749154545 on #19123). That is 163 sites on 161 lines in 44 files, covering 40 numbers. Each rewritten line now cites the commit in origin/main history that decided what the line describes, and it says in its own words what that commit decided.

No ADR or ruling-record file in docs/adr/ or scripts/adr-anchors/ records the decision behind any of the 43 dead numbers in scope. ADR-0104 names #12380 only as a reference, and ADR-0055 states the rule that #8772's ruling enforced, not the ruling itself. So every anchor is a commit: 38 distinct shas. One number was dropped rather than anchored: #17286, a tracking card that recorded an axis as undecided, under which no commit landed. The sentence keeps its reason in words.

Three comment sites in scope are left on purpose (see Acceptance notes). Two are the [#6259] marker in api-derivation.ts:163, which a test string reads, and the test comment that names that marker. The third is field.zod.ts:370, whose #6111 is objectui's number.

Only comments changed. Every source file keeps its line count (174 lines out, 174 in, over 45 files), so no line citation into these files moves. Thirteen of those 174 lines held no dead citation. Eleven are the other half of a sentence that had to be reflowed or rewritten. One is a table header (value-roundtrip-conformance.ts:20, 「card」 to 「card or commit」, because its row now holds a commit). One is api-derivation.ts:164, which now carries the [#6259] sentence's commit. No code token moves (see the guard below). The 41 string-literal sites that carry a dead number are tokens, so they are left as they were and listed below.

No citation number is added. Every tracker number on an added line was already on the line it replaces. No PR number stands on an added line.

Two more kinds of file change, both mechanical:

  • One regenerated reference page. Two of the rewritten docblock lines (feed.zod.ts:15, :18) project into content/docs/references/data/feed.mdx. check:docs proved that page stale, and pnpm --filter @objectstack/spec check:generated --fix regenerated only it. The diff is two lines, each the same substitution as its source line. No page a held file projects into (analytics.mdx, data-engine.mdx, hook.mdx, driver-turso.mdx) moved.
  • A patch changeset for @objectstack/spec (see Changeset below).

Census: data/, before and after

Instrument. This is the instrument of stages 1 and 2. It sends REST GET /repos/objectstack-ai/objectstack/issues/N without following redirects, for every distinct number cited in packages/spec/src/data. The population is:

  • the citation gate's own exported CITATION_RE and NON_CITATION_HEADS, kept when the qualifier is none, objectstack, objectstack-ai/objectstack, framework, pre- or post-;
  • widened here to the capitalised spellings of those qualifiers (Pre-, POST-, Framework: 7 sites, one of them dead), which stage 2's case-sensitive set did not read;
  • N of 100 or more, excluding summon heads.

Each site is classified by the TypeScript parser as a line comment, a docblock, a block comment or a string.

Controls. The lit controls were #16862, #16847 and #17698. The dead controls were #16714, #16715 and #16697. They were probed at the start, after every 100 numbers and at the end. They read 24 of 24 lit (200) and 24 of 24 dead (404) over 8 checkpoints in both runs.

reading tree numbers probed 200 404 301 or other dead sites, all of data/ in scope excluded (held files) in-scope lines in-scope files dead numbers in scope
before base 9bf5e67af, probed 2026-09-28T19:32Z to 19:36Z 618 571 47 0 240 207 33 204 47 43
after head 96fd49caa2, probed 2026-09-28T23:19Z to 23:23Z 600 571 29 0 77 44 33 43 16 21

Before, in scope, by class. 92 non-test docblock sites and 13 non-test line comments. 16 test docblock sites and 45 test line comments. 39 test string sites. 2 non-test string sites.

After, in scope. 41 string sites and 3 comment sites remain, all three deliberate. The head probe found no number newly dead since the base probe: the same 571 numbers answer 200.

PR #20226's area table read data 239 at an earlier base; this census reads 240 at 9bf5e67af. The 33 excluded sites sit in object.zod.ts (15), analytics.zod.ts (3), analytics-strictness-batchd.test.ts (2), analytics-date-range-two-bound-window.test.ts (1), driver/turso.zod.ts (2), driver/turso.test.ts (3), filter-subtree-provenance.ts (3), filter-logic-conformance.ts (3) and object.form.ts (1). data-engine.* and hook.form.ts carry none.

Per-number table

The counts are in-scope sites and files at the base. rewritten / left gives comment sites rewritten and sites left. Every anchor was read in its diff or message, not only in its subject: it is the commit that made the change the line now describes, and its own diff or message names the number it replaces.

number sites / files rewritten / left anchor: what it decided
#6111 (objectui) 1/1 0/1 objectui's number, left: see Acceptance notes
#6259 5/2 1/4 6968885ef: retires the producer-less batch: 'bulk' row of DATA_ACTION_TO_API_OPERATION and the prose calling batch a runtime action. The marker and 2 test strings stay (see Acceptance notes)
#6345 18/5 17/1 e2798fab7: one driver vocabulary; both boot hosts read the shared table; mongo to mongodb; turso a builtin; the fork-1 and fork-2 refusals
#6571 10/2 8/2 2f3e79351: $between endpoints accept the ISO/clock strings the platform produces, as a bare string (rider ①)
#8495 9/2 6/3 4bfe1a539: refuses ${…} placeholders in memory persistence.path / persistence.key at publish
#8656 1/1 0/1 a test title only
#8696 20/8 17/3 90a12fb18, the card's mongodb arm: a bound secret rides beside an unmodified url as MongoClient auth. Its own pins carry the multi-host form new URL() cannot parse and the bound secret outranking options.auth
#8772 3/2 3/0 75b7c240a: Direction 2 of the 2026-08-16 maintainer ruling. The builder forces required: true on a master_detail under controlled_by_parent, and raw parse stays tolerant. ADR-0055 stays cited beside it
#8778 1/1 1/0 7901b2dd2: stamp-only tenancy.organizationField, declared by sys_api_key
#8794 2/1 2/0 1850ebbb0: corrects the reuse-safety claim on the filter-subtree mark from the survey's measurement, and routes a mechanism change to a spec-seat ruling (stage 1's anchor too)
#8836 2/1 2/0 1850ebbb0: the same commit, which pins the invariant (one line carries both numbers)
#8873 6/3 6/0 096106522: a bound credentialsRef reaches the postgres server on the DSN branch. Its diff records that pg sends a password only when the server asks
#8874 1/1 1/0 d70428ae7: a declared mysql ssl reaches mysql2 as its own TLS options object, because mysql2 rejects a bare boolean
#8876 9/5 6/3 d634e665b: exports urlUserinfoUsername, and its diff states the asymmetry that a username is not credential material
#9040 20/6 14/6 24206416a: refuses a credential in the mongo options passthrough at publish, and redacts the passthrough secret paths on read
#9041 22/2 17/5 d491625c1: refuses a bound credentialsRef with a user-less mongo config.url, with the triage's fences
#10165 5/1 1/4 801296050: ttl.onlyWhen with the canonical null predicate (maintainer ruling 2026-08-20, option A)
#10274 1/1 1/0 d1ba685ec: re-measures the objectui pin citations and gates the class
#10329 6/2 6/0 15d58dbf1: retires the import lookup transform's steering params (ADR-0049)
#10347 2/1 2/0 530c1df65: the Archiver honours a declared ttl (maintainer ruling 2026-08-20)
#10527 2/1 1/1 5649efbf9: refuses a diverging retention + ttl + archive triple at parse time
#11065 7/3 5/2 20950404c: a boolean aggregand counts as 1 or 0 in avg and sum, the first face aligned. No commit message names the card; this is where the number first entered the tree
#11195 3/1 2/1 b37231883: UserActionsConfigSchema adopts group / hideFields / rowColor
#11215 1/1 1/0 42a117b88: documents NoSQLIndexSchema.unique's deliberate scope-vocabulary omission
#11350 1/1 1/0 ece4dad31: records the 2026-08-23 maintainer ruling on entry nameability (stage 1's anchor too)
#11408 2/1 1/1 f11fc61c5: declares editMode (maintainer ruling 2026-08-24)
#11507 5/2 5/0 88b9d749a: declares sys_activity.type an open, author-extensible vocabulary (maintainer ruling 2026-08-24, direction 4)
#11658 1/1 1/0 1a6a19c31: opens RecordActivityProps.types to author-contributed kinds
#12380 4/2 4/0 4045b954d: makes the SQLite Field.json codec injective; its message carries the measured boundary
#12868 1/1 0/1 a test title only. Its comment site sits in object.form.ts, now held by PR #20519; its deciding commit is c459da6bc (see Acceptance notes)
#13156 1/1 1/0 fd289be45: strips tracker ids from function-declaration-built refusal prose (the card's A half)
#13644 3/2 2/1 34ce8e7db: declares ctx.referentialFieldClear on HookContextSchema
#14426 2/2 1/1 40a44b91b: the undefined-comparand refusal prescribes the null predicate by its ruled spellings, position-safe
#14676 1/1 1/0 13c48c2a5: retires connector.errorMapping; its test states the same assertion-set reasoning
#16126 2/2 2/0 859ded3ec: refuses a whitespace-only reference on lookup / master_detail
#16685 4/2 4/0 ed7243d52: accepts boolean / toggle for sum / avg / min / max (decision batch #80)
#16867 3/2 2/1 0ee32edef: notNull / not_null prescribe storage.notNull, not required
#17014 3/2 2/1 80aef8032: the one-day date-range presets prescribe a one-day window, and the table states its end-token convention
#17286 1/1 1/0 dropped: a tracking card with no landing. The sentence now says the card is gone and to measure driver-memory for the open set
#17348 1/1 1/0 51efbf116: pins the driver-memory temporal text-operator divergence by name in that driver's conformance suite
#17590 1/1 1/0 e04a0aff2: $contains on a JSON column is a per-dialect membership test (director-seat ruling 2026-09-12)
#18012 8/3 7/1 176b03582: $between requires two non-blank endpoints (decision batch #146 item 5, letter A)
#19377 6/2 6/0 a60c913de: refuses a { $field } reference as a $between endpoint at the runtime filter door

Every cited sha matches exactly one commit (git rev-parse --disambiguate, count 1), and every one is an ancestor of the base (merge-base --is-ancestor, exit 0). That is 38 distinct shas.

Wordings to check, each true of its commit:

  • datasource.zod.ts:352 names only the card's mongo arm (90a12fb18) for "the defect class … closed", because the paragraph is about mongo. The card's mysql arm (72050cc47) is not cited anywhere in this stage.
  • datasource.zod.ts:354: 「the triage's, as commit d491625 landed them」. d491625c1's message lists the fences as "per triage".
  • filter.zod.ts:1021-1025: the #17286 pointer becomes 「was measured on a tracking card … That card is gone: measure driver-memory for the open set, ⛔ not this text.」 The warning that this paragraph is not the authority is kept.

The 41 string sites left as tokens

  • Test titles and test-code strings (39 sites). driver/driver-credential-refusal.test.ts 14, object.test.ts 6, datasource-credential-redaction.test.ts 3, driver/driver-placeholder-refusal.test.ts 3, filter.test.ts 3, api-derivation.test.ts 2 (the split('[#6259]') literal and its message), field.test.ts 2, and 1 each in date-range-presets.test.ts, driver/postgres.test.ts, field-rows-option-description.test.ts, filter-comparand-type.test.ts, hook.test.ts and object-strictness-batch20.test.ts.
  • Non-test strings (2 sites). aggregation-conformance.ts:398 and :407, the note of two exported AGGREGATION_CASES rows (#11065, #11151). They ship as data. Their only readers are driver conformance suites, which print a note as the assertion message when a case fails, to a driver developer and never to a metadata author. So they are neither comments nor form D author-shown text. This is the same disposition stage 1 gave the two why strings and stage 2 the PROVENANCE_WAIVERS reason.

No author-shown text in data/ carries a dead number, so nothing here is #20233's form D.

Mechanical guard: no code token moves

The check compares leaf tokens with comments stripped, base 9bf5e67af against head 96fd49caa2. It uses the TypeScript parser's leaf tokens, so template literals are scanned in context, and it excludes JSDoc nodes. It ran over all 45 touched .ts files.

  • Real run: 140,379 base tokens, 0 files with a token change (exit 0).
  • Comment-insertion control: 0 files changed, as expected (exit 0).
  • Positive control (a declaration inserted into feed.zod.ts): 1 file reads DIFFER (exit 1).
  • Positive control (one digit changed inside the split('[#6259]') string in api-derivation.test.ts): 1 file reads DIFFER (exit 1).

Changeset

This change ships bytes, so a patch changeset for @objectstack/spec is included. It says only that the provenance comments were re-anchored.

Measured on the built package: 14 of the touched sources are src/**/*.zod.ts, which files[] ships verbatim. The rewritten docblocks also reach dist. 88b9d749a, e2798fab7 and 24206416a each appear in 1 declaration file. 24206416a appears in 20 bundled .js files and 2f3e79351 in 28. The positive control, a pre-existing feed.zod.ts docblock sentence, appears in dist/data/index.d.ts.

Gates (head 96fd49caa2)

  • Citation judging pass, run as CI runs it: pnpm check:issue-citations && node scripts/check-issue-citations.mjs exits 0. The self-test passes 73 cases in 7 batteries. The live run judged 11 citations across 25 files, and all 11 resolve.
  • Doc authoring: pnpm check:doc-authoring exits 0.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at the final head derived 108 families, and all 108 exit 0. --ran reports 108 run, 0 NOT MEASURED, 0 unrun, and exits 0. (check:i18n was derived at the earlier heads from object.form.ts, and left the set when that file went back to base.)
    • At an earlier head, four gates first exited 3 (PREREQUISITE NOT MET) because the workspace was unbuilt: check:doc-formula-expressions, check:doc-security-posture, check:skill-examples and check:docs-transcript-drift. At the final head a full turbo run build of ./packages/* ran first (71 tasks, exit 0, under the shared verify lock), and every gate exited 0 on its first run.
    • check:generated was run under the lock against that build: all 15 artifacts are up to date.
  • Build, tests, typecheck and lint:
    • pnpm --filter @objectstack/spec build exits 0.
    • vitest run --maxWorkers=2 src/data in packages/spec at the final head: 107 files and 3,517 tests pass (1 todo), covering every touched test file.
    • The 12 spec suites outside src/data that read data/ source text pass at the final head: 12 files, 503 tests. These are scripts/{file-description,root-index,skill-map-guards,strictness-ledger}.test.ts, src/api/api-entry-graph.pin.test.ts, src/contracts/scoped-context.test.ts, src/shared/{alias-integrity,evaluated-slot-population,retired-key-migrate-sentence}.test.ts, src/system/constants/platform-object-names.test.ts, src/type-alias-convention.pin.test.ts and src/ui/dashboard.test.ts.
    • pnpm --filter @objectstack/spec typecheck at the final head exits 0, including check:test-typecheck (53 files, 251 errors, 138 pinned signatures held).
    • Lint, as a proven narrowing at the final head: eslint --no-inline-config --format json over the 45 touched .ts files gives 45 files, 0 errors and 0 warnings. All 45 are in eslint's own population (isPathIgnored is false for each). eslint.config.mjs never enables type-aware linting (no parserOptions.project, which its own line 328 states), so a comment edit here cannot move the verdict on any untouched file. The repo-wide pnpm lint is CI's run.

Acceptance notes


Generated by Claude Code

…its that decided them (stage 3)

Every comment and docblock site under packages/spec/src/data that cited a
tracker number answering 404 now cites the commit in this repository's
history that decided what the line describes, and says in words what that
commit decided. The files an open PR or an in-flight claim holds are left
out. Comment-only: every file keeps its line count and no code token or
test string moves.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
…geset (stage 3)

check:docs proved content/docs/references/data/feed.mdx stale: two lines
project from the rewritten feed.zod.ts docblock. check:generated --fix
regenerated only that artifact. The rewritten docblocks ship in
src/**/*.zod.ts and dist, so @objectstack/spec takes a patch changeset.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
… anchor the sentence one line down

api-derivation.test.ts splits DATA_ACTION_TO_API_OPERATION's TSDoc on the
literal `[#6259]`, so that marker stays. Writing the deciding commit onto the
marker's own line made the diff-scoped check-issue-citations verdict read the
kept number as an added citation; the commit now sits on the next line of the
same sentence, which carries no number.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
…it (stage 3)

The #20437 PR opened after this stage's claim and edits
packages/spec/src/data/driver/turso.test.ts, so that file joins the stage's
exclusions beside driver/turso.zod.ts. Its two comment sites return to the
base bytes (blob 7fe99eb) and wait for a later stage.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
…t (stage 3)

The #20432 PR opened after this stage's claim and edits
packages/spec/src/data/object.form.ts, so that file joins the stage's
exclusions. Its one comment site returns to the base bytes (blob 60713e0)
and waits for a later stage.

Claude-Session: https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx
Co-authored-by: Claude <noreply@anthropic.com>
…e open PRs that now hold them (stage 3)

The #20494 PR (object.zod.ts) and the #20444 PR (filter-logic-conformance.ts)
opened after this stage's claim, so both files join the stage's exclusions.
Their comment sites return to the base bytes (blobs befde04 and c9b32ac)
and wait for a later stage.

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/spec, touching 17 documentable anchor(s). ⚠️ 13 changed file(s) yielded no anchor (packages/spec/src/data/aggregate-field-type-compatibility.ts, packages/spec/src/data/api-derivation.ts, packages/spec/src/data/datasource-credential-redaction.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/concepts/metadata-driven.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/drivers.mdx (via MongoConfigSchema (symbol, a top-level const), MysqlConfigSchema (symbol, a top-level const))
  • content/docs/data-modeling/external-datasources.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/field-types.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/data-modeling/validation-rules.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/deployment/troubleshooting.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/deployment/validating-metadata.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/getting-started/quick-reference.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/kernel/contracts/data-engine.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/backward-compatibility.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/objectql/types.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/protocol/objectui/concept.mdx (via FieldSchema (symbol, a top-level const))
  • content/docs/ui/forms.mdx (via FieldSchema (symbol, a top-level const))

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

  • content/docs/releases/v17/17-0.mdx (via DatasourceSchema (symbol, a top-level const), FieldSchema (symbol, a top-level const), HookContextSchema (symbol, a top-level const))
  • content/docs/releases/v17/17-1.mdx (via FieldSchema (symbol, a top-level const))

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

What this run could not see
  • 13 changed file(s) yielded no anchor (packages/spec/src/data/aggregate-field-type-compatibility.ts, packages/spec/src/data/api-derivation.ts, packages/spec/src/data/datasource-credential-redaction.ts, …) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 137 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 1378ec7c0cce7f77155ec84eec06ab41e0ec9268 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 1378ec7c0cce7f77155ec84eec06ab41e0ec9268

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 96fd49caa2e88a14b1c4a598323b5cf245c79e20
Local-runs: none

Read: card #20234 (body; every comment: triage 5856637615, the seat-2 pointer 5858331362, the stage-1 claim 5859418643, report 5860236501, ACCEPT 5860414140 and landing 5860571318, the stage-2 claim 5860586561, report 5861177436, contract review 5861396181, ACCEPT 5861418146 and landing 5861600944, the stage-3 claim 5876941555 and report 5881236928), PR #20533 (body, its one comment 5881210559, the 47-file list, the six commits and the net diff against the merge base 9bf5e67af, read from the local ref at the head above), the 38 cited commits (subject, message and stat for all; the diff wherever a rewritten line claims more than the subject), packages/spec/package.json files[], the citation gate's grammar and deferred surfaces, lint.yml's job roster, and the other 11 open PRs' file lists. Nothing was built, run or re-run; the diff was read with git, the anchors with git show, the numbers with grep. The check-runs were read for judgment once, at 2026-09-29T00:36:35Z; an input-gathering read nine minutes earlier, right after the PR opened, is not repeated here.

① Derived judgments

(a) Scope and file surface: right. 47 files: 45 .ts under packages/spec/src/data/**, the generated content/docs/references/data/feed.mdx, and .changeset/spec-data-provenance-anchors.md. Every claim exclusion is at the base blob on the head (data-engine.zod.ts, data-engine.test.ts, hook.form.ts, all five analytics* files, cube-member-inner-name-retirement.test.ts, driver/turso.zod.ts, filter-subtree-provenance.ts: base oid equals head oid, 11 of 11), and so are the four post-claim exclusions (driver/turso.test.ts 7fe99ebf9, object.form.ts 60713e06f, object.zod.ts befde04ca, filter-logic-conformance.ts c9b32acba). PR #20504 does edit driver/turso.test.ts and driver/turso.zod.ts, PR #20519 does edit object.form.ts, and object.zod.ts and filter-logic-conformance.ts have moved on origin/main since the merge base, so the four holds were real. The 12 open PRs at the judging read (11 plus #20536, opened after the dev's re-read) touch none of this diff's 47 files. origin/main is 23 commits ahead of the merge base; of the five data/ files it moved, none is in this diff. No governed surface is touched; 363 changed lines.

(b) Comment-only, no test string moves: right. git diff -U0 over the 45 .ts files: 174 lines added, 174 removed, and every added or removed line begins with a comment marker except one pair in object.test.ts:313, where the code half { revoked_at: { $nin: [null] } }, is byte-identical on both sides and only its trailing // comment changes. So no string literal, test title or code token moves anywhere in the diff, the 20 test files included; the dev's parser-level token comparison is not re-run and not needed for that verdict. Every file keeps its line count. Read by eye in full: datasource.zod.ts, driver/config-registry.zod.ts, driver/common.zod.ts, driver/driver-credential-refusal.test.ts, object.test.ts, filter.zod.ts, value-roundtrip-conformance.ts, api-derivation.ts and its test, feed.zod.ts, mapping.zod.ts, datasource-credential-redaction.ts, aggregation-conformance.ts, aggregate-field-type-compatibility.ts, and every remaining hunk.

(c) Anchor truth: right. 38 distinct sha-shaped tokens stand on added lines and none on removed lines; every one is a commit, rev-parse --disambiguate answers exactly one object for each, and all 38 are ancestors of origin/main at 31d281d3b2 (38 of 38). For all 39 sha-to-number pairs in the dev's table, the commit's own message or diff names the number it replaces (grep over git show; the message alone for 30, the diff for the rest). Sampled beyond the subject, across different numbers:

(d) Form C and D: right. Added-minus-removed tracker numbers is empty for every file, tests included: 50 distinct numbers on removed lines, 40 net-removed (163 sites), the other 10 (#7596, #7990, #8082, #8336, #8586, #9138, #9147, #11151, #11535, #14477) re-appear exactly as often as they were removed, on their own lines. No PR #N stands on any added line. Each rewrite says in words what the commit decided, in the form the stage-1 and stage-2 precedents used (commit 9-hex, or [commit 9-hex] where the marker form was used). The #17286 drop is within the ruling: no commit on origin/main names #17286 as a landing (the only two mentioning it are e04a0af, which cites the tracking card, and PR #20226's commit), the string entered the tree with e04a0af's own docblock, and filter.zod.ts:1021-1025 keeps the reason in words (a tracking card that recorded the semantics as undecided; measure driver-memory for the open set) while the warning that the paragraph is not the authority stays. Nothing author-shown is touched, so form D does not arise here. check:issue-citations runs in Lint & Repo Gates (success at the read); the 20 test files it defers are covered by the added-minus-removed count above.

(e) feed.mdx projection: right. Of every removed docblock line in the diff, exactly two fragments occur in a reference page at the base, both in feed.mdx (feed.zod.ts:15 and :18), and the page's two changed lines are those substitutions verbatim with the * prefix stripped; no other page under content/docs/references/ holds text this diff rewrote. driver-turso.mdx:24 still reads #6345, and rightly: it projects from the excluded driver/turso.zod.ts, unchanged here. check:docs runs in Type Check · source gates, success at the read, which is the generator's own verdict that the committed page equals its output at this head.

(f) Left-behind sites: right. api-derivation.test.ts:236 splits the TSDoc on the literal [#6259] and :237 asserts the history half is non-empty, so api-derivation.ts:163 is byte-identical and the commit sits on :164 (a line that held no number). field.zod.ts:370 reads objectui#6010 (field) and objectui#6110 + #6111 (section): the sentence's own grammar makes #6111 objectui's, so it is not a dead citation of this repository and leaving it is right (the #20330 grammar family, carrier named). aggregation-conformance.ts:398 and :407 are note strings of exported AGGREGATION_CASES rows: string tokens, which the claim's no code token moves already forbids touching, and their only readers print them as vitest assertion messages (memory-aggregation-conformance.test.ts:153, :164; mongodb-aggregation-translation.test.ts:113), never to an author; the same disposition stages 1 and 2 gave their shipped strings. The test titles #8656 (field.test.ts:1433) and #12868 (field-rows-option-description.test.ts:192) are strings, left by the claim's rule.

(g) Census arithmetic: the diff's own count is 163 sites over 40 numbers in 44 files, equal to the dev's totals row by row; four of the numbers were spot-probed (#8696, #17286, #9041, #11065 answer 404; controls #16862 200, #16714 404). The dev's REST census is otherwise not re-run.

② Semver level

patch for @objectstack/spec is right and Clause-②: no is right. The package ships bytes from this diff: files[] carries dist and src/**/*.zod.ts, and 14 of the 45 touched sources are .zod.ts files that ship verbatim, with their docblocks reaching dist. (b) shows no export, key, value or type moves, so nothing widens or narrows: the level that publishes docblock text and no surface is patch, the same level stages 1 (21ab410417) and 2 (5cf58eb164) took for the same act. The changeset names one package, carries no tracker number and no model identifier, and Check Changeset is success at the read.

③ Boundary flags

Blocking: none.

Dev flags, each answered:

  1. Four exclusions beyond the claim's list: measured right in (a); the two files whose PRs have since landed (object.zod.ts 15 sites, filter-logic-conformance.ts 3 sites) are a later stage's, with the anchors recorded in the PR body.
  2. The [#6259] marker line kept byte-identical, the sha one line down: verified in (f).
  3. Thirteen collateral lines with no number (reflow halves, the card or commit table header at value-roundtrip-conformance.ts:20, the :164 carrier): all comment text, all inside the 174-for-174 balance.
  4. The census instrument and the earlier-head gate incidents (exit 3 before the closure build, a runner timeout, lock queue timeouts): none is a verdict; the head's check-runs answer every family that matters here.
  5. The dev unshallowed the shared repository and fetched a scratch ref: the checkout reads not shallow now, and refs/os-review/ is empty, so the ref was deleted as stated. A side effect on the common .git, benign, not this diff's; noted.
  6. Commit trailers: all six commits carry the model-free pair Claude-Session plus Co-authored-by: Claude, no model identifier.
  7. No merge of origin/main: right; 23 behind at the read, 0 of them touching a file in this diff.
  8. Six commits, no force-push; 9. worktree cleanup: not observable from here, not this diff's.
    Open questions: the report lists none. Out-of-scope findings: the [finding] check-issue-citations reads the pre- in pre-#N (and post- in post-#N) as a repository qualifier, so a dead number in that spelling is classed cross-repo and never judged #20330 grammar family (capitalised qualifiers, objectui#6110 + #6111) has its carrier named; the two note strings and the two freed files go to a later stage of this card.

Non-blocking wording, each true of its commit: mapping.zod.ts:89-90 now says the triage ruling that commit 15d58db landed confirms the declined dialect, and that commit's own changeset and docblock record exactly that (the #10329 triage ruling declined again, confirms that posture). field-value.zod.ts:125 credits 51efbf1 with the pinning, which is exact, though the divergence it pins was filed as its own card. datasource.zod.ts:352 names 90a12fb for the defect class closed for mongo only, which is the arm the paragraph is about. The PR's docs-drift comment 5881210559 flags 13 changed files with no documentation anchor; it is the bot's advisory on a comment-only diff, and Flag docs affected by code changes is success.

Escalated: none.

CI at this head, the judging read: 34 check-runs, 31 success, 2 skipped (Console Pin Gate, Packed-tarball smoke (opt-in)), 0 failure, 1 not concluded: Test Core (5/6) was still in progress and its Test Core aggregate had not yet been created, so the required Test Core context is NOT presumed green here and must be read concluded before the PR is armed. The other required contexts are success: Lint & Repo Gates, TypeScript Type Check (with its four lanes), Dogfood Regression Gate, Build Core, Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard. No red run exists to attribute. Gate families the dev derived (108) are answered here by Lint & Repo Gates (the repo check:* steps, pnpm lint, the diff-scoped citation pass, doc-authoring), Type Check · source gates (the spec check:generated family: docs, api-surface, authorable-surface, liveness and siblings), Type Check · workspace (typecheck), Type Check · debt ledger, Type Check · consumer gates, Build Core, the Test Core shards, Spec property liveness and Check Changeset. PR is a draft, mergeable_state blocked (draft), first line Part of #20234, no closing keyword, Clause-②: no present.

Implemented-by: claude/issue-20234-dead-citations-data
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 00:42
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 03b19d9 Sep 29, 2026
37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20234-dead-citations-data branch September 29, 2026 01:03
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
… commits that decided them (stage 4) (objectstack-ai#20548)

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

## What changed

This is stage 4 of the staged sweep: the `data/` remainder. It covers
the six `packages/spec/src/data/` files stage 3 (PR objectstack-ai#20533, landed
`03b19d9cfd`) left out because an open PR held them, and nothing else.
They are `object.zod.ts`, `filter-logic-conformance.ts`,
`object.form.ts`, `data-engine.zod.ts`, `data-engine.test.ts` and
`hook.form.ts`. Later stages cover the other areas, so this PR says
`Part of`.

The census below measured all six. Three of them carry comment or
docblock sites that cite a tracker number answering 404.
`data-engine.zod.ts`, `data-engine.test.ts` and `hook.form.ts` carry
none, so they are not in the diff.

Every such site has been rewritten in ruling C+D's form C (comment
5749154545 on objectstack-ai#19123). That is **19 sites on 19 lines in 3 files,
covering 9 numbers**. Each rewritten line now cites the commit in
`origin/main` history that decided what the line describes, and it says
in its own words what that commit decided. Where a PR number was already
on the line (`PR objectstack-ai#13529`), it stays beside the commit as the link.

No ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/`
records the decision behind any of the 9 numbers: a search for each
number, with and without `#`, finds nothing there. So every anchor is a
commit: **9 distinct shas**. Stage 3 had already read these commits and
recorded them in PR objectstack-ai#20533's body. They were not copied from there. Each
one was re-read against the current line it anchors: its own message or
diff names the number it replaces, and it made the change the line
describes. `object.zod.ts` and `filter-logic-conformance.ts` moved on
`main` after stage 3 read them (PRs objectstack-ai#20521 and objectstack-ai#20523). Each site was
therefore re-read at this base, `03b19d9cfd`.

Only comments changed. Every source file keeps its line count (20 lines
out, 20 in, over 3 files), so no line citation into these files moves.
One of the 20 lines held no dead citation:
`filter-logic-conformance.ts:249`, the first half of a sentence reflowed
onto `:250`. No code token moves (see the guard below).

**No tracker number is added.** Every tracker number on an added line
was already in the hunk it replaces. `PR objectstack-ai#13529` stands on three added
lines, and on the three removed lines of the same hunks. It is the link
beside commit `9dac1ae01`, which stage 3 recorded the same way.

No reference page under `content/docs/references/` moved: none of the
rewritten docblocks projects into one (`check:docs` at the head: `226
generated files in sync`). The PR adds one `patch` changeset for
`@objectstack/spec` (see Changeset below).

## Census: the six files, before and after

**Instrument.** This is the instrument of stages 1 to 3. It sends REST
`GET /repos/objectstack-ai/objectstack/issues/N` without following
redirects, for every distinct number cited in `packages/spec/src/data`.
The population is:
- the citation gate's own exported `CITATION_RE` and
`NON_CITATION_HEADS`, kept when the qualifier is none, `objectstack`,
`objectstack-ai/objectstack`, `framework`, `pre-` or `post-`;
- widened case-insensitively to `Pre-`, `POST-` and `Framework`, as in
stage 3;
- N of 100 or more, excluding `summon` heads.

Each site is classified by the TypeScript parser as a line comment, a
docblock, a block comment or a string.

Two cross-checks close the population. First, a raw `#N` count in each
of the six files equals the census rows plus the cross-repo rows in five
files. In the other two it is one higher, and the extra is a second
number after a slash inside a string (`objectstack-ai#5322/objectstack-ai#5134` in a `note`,
`objectstack-ai#6262/objectstack-ai#6433` in a test title). Both answer 200. Second, no spelled
citation (`issue N`, `PR N`, `card N`) occurs in any of the six.

**Controls.** The lit controls were `objectstack-ai#16862`, `objectstack-ai#16847` and `objectstack-ai#17698`. The
dead controls were `objectstack-ai#16714`, `objectstack-ai#16715` and `objectstack-ai#16697`. They were probed at
the start, after every 100 numbers and at the end: 24 of 24 lit (200)
and 24 of 24 dead (404) over 8 checkpoints in the base run, and 21 of 21
lit and 21 of 21 dead over 7 checkpoints in the head run.

| reading | tree | numbers probed | 200 | 404 | 301 or other | dead
sites, all of `data/` | dead sites, the six files | lines | files |
numbers |
|---|---|---|---|---|---|---|---|---|---|---|
| before | base `03b19d9cfd`, probed 2026-09-29T01:11:59Z to 01:15:49Z |
601 | 572 | 29 | 0 | **77** | 19 | 19 | 3 | 9 |
| after | head `53c9070dfd`, probed 2026-09-29T01:25:55Z to 01:29:35Z |
597 | 572 | 25 | 0 | **58** | 0 | 0 | 0 | 0 |

The head probe found no number newly dead since the base probe: the same
572 numbers answer 200. The base reading of 77 equals stage 3's after
reading at `96fd49caa2`.

**Per file.** Cited sites here are every in-repo citation the population
reads, live or dead.

| file | cited sites (base) | dead sites before | by class | dead sites
after |
|---|---|---|---|---|
| `object.zod.ts` | 120 | 15 | 8 docblock, 7 line comment | 0 |
| `filter-logic-conformance.ts` | 97 | 3 | 2 docblock, 1 line comment |
0 |
| `object.form.ts` | 31 | 1 | 1 line comment | 0 |
| `data-engine.zod.ts` | 48 | 0 | | 0 |
| `data-engine.test.ts` | 29 | 0 | | 0 |
| `hook.form.ts` | 0 | 0 | | 0 |

None of the 19 sites is a string, so this stage leaves no string token
behind.

## Per-number table

| number | sites / lines | anchor: what it decided |
|---|---|---|
| `objectstack-ai#8772` | 4 / 4, `object.zod.ts:2718`, `:2731`, `:2744`, `:2910` |
`75b7c240a`: Direction 2 of the 2026-08-16 maintainer ruling.
`ObjectSchema.create()` forces `required: true` on a `master_detail`
reference under `controlled_by_parent` and refuses an explicit
`required: false`. Raw parse stays tolerant, and runtime tolerance is
the ruling's other half. Its changeset records the measurement that only
the security gate closed that shape while the declaration surface
accepted it (`:2731`). ADR-0055 stays cited beside it. It is the same
anchor stage 3 gave `object.test.ts` |
| `objectstack-ai#10165` | 2 / 2, `object.zod.ts:818`, `:1036` | `801296050`:
`ttl.onlyWhen` with the canonical null predicate (maintainer ruling
2026-08-20, option A). One shared `onlyWhen` union, and both of
`retention.onlyWhen`'s conflicts mirrored. Its diff wrote both
`[objectstack-ai#10165]` blocks |
| `objectstack-ai#10347` | 3 / 3, `object.zod.ts:1006`, `:1042`, `:1049` |
`530c1df65`: the Archiver honours a declared `ttl`. It selects by the
ttl cutoff on `ttl.field` when `ttl` is declared, and by `created_at` /
`archive.after` otherwise (maintainer ruling 2026-08-20) |
| `objectstack-ai#10527` | 1 / 1, `object.zod.ts:1005` | `5649efbf9`: refuses a
diverging retention + ttl + archive triple at parse time. Its diff wrote
this very paragraph |
| `objectstack-ai#11195` | 1 / 1, `object.zod.ts:1791` | `b37231883`:
`UserActionsConfigSchema` adopts `group` / `hideFields` / `rowColor`
(the "last three" the line names) |
| `objectstack-ai#11408` | 1 / 1, `object.zod.ts:2189` | `f11fc61c5`: declares
`editMode` on the object document (maintainer ruling 2026-08-24, the
`objectstack-ai#10144` declare-or-rule-out family, which stays cited) |
| `objectstack-ai#13608` | 3 / 3, `object.zod.ts:2317`, `:2354`, `:2366` |
`fc9ba76a5`: `publicSharing.eligibility` is held at redemption, not only
at mint, fail-closed, with the undifferentiated `null` refusal. Its
changeset heads with objectstack-ai#13608. It is the same anchor stage 1 gave
`contracts/share-link-service.ts` |
| `objectstack-ai#13195` | 3 / 3, `filter-logic-conformance.ts:190`, `:250`, `:525` |
`9dac1ae01`, PR objectstack-ai#13529's squash commit, which stays as the link:
`$exists` means has-a-value on driver-memory's live mingo path, its
analytics face and driver-mongodb's `translateFilter` (the "last three
key-presence exits") |
| `objectstack-ai#12868` | 1 / 1, `object.form.ts:256` | `c459da6bc`: narrows the
per-option `default` key out of the form-view options vocabulary, which
offered a key nothing on that surface read. Commit `e808890958`, which
wrote this line, names objectstack-ai#12868 as the same offer-vs-door class |

The shas were checked at the base and again at `origin/main`
`288611e3e5`. Every one matches exactly one commit (`git rev-parse
--disambiguate`, count 1). Every one is an ancestor (`git merge-base
--is-ancestor`, exit 0 for 9 of 9). The control leg `e9584681a4` also
exits 0, and the repository is not shallow. For each commit, a grep of
its own message or diff finds the number it replaces. Seven of the nine
name it in the message. `fc9ba76a5` names it in its diff (20 lines,
including its changeset heading), and so does `c459da6bc` (8 lines,
including its changeset heading).

Wordings to check, each true of its commit:
- `object.zod.ts:2731` now reads 「closes that shape, and commit
75b7c24 records that the declaration and the enforcement disagree」.
The measurement was the card's. The commit's changeset records it: "only
the security gate closed that shape while the declaration surface
accepted it".
- `object.zod.ts:2189` reads 「Declared here by commit f11fc61's
maintainer ruling」, and `:2744` reads 「the other half of commit
75b7c24's ruling」. This is stage 3's wording for the same relation
(`object.test.ts`, 「the other half of commit 75b7c24's ruling」): the
commit that landed the ruling and quotes it.
- `object.zod.ts:1049` reads 「That is the whole of what [commit
530c1df] changed here」. Commit `52db1d1f2a` wrote the paragraph.
`530c1df65` is the change it describes.

## Mechanical guard: no code token moves

The check compares leaf tokens with comments stripped, base `03b19d9cfd`
against head `53c9070dfd`. It uses the TypeScript parser's leaf tokens
(TypeScript from the head's lockfile), so template literals are scanned
in context, and it excludes JSDoc nodes. It ran over all 3 touched `.ts`
files. It is the stage-3 instrument, unchanged.

- Real run: 13,624 base tokens (object.zod.ts 8,774, object.form.ts
3,226, filter-logic-conformance.ts 1,624), **0 files with a token
change** (exit 0).
- Comment-insertion control (`object.form.ts`): 0 files changed, as
expected (exit 0).
- Positive control (a declaration inserted into `object.zod.ts`): 1 file
reads DIFFER at token 1629 (exit 1).
- Positive control (one digit changed inside the `objectstack-ai#5322/objectstack-ai#5134` `note`
string in `filter-logic-conformance.ts`): 1 file reads DIFFER at token
889 (exit 1).

Line balance: `object.zod.ts` +15 / -15, `filter-logic-conformance.ts`
+4 / -4, `object.form.ts` +1 / -1. Line counts are equal at base and
head: 3,240, 621 and 751.

## Changeset

This change ships bytes, so a `patch` changeset for `@objectstack/spec`
is included. It says only that the provenance comments were re-anchored.
`Clause-②: no`: no export, key, value or type moves (the guard above).

Measured on the head's built package: `object.zod.ts` is
`src/**/*.zod.ts`, which `files[]` ships verbatim. The rewritten
comments also reach `dist`:
- `9dac1ae01` appears in `dist/data/index.d.ts` (the
`filter-logic-conformance.ts` docblock) and in 4 bundled `.js` files;
- `fc9ba76a5`, `f11fc61c5` and `b37231883` each appear in 22 bundled
`.js` files, and `c459da6bc` in 12;
- the positive control, the pre-existing `object.zod.ts` sentence
「Fail-CLOSED at both points」, appears in 11 bundled `.js` files.

## Gates (head `53c9070dfd`)

- **Citation judging pass, run as CI runs it:** `pnpm
check:issue-citations && node scripts/check-issue-citations.mjs` exits
0. The self-test passes 73 cases in 7 batteries. The live run judged 6
citations across 3 files: 3 resolve (`objectstack-ai#9138` twice, `objectstack-ai#11410`) and 3
resolve as a pull request (`objectstack-ai#13529`, the link).
- **Doc authoring:** `pnpm check:doc-authoring` exits 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at the head derived 79 families, and
all 79 exit 0. `--ran` reports 79 run, 0 NOT MEASURED, 0 unrun, and
exits 0. A full `turbo run build` of `./packages/*` ran first, under the
shared verify lock: 71 of 71 tasks, VERDICT command-exit 0. So no gate
met an unbuilt prerequisite.
- `pnpm --filter @objectstack/spec run check:generated`: under the lock
against that build, `All 15 generated artifacts are up to date`, VERDICT
command-exit 0.
- **Tests and typecheck:**
- `pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2
src/data` under the lock: Test Files 107 passed (107), Tests 3527
passed, 1 todo (3528), VERDICT command-exit 0. It covers every test in
`data/`, among them `object.test.ts`, which reads these schemas.
- The 13 spec suites outside `src/data` that read the touched files'
source text or pin their line numbers, under the lock: Test Files 13
passed (13), Tests 544 passed (544). They are stage 3's 12
(`scripts/{file-description,root-index,skill-map-guards,strictness-ledger}.test.ts`,
`src/api/api-entry-graph.pin.test.ts`,
`src/contracts/scoped-context.test.ts`,
`src/shared/{alias-integrity,evaluated-slot-population,retired-key-migrate-sentence}.test.ts`,
`src/system/constants/platform-object-names.test.ts`,
`src/type-alias-convention.pin.test.ts`, `src/ui/dashboard.test.ts`)
plus `src/shared/union-author-message-pins.test.ts`, which pins
`data/object.zod.ts:855`.
- `pnpm --filter @objectstack/spec typecheck` under the lock exits 0,
including `check:test-typecheck` (53 files, 251 errors, 138 pinned
signatures held).
- **Lint, as a proven narrowing at the head:** `eslint
--no-inline-config --format json` over the 3 touched `.ts` files gives 3
files, 0 errors and 0 warnings. All 3 are in eslint's own population
(`isPathIgnored` is false for each). `eslint.config.mjs` never enables
type-aware linting (no `parserOptions.project`, which its own line 328
states), so a comment edit here cannot move the verdict on any untouched
file. The repo-wide `pnpm lint` is CI's run.

## Acceptance notes

- **Base.** The branch forked from `03b19d9cfd`, stage 3's landing.
`origin/main` then moved two commits (`05077d4c26`, PR objectstack-ai#20532, and
`288611e3e5`, PR objectstack-ai#20536), and neither touches `data/`. `dispatch-gates`
flagged its derivation as stale because `scripts/regen-artifacts.mjs`
had moved, so `origin/main` was merged in (`53c9070dfd`, a clean merge
with no driver-deferred path) before the gates ran. The PR's delta
against `origin/main` is exactly its 4 files. `origin/main` has since
moved two more commits: `7e36a3cd7c` (PR objectstack-ai#20531) and `ba5927f714` (PR
objectstack-ai#20460). Neither touches `data/` or anything the gate derivation reads,
and a re-derivation prints the same 79 commands. A no-driver
`merge-tree` of the head onto `ba5927f714`, from a bare shared clone,
exits 0. So there is no second merge.
- **Open PRs, re-read at 2026-09-29T02:01Z:** 9 open PRs, and none
touches any of the six files. The `data/` files open PRs touch are
objectstack-ai#20458's `analytics*` files, objectstack-ai#20504's `driver/turso.*`, and objectstack-ai#20545's
`filter-number-comparand-declared-type.*`, which is disjoint. Since the
claim, PR objectstack-ai#20460 has landed (`ba5927f714`) without touching
`filter-subtree-provenance.ts`. That file's 3 dead sites are outside
this claim's fence, so they are left for a later stage.
- **The rung.** Two anchored changes also have ADR-0087 entries in
`packages/spec/src/migrations`: `cbp-master-detail-required-forced` for
objectstack-ai#8772, and `form-view-option-default-retired` for objectstack-ai#12868. The second
entry's own header names commit `c459da6bc`. This PR takes the commit
rung, as stages 1 to 3 did. The D3 id is the more durable in-repo
record, if the ruling's first rung is later read to include those
entries.
- **What stays in `data/` after this stage: 58 dead sites.**
- **12 comment sites in files other open work still holds.**
`analytics.zod.ts`, `analytics-strictness-batchd.test.ts` and
`analytics-date-range-two-bound-window.test.ts` hold 5 (objectstack-ai#20300, PR
objectstack-ai#20458). `driver/turso.zod.ts` and `driver/turso.test.ts` hold 4
(objectstack-ai#20437, PR objectstack-ai#20504). `filter-subtree-provenance.ts` holds 3. It was held
by objectstack-ai#20367 and is now free (see above).
- **3 comment sites stage 3 left on purpose.** They are the test-read
`[objectstack-ai#6259]` marker at `api-derivation.ts:163`, the test comment at
`api-derivation.test.ts:232` that names it, and `field.zod.ts:370`,
whose `objectstack-ai#6111` is objectui's number.
- **43 string sites**, left as tokens: 41 test strings (2 of them in the
held analytics and turso test files) and the 2 exported
`AGGREGATION_CASES` note strings in `aggregation-conformance.ts`
(`:398`, `:407`, objectstack-ai#11065), which objectstack-ai#20489's claim holds.
- **Outside `data/`,** the card's other remaining items are unchanged:
the migrations and ui areas, the `liveness/**` notes, the `why` strings,
the `PROVENANCE_WAIVERS` reason, and `rest-server.zod.ts`.
- **The citation gate's reach.** It defers `packages/**/*.test.ts`. No
test file is touched here, so all 3 touched files are in its judging
population.

---
_Generated by [Claude
Code](https://claude.ai/code/session_014EJ1ED8X4MMrT18BhVx4tx)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…commits that decided them (objectstack-ai#20609)

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

## What changed

This is the first stage of the `domain:services` lane of the
dead-citation sweep. It covers
`packages/services/service-messaging/src/**` and nothing else, the
largest package in the lane that no open PR or in-flight claim holds
(the claim, `5884863234`, gives the order). Later stages cover the other
packages, so this PR says `Part of` and the card stays open.

Every comment or docblock site in scope that cited a tracker number
answering 404 has been rewritten in ruling C+D's form C (comment
5749154545 on objectstack-ai#19123), the way the landed `packages/spec/src` stages
apply it (PR objectstack-ai#20533 is the method). That is **127 sites on 109 lines in
28 files, covering 14 numbers**: the 97 census sites outside the
generated headers, and 30 sites in test comments, which the census
defers. Each rewritten line now cites the commit in `origin/main`
history that decided what the line describes, and it says in its own
words what that commit decided.

No ADR or ruling-record file in `docs/adr/` or `scripts/adr-anchors/`
records the decision behind any of the 14 numbers, so every anchor is a
commit: **13 distinct shas**. No number was dropped.

Only comments changed. Every touched source file keeps its line count
(116 lines out, 116 in, over 28 files), so no line citation into these
files moves. Seven of those 116 lines held no dead citation: they are
the other half of a sentence that had to be reflowed
(`inbox-caller.ts:87`, `:88`, `messaging-service.test.ts:972`,
`notification-keyed-text-bounds.test.ts:83`,
`notification-subscription.object.ts:81`, `:82`), or a pointer that lost
its referent (`sql-outbox.ts:281`, 「the race the card describes」 to 「the
race that commit describes」, because line 278 now names the commit). No
code token moves (see the guard below).

**No citation number is added.** Every tracker number on an added line
was already on the line it replaces (added-minus-removed over the whole
diff: 0). No PR number stands on an added line.

Fifteen dead sites are left on purpose: 12 string literals and 3
generated file headers (see the list below).

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

## Census: `service-messaging`, before and after

**Instrument (A1).** The gate's own `node
scripts/check-issue-citations.mjs --census --json`, read-only,
unchanged. Its surface is comment prose in `packages/**/src/**/*.ts`
with string literals blanked, and it defers `*.test.ts`. The count below
is its `allocated-but-absent` findings under
`packages/services/service-messaging/`.

| reading | tree | board | whole-repo `allocated-but-absent` |
service-messaging sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `7a1faf1a5`, run 2026-09-29T06:31:54Z to 06:35:25Z |
enumerated, 185 pages, frontier objectstack-ai#20606, 18,433 numbers | 2,457 | **100**
| 82 | 22 | 13 |
| after | head `685200760`, run 06:48:33Z to 06:52:17Z | enumerated, 185
pages, frontier objectstack-ai#20606, 18,433 numbers | 2,360 | **3** | 3 | 3 | 1 |

The before count matches the 100 that census `5884031174` read at
`f11b5f20`. The whole-repo drop is 97, exactly this diff's census sites,
and the `resolves` tally is 32,744 in both runs. The 3 left are the
generated headers below. `267c11562`, the final head, adds only the
changeset, which is outside the census surface.

**Supplementary instrument, the whole scope.** The census does not read
test files or strings, and this stage's scope includes both. So a second
reading runs the gate's own exported `extractCitations` (whole-file and
comment-prose projections) and `classifyCitation` over every `.ts` file
under `service-messaging/src` (87 files), against the same enumerated
board.

| reading | citations | dead | src comment | test comment | src string |
test string |
|---|---|---|---|---|---|---|
| before, `7a1faf1a5` | 613 | **142** | 100 | 30 | 3 | 9 |
| after, `685200760` | 486 | **15** | 3 | 0 | 3 | 9 |

Its src-comment column equals the census's 100, which is the control on
the second instrument. The 450 resolving citations and 21 pull-request
citations are the same in both readings.

## Per-number table

Sites and files are all dead sites in scope at the base (comments and
strings, tests included). `rewritten / left` counts comment sites
rewritten and sites left. Every anchor was read in its diff or message,
not only in its subject: it is the commit that made the change the line
describes, and its own diff or message names the number it replaces.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#6206` | 1/1 | 1/0 | `8e13ca876`: the share-link routes pass the
whole authz envelope into enforcement instead of a four-field trim. The
line lists it as one member of the defect family behind
`assembleExecutionContext` |
| `objectstack-ai#6363` | 14/2 | 13/1 | `17d095413`: `listInbox`'s `unreadCount`
counts the total unread, not the fetched window (maintainer ruling
2026-08-07, Option A: make the declaration true); it adds
`countUnreadTotal`. The same anchor the spec stages gave this number |
| `objectstack-ai#9722` | 1/1 | 1/0 | `2074b2651`: corrects the
`sys_notification_subscription` index note — `role:` and `team:` resolve
against `sys_member` and `sys_team_member` |
| `objectstack-ai#9807` | 4/3 | 4/0 | `44738f7af`: marks the subscription-to-recipient
expansion NOT WIRED and aligns `principal` with the forms
`RecipientResolver.resolveOne()` accepts, email kept verbatim |
| `objectstack-ai#11374` | 17/6 | 16/1 | route A of the maintainer's 2026-08-24
ruling: a keyed text column declares a `maxLength` sourced from its
producer. Written as 「route A, ruling 2026-08-24」 beside `e4902d2b9`,
the commit that applied it here. `scripts/check-keyed-text-bounds.mjs`'s
header states route A in words |
| `objectstack-ai#11452` | 6/3 | 5/1 | `3b5f0360c`: the plugin-facing
`listInboxAsCaller`, scoped to the authenticated caller |
| `objectstack-ai#11453` | 26/13 | 23/3 | `1a47a5368`: `ack()` refuses a row that is
not `in_flight` (`NotificationAckError`, `DELIVERY_NOT_ELIGIBLE`), as a
compare-and-set in the SQL outbox. The same anchor stage 2 gave it |
| `objectstack-ai#11671` | 4/4 | 1/3 | `09b4f4e4e`: `os i18n extract --source-hashes`
writes the per-locale provenance companion (maintainer ruling objectstack-ai#12069
Option A, which stays cited) |
| `objectstack-ai#11741` | 6/2 | 5/1 | `b706af987`: `SendEmailInput` gains
`organizationId`, and the email channel threads it on both arms. The
same anchor stage 1 gave it |
| `objectstack-ai#11859` | 29/13 | 27/2 | `d9cf78eaa`: `ack()` takes the claimed
record back and binds its claim credential in the compare-and-set. The
same anchor stage 2 gave it |
| `objectstack-ai#12144` | 2/2 | 2/0 | `3a04b0125`: identifier ceilings are
storage-owned (`sys_metadata.name` is 255) |
| `objectstack-ai#12147` | 1/1 | 1/0 | `945e91a13`: the class-level
`check-keyed-text-bounds` gate |
| `objectstack-ai#12978` | 17/6 | 16/1 | `e4902d2b9`: declares the sourced `maxLength`
on all 15 keyed text columns of the `sys_notification_*` objects. No
commit message names the card; its diff is where every `[objectstack-ai#12978]` marker
entered the tree |
| `objectstack-ai#18424` | 14/4 | 12/2 | `879b51270`: an email or SMS channel with no
transport refuses with `transport_not_configured` instead of reporting
success |

Every cited sha matches exactly one commit (`git rev-parse
--disambiguate`, count 1 for each), and every one is an ancestor of the
base (`merge-base --is-ancestor`, exit 0 for all 13). The history was
unshallowed first (`git fetch --unshallow`, 15,062 commits), so no
anchor was read from a truncated log.

Wordings to check, each true of its commit:
- `inbox-caller.ts:86-88`: 「(objectstack-ai#6071, objectstack-ai#6551, and the share-link envelope
trim commit 8e13ca8 undid)」. `8e13ca876`'s message records the trim
(four fields kept, five dropped) and the whole-envelope fix.
- `outbox.ts:72`: 「the option-A shape commit d9cf78e's ruling
refused」. `d9cf78eaa`'s message: 「The caller never supplies an identity:
ownership is proven by round-tripping what claim() returned.」
- The fifteen `sys_notification_*` bound comments: `[commit e4902d2]
... (route A, ruling 2026-08-24)`. `e4902d2b9`'s message opens 「Every
bound names its producer in the declaration」, and `3954fb7df`'s records
the ruling's date and its A and C routes.
- `notification-keyed-text-bounds.test.ts:82-83`: the `objectstack-ai#9807` pointer
becomes 「Every other arm of the grammar commit 44738f7 documented is
narrower」, because `44738f7af` is where the email arm of the selector
grammar was written down.
- `outbox-ack-claim-ownership.integration.test.ts:40`: 「the objectstack-ai#11453 file
beside this one」 names the file itself,
`outbox-ack-precondition.integration.test.ts`.

## The 15 sites left

- **Non-test strings (3 sites, 2 lines), refusal text.** `outbox.ts:187`
(「see objectstack-ai#11453」) and `outbox.ts:210` (「(objectstack-ai#11453, objectstack-ai#11859)」) are inside
`notificationAckNotClaimedMessage` and
`notificationAckLostClaimMessage`, the messages `NotificationAckError`
carries. They are runtime strings, so they are form D, not form C. The
landed objectstack-ai#20234 stages left every string site as a token and rewrote no
refusal text, so these are left and listed, as PR objectstack-ai#20533 did. The form D
stages that did rewrite strings (objectstack-ai#20233's) cover `os migrate meta`
guidance, a different class.
- **Test titles (9 sites, 8 lines).** `describe` titles in
`email-channel.test.ts:90`, `:592`, `messaging-service.test.ts:809`,
`:1286`, `notification-keyed-text-bounds.test.ts:38` (2 numbers),
`outbox-ack-claim-ownership.integration.test.ts:109`,
`outbox-ack-precondition.integration.test.ts:110` and
`sms-channel.test.ts:90`. Tokens, left as they were.
- **Generated headers (3 sites).** Line 8 of `es-ES`, `ja-JP` and
`zh-CN` `.source-hashes.generated.ts` reads 「(objectstack-ai#11671, maintainer ruling
objectstack-ai#12069 Option A, extending objectstack-ai#8765 Option B)」. `os i18n extract` writes
that line from `packages/cli/src/utils/i18n-extract.ts:2294`, and 27
generated files across the repo carry it. A hand edit here would be
undone by the next extract, so the fix belongs at the producer in a
later stage, which regenerates every copy. The hand-written
`translations/index.ts:29` is rewritten here.

## Mechanical guard: no code token moves

The check compares leaf tokens with comments stripped, base `7a1faf1a5`
against head. It uses the TypeScript parser's leaf nodes, so template
literals are read in context, and it excludes JSDoc nodes. It ran over
all 28 touched `.ts` files.

- Real run: 52,337 base tokens, **0 files with a token change** (exit
0).
- Comment-insertion control, in `outbox.ts`: 0 files changed, as
expected (exit 0).
- Positive control, a declaration inserted into `outbox.ts`: DIFFER
(exit 1). The first attempt was a no-op: its anchor text was still
inside the replacement, so `scripts/ablation-replace.mjs` refused it
before the guard ran. It was redone with a hitting anchor.
- Positive control, one digit changed inside the kept `outbox.ts:210`
refusal string: DIFFER (exit 1).

Every mutation went through `scripts/ablation-replace.mjs`, and each
restore was proven byte-identical to the HEAD blob (`80618f8711e2`) with
`git diff HEAD` empty.

## Changeset

This change ships bytes, so a `patch` changeset for
`@objectstack/service-messaging` is included. It says only that the
provenance comments were re-anchored.

Measured on the built package (A3): `files[]` is `dist`, `README.md` and
`CHANGELOG.md`. After `pnpm --filter @objectstack/service-messaging
build`, the rewritten comments reach both halves of `dist`. `d9cf78eaa`
appears 8 times in `dist/index.d.ts`, `1a47a5368` 6 times and
`17d095413` 6 times, and `e4902d2b9` appears 15 times in
`dist/index.js`. The positive control, an unchanged
`notification-subscription.object.ts` docblock sentence, appears in
`dist/index.d.ts`, and a negative control phrase appears nowhere. The
only dead numbers left in `dist` are the two kept refusal strings.

## Gates (head `267c11562`)

- **Citation judging, as CI runs it:** `pnpm check:issue-citations`
(self-test, 114 cases in 8 batteries) exits 0, and `node
scripts/check-issue-citations.mjs` exits 0. The diff-scoped run judged 5
citations across 19 files, and all 5 resolve.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `267c11562` derived 64 families.
They include all 50 derived at dispatch, plus 14 more. All 64 exit 0.
`--ran` reports 64 run, 0 NOT MEASURED, 0 unrun, and exits 0.
- Three gates first exited 3 (PREREQUISITE NOT MET) because the
workspace was unbuilt: `check:dual-build-cjs-loads`, `check:i18n` and
`check:type-check-debt`. A full `turbo run build` of `./packages/*` and
`./packages/*/*` then ran under the shared verify lock (71 tasks, exit
0). The first two exited 0 on their rerun.
- `check:type-check-debt` exited 3 once more: `outbox.ts`'s mtime had
moved during the guard controls, although its bytes had not, so turbo's
cache hit left `dist` older than the source. A direct `pnpm --filter
@objectstack/service-messaging build` then let it exit 0 (4 ledger
entries re-measured, none above its number).
- **Tests and typecheck:**
- `pnpm --filter @objectstack/service-messaging test`: 46 files and 507
tests pass, covering every touched test file.
- `pnpm --filter @objectstack/service-messaging typecheck` exits 0. Its
`tsc` program lists all 46 test files and 87 files under `src/` in total
(`--listFiles`).
- **Lint, as a proven narrowing:** `eslint --no-inline-config --format
json` over the 28 touched `.ts` files gives 28 files, 0 errors and 0
warnings. All 28 are in eslint's own population (`isPathIgnored` is
false for each). `eslint.config.mjs` never enables type-aware linting
(no `parserOptions.project`, as its own line 328 states), so a comment
edit here cannot move the verdict on any untouched file. The repo-wide
`pnpm lint` is CI's run.
- **Control bytes:** `pnpm check:nul-bytes` exits 0, and a raw scan of
the 28 files for control bytes finds none.

## Acceptance notes

- **The census instrument returned a truncated board once, at exit 0.**
The first `--census --json` run of this stage (06:25:55Z, base
`7a1faf1a5`) read `enumerated (85 pages)`, frontier objectstack-ai#8854, 8,444
numbers, when the newest number was above objectstack-ai#20600. The `Link` header of
its 85th page had carried no `rel="next"`, so `enumerateBoard` stopped
and classified 16,187 citations as `never-issued`. A reader counting
only `allocated-but-absent`, as this stage's count does, would have got
8 service-messaging sites instead of 100, silently. The next four
enumerations in this session read 185 pages and frontier objectstack-ai#20606, and the
counts above come from those. Nothing in `enumerateBoard` compares its
frontier with the newest issue number, which `probeBoard` does read.
Reported to the seat, not changed here: this stage makes no instrument
change.
- **What stays for later stages.**
- The 3 generated `objectstack-ai#11671` headers, whose producer is
`packages/cli/src/utils/i18n-extract.ts:2294`. That line is the
repo-wide carrier (27 generated files).
- The 3 refusal-string sites in `outbox.ts` (form D) and the 9
test-title sites.
- **Base.** The branch is 9 commits behind `origin/main` (`0f6dcac5e`,
read at 07:23Z). One of them, `8c87d26a5` (the version packages
release), touches `service-messaging`, but only its `CHANGELOG.md` and
`package.json`, and neither is in this diff. So there was no merge.
- **Anchors shared with the spec stages.** `17d095413` (objectstack-ai#6363),
`1a47a5368` (objectstack-ai#11453), `d9cf78eaa` (objectstack-ai#11859) and `b706af987` (objectstack-ai#11741) are
the anchors stages 1 and 2 already gave the same numbers in
`packages/spec/src`, so each number carries one anchor across the tree.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…me/src to the commits that decided them (objectstack-ai#20624)

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

## What changed

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

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

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

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

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

Twenty-eight dead comment sites are left on purpose:
- **20 in `domains/meta.ts`.** PR objectstack-ai#20615 (objectstack-ai#20590's) opened at
2026-09-29T08:12:40Z, after this stage's claim and first read, and edits
that file. So the file went back to its base blob (`b4ddb362cc`) in
`a5cdfd8a46`, as PR objectstack-ai#20612 did with `authoring-rules.ts`. The anchors
are verified and listed below for the follow-up.
- **8 with no deciding commit, or with a literal reader.** See "The
sites left" below.

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

**Wordings to check, each true of its commit:**
- `objectstack-ai#10293` (3 sites) cited the p1 flake whose signature had the
expected-noise lines lifted into it. They now read 「(a vitest teardown
race, fixed by commit 92a69d8)」. `92a69d813` names that number in its
subject and fixed the flake by disarming vitest's console-forwarding
teardown race, which is why the noise pointed the dispatch at the wrong
mechanism.
- `objectstack-ai#16755` and `objectstack-ai#16783` each cited an open PR that held a file at the
time. They now read 「the change that landed as commit 44c849c held
that file」 and 「then held by the change that landed as commit
854639b」. Each commit's diff edits the named file
(`domains/automation.ts`, `seed-loader.test.ts`).
- Quoted rulings keep their words.
`dispatcher-plugin.declared-5xx-prose-withhold.test.ts:13` and
`dispatcher-plugin.declared-user-message.test.ts:35` quote the
2026-08-27 ruling, and
`dispatcher-5xx-demoted-code-withhold.test.ts:281` quotes an older note.
There the commit stands in an editorial bracket (`[commit 79c46da]`,
`[commit 0783d7b]`) in place of the number.
- `objectstack-ai#9934`'s 「second constraint」 and 「third constraint」 now read 「the
ruling's second constraint, commit 79c46da」. That commit's own diff
calls status-agnosticism 「the ruling's second constraint」.
- `domains/packages.ts:841` read 「declares, since objectstack-ai#19364:」 above the
`enabled` line, but that line predates `ada701220` (objectstack-ai#19364's commit). It
now reads 「declares — a key commit ada7012 kept rather than retired:」.
- `route-ledger.ts:288`: 「objectstack-ai#16758 filed the second kind」 now reads
「Commit 6e9bee6 gated the second kind」, because that commit added the
row census after the index-slice incident the sentence goes on to
describe.
- `flow-clone.ts:7` and `domains/automation.ts:2403` cite `e170b0ae5`
for objectstack-ai#11513: the commit that landed 「lock package-declared permission
sets at the save door; clone to customize」, whose changeset names the
number.

## The sites left

**No deciding commit, or a literal reader (8 sites):**
- `api-exposure.ts:108` (objectstack-ai#6259): `api-exposure.test.ts:152` splits this
`@param` block on the literal `'objectstack-ai#6259'`, so rewriting the comment would
change what the test measures. Its deciding commit is `6968885ef`, which
the three test-comment sites of the same number now cite.
- `domains/mcp.ts:360` (objectstack-ai#8722): a wider contract change 「archived
unscheduled」. It never landed, so no commit decided it.
- `domains/meta-state-plural-tolerance.test.ts:130` (objectstack-ai#10179): an untaken
option on a tracking card. The only commit naming the card, `53a48c93f`,
recorded the opposite state.
- `package-door-namespace-conflict-code.test.ts:30` (objectstack-ai#14745): a residue
item on a review card. The only commit carrying the token is the one
that added this file.
- `route-ledger.conformance.test.ts:33` (objectstack-ai#17038): an ablation measured
on a PR whose squash commit, `6a7910abb`, neither records nor performs
it.
- `route-ledger.conformance.test.ts:38` and `route-ledger.ts:300`
(objectstack-ai#17041): a maintainer decision the lines call open.
- `security/artifact-granted-permissions.test.ts:291` (objectstack-ai#17148): a
question the line itself says is unsettled.

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

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

## Mechanical guard: no code token moves

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

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

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

## Changeset

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

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

## Gates (head `a5cdfd8a46`)

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

- **The held file.** The claim's read (07:51Z) and this stage's first
read of the open PRs' file lists (08:06:32Z, 8 open PRs) found none
touching `packages/runtime/src`. PR objectstack-ai#20615 opened at 08:12:40Z and edits
`domains/meta.ts`. The re-read at 09:07:08Z (7 open PRs) found it, and
it is the only open PR touching the package. The file went back to its
base blob in `a5cdfd8a46`, and `git hash-object` equals `b4ddb362cc`,
the blob at the base and at `origin/main`. The 20 anchors above are
ready for the follow-up once that PR lands.
- **Form D, not touched here.** `domains/activation-gate.ts:279` is part
of the enablement door's refusal message and ends 「(objectstack-ai#10243).」. An author
sees it, so it is ruling D's (no number, the lesson in words), a string
change outside this comment-only scope. It needs a form-D carrier. The
other four non-test string sites are ledger `note` data and a gate's own
string.
- **The grammar does not read a slash-joined number.** `CITATION_RE`
refuses a `#` preceded by `/`, so the second number of `#A/#B` is never
judged. In `packages/runtime/src`, 3 such dead numbers exist (`objectstack-ai#10630`,
`objectstack-ai#12281`, `objectstack-ai#12194`), and all 3 are rewritten here. The other 36 distinct
slash-joined numbers there were probed by REST and answer 200. One more
dead one, `objectstack-ai#17219`, stands slash-joined inside a test title, a string,
and is kept. This is the same shape as PR objectstack-ai#20612's slash-joined
`objectstack-ai#5775/objectstack-ai#6629`. It is noted, not filed.
- **Outside the scope and the census surface.**
`packages/runtime/vitest.config.ts:54` cites `objectstack-ai#17853`, which answers
404. The file is outside `src/**`, so it is left for whoever owns the
package's config. The other numbers there, and those in `tsup.config.ts`
and `README.md`, answer 200.
- **Base.** The branch merged `origin/main` once (`ca6d13d6ab`, merging
`c1d8051e0a`) before the `--base origin/main` run, as the dispatch
orders. That merge brought PR objectstack-ai#20609's and PR objectstack-ai#20612's landed stages and
touched none of this diff's files. `origin/main` has since moved to
`ed6f7348f9`, one commit that touches only `packages/cli`, so there was
no second merge.
- **Anchors shared with the landed stages.** 24 numbers keep the anchor
the spec, lint or service-messaging stages already gave them, for
example `f19475c0a` (objectstack-ai#14143), `b003cf2e8` (objectstack-ai#13657), `311433f6b`
(objectstack-ai#12194), `8e13ca876` (objectstack-ai#6206) and `17d095413` (objectstack-ai#6363).

## Deviations

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

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

---------

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

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

## What changed

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

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

## The sites left

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

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

## Mechanical guard: no code token moves

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

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

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

## Changeset

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

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

## Gates (head `93e4d69ba6`)

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

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

## Deviations

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

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

---------

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

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

## What changed

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

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

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

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

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

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

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

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

## Held files

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

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

## The sites left

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

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

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

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

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

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

## Changeset

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

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

## Gates (head `6bb4d3b531`)

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

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

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

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

## Deviations

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

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

---------

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

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

## What changed

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

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

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

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

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

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

## Wordings to check

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

## Mechanical guard: no code token moves

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

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

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

## Changeset

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

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

## Gates (head `686a4c60cb`)

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

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

## Deviations

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

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

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…a.ts to the commits that decided them (objectstack-ai#20689)

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

## What changed

This is stage 5 of the `domain:cli` lane of the dead-citation sweep: the
20 comment sites in `packages/runtime/src/domains/meta.ts` that stage 1
(PR objectstack-ai#20624) held while PR objectstack-ai#20615 edited the file. PR objectstack-ai#20615 has merged,
and no open PR touches the file (the file lists of all open PRs were
read twice: 9 PRs at 2026-09-29T17:14:19Z and 6 at 17:43:04Z). Every one
of the 20 sites cited a tracker number that answers 404. Each 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
keeps saying in its own words what that commit decided. PR objectstack-ai#20533 is the
method and PR objectstack-ai#20624 the precedent. The card stays open for the lane's
remaining stages, so this PR says `Part of`.

That is **20 sites on 20 lines, covering 9 numbers**, rewritten to **9
distinct commits**. They are the anchors PR objectstack-ai#20624's body listed for
this file, each re-verified against the file as PR objectstack-ai#20615 left it (that
PR's one hunk is at `:1874`, disjoint from every site). No ADR or
ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records any
of these decisions, so every anchor is a commit.

One more line changed: `:1976`, the other half of the `:1974` sentence.
It read "This tail was unreachable until this card", and "this card" was
the number `:1974` cited. It now reads "until that commit".

Only comments changed: **21 lines out, 21 in**, and the file keeps its
1,991 lines, so no line citation into it moves. **No citation number is
added.** Every tracker number on an added line was already on the line
it replaces (21 line pairs, added-minus-removed empty), and no PR number
stands on an added line.

There is **no changeset**. This diff publishes no byte from
`@objectstack/runtime`, so it takes `skip-changeset` (see Changeset
below). That departs from the dispatch, which ordered a changeset on
stage 1's precedent. The measurement is below.

## Census: `domains/meta.ts`, 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. The
count is its `allocated-but-absent` findings for
`packages/runtime/src/domains/meta.ts`. Both runs enumerated the whole
board, so neither read a truncated board.

| reading | tree | board | whole-repo `allocated-but-absent` | `meta.ts`
sites | lines | numbers | `packages/runtime` |
|---|---|---|---|---|---|---|---|
| before | base `6981abfd26`, run 2026-09-29T17:10:45Z to 17:16:23Z |
enumerated, 186 pages, frontier objectstack-ai#20686, 18,513 numbers | 1,318 | **20**
| 20 | 9 | 23 |
| after | `7cdd37d1f8`, run 17:17:15Z to 17:23:01Z | enumerated, 186
pages, frontier objectstack-ai#20686, 18,513 numbers | 1,298 | **0** | 0 | 0 | 3 |

The whole-repo drop is exactly these 20 sites: a site-by-site diff of
the two JSON outputs differs only in `domains/meta.ts`. The 3 left in
`packages/runtime` are stage 1's deliberate sites
(`api-exposure.ts:108`, `domains/mcp.ts:360`, `route-ledger.ts:300`).
`meta.ts` is byte-identical at `7cdd37d1f8` and at the head (blob
`8201775c6d`).

A REST probe of every number cited in the file (44 distinct) found 35
answering 200 and 9 answering 404. The 9 are exactly the census's 9, all
in comments. So no dead number stands in a string literal in this file.

## Per-site table

Every anchor was read in its message or diff, not only its subject, and
`git blame` at the base ties each line to the commit that wrote it.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#10503` | `:14`, `:1143`, `:1159`, `:1250`, `:1308` | `67ceb9aef`:
the dispatcher `/metadata` transport folds the URL segment through
`canonicalMetaUrlType` before the org-scope decision, and at the
`/published` code-store fallback. Its diff adds four of these five tags.
|
| `objectstack-ai#10340` | `:1309` | `26f3588fb`: REST's `/meta` doors decide org
scope on the folded type, not the raw URL spelling (its message names
objectstack-ai#10340 as the card it settles). |
| `objectstack-ai#11006` | `:103` | `cccbe51bf`: `MetadataProtocol` declares
`publishMetaItem`, and its changeset states the end state "an undeclared
key in a request literal at the member's call shape is now a compile
error". The wording is the one stage 1 gave `domains/packages.ts:138`. |
| `objectstack-ai#8726` | `:116` | `e783e163d`: `domains/mcp.ts` narrows the protocol
slot to a type picked from the declared `MetadataProtocol` contract (the
docblock at `mcp.ts:321` blames to it). |
| `objectstack-ai#8848` | `:200`, `:1398` | `4fc4a3c0b`: `/metadata/:type/:name`
refuses an unsupported verb instead of serving it as a read. Both lines
blame to it. |
| `objectstack-ai#8919` | `:1247` | `b5378550e`: gates REST `/meta` publish and
rollback on `manage_metadata`, and its diff introduces "the same
single-resolution shape" (the capability gate's context reused for
scope). |
| `objectstack-ai#10888` | `:1337` | `d806081dd`: renders the spec-validation 422
findings clause per write face. The line blames to it. |
| `objectstack-ai#12195` | `:819`, `:827`, `:1046`, `:1167`, `:1974` | `7986d973f`,
stage 3 of the compound-name retirement: un-folds the dispatcher
(exactly three and two segments), decodes the `:name` segment, and
answers the newly reachable tail with a located `ROUTE_NOT_FOUND`. All
five lines blame to it. |
| `objectstack-ai#12194` | `:831`, `:1050`, `:1173` | `311433f6b`, stage 1: the
item-name grammar, refused at the publish door. |

Every cited sha matches exactly one object (`git rev-parse
--disambiguate`, count 1 for each of the 9), is a commit, has one
parent, and is an ancestor of the base (`merge-base --is-ancestor`, exit
0 for all 9). The checkout is not shallow. The control leg `f5a9bc2f3`
(2026-08-10, older than the oldest anchor `e783e163d` of 2026-08-14)
exits 0, and the negative control, this branch's own `7cdd37d1f8`, exits
1. All 9 anchors are the ones the landed stages already give these
numbers, so each number carries one anchor across the tree.

**Wordings to check, each true of its commit:**
- `:831` keeps its quotation: "breaking commit 311433f's landed
acceptance criterion that 'reads and `deleteMetaItem` still answer for
pre-grammar residue rows...'". The quoted words are the criterion's own.
That commit's changeset states the same criterion: "Reads and
`deleteMetaItem` deliberately stay open, so any pre-grammar residue row
remains listable and clearable."
- `:1250` was written by `15eb2c97f`, the org-presentation capability
commit, which moved the fold earlier so that the capability verdict
reads it too. The fold itself, which the tag names, is `67ceb9aef`'s,
and the lines around it already carry that commit's own `[objectstack-ai#12702]` tags.
- `:1050` and `:1173` keep "stage 1" beside the commit, because the
sentences contrast the retirement's stage 1 with the fold that stage 3
removed.

## Mechanical guard: no code token moves

**H2 holds on both counts.** The comment-stripped diff is empty, and the
emitted `dist` is byte-identical.

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, JSDoc nodes excluded) of the file at base
`6981abfd26` and at head `b96c13e3ab`. Controls mutate the head text in
memory only.
- Real run: 6,794 tokens on both sides, 0 differing (exit 0).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: differs at token 0 (exit 1).
- String control (`'Not found'` to `'Not founD'` in the code literal at
`:495`): 1 differing `StringLiteral` at token 1338 (exit 1). A first
string control anchored on the first occurrence of that text, which is
inside a comment at `:465`, so it was a second comment control. It read
0, and it was re-anchored on the code literal.

**Emitted `dist`.** `@objectstack/runtime` was built fresh with `pnpm
--filter @objectstack/runtime build` at the base (in a throwaway
detached worktree at `6981abfd26`) and at `7cdd37d1f8`, with the same
dependency builds. All six `dist` files (`index.js`, `index.cjs`, their
maps, `index.d.ts`, `index.d.cts`) have equal sha256.
- Positive control: in the base tree, a one-character change to the code
string `'Save not supported'` in this file (through
`scripts/ablation-replace.mjs`, which verified that the anchor hit and
that the blob restored to HEAD) changes the sha256 of `index.js` and
`index.cjs`. So the build reads this file, and its comments simply never
reach `dist`.
- None of the file's comment text appears in `dist`, changed or
unchanged: 0 hits for "is the FOLD this transport was missing",
"Percent-decode the", "A LOCATED refusal, not a bare" and "A throw here
is a FAULT". Its code does appear (`function decodeMetaNameSegment`,
`METADATA_ITEM_METHODS`).

The line count is 1,991 at base and head. A raw scan of the file for
control bytes finds none (a positive probe on a scratch file matched).

## Changeset

**None; `skip-changeset` instead.** `@objectstack/runtime`'s `files[]`
is `dist`, `README.md` and `CHANGELOG.md`, and the build above emits
byte-identical `dist` at base and head. So this diff publishes nothing
from a released package, which is what the label is for (AGENTS.md's
Post-Task Checklist, step 3).

The dispatch ordered a `patch` changeset matching
`.changeset/runtime-provenance-anchors.md`. Stage 1 shipped that
changeset because its rewritten docblocks reached `dist`. For this file
they do not. The dropped changeset stands in `7cdd37d1f8`: if the seat
rules for it, `git checkout 7cdd37d --
.changeset/runtime-meta-provenance-anchors.md` restores it. Stage 1's
changeset, still pending, already describes re-anchored provenance
comments under `src/` in general terms.

## Gates (head `b96c13e3ab`)

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

```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 140s (2m20s) · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 8s · declare it in the PR body · pnpm --filter @objectstack/runtime 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 13s · declare it in the PR body · pnpm --filter @objectstack/runtime typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 113s (1m53s) · declare it in the PR body · pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2
```

- **Build:** the whole workspace, `turbo run build
--filter='./packages/*' --filter='./packages/*/*'`, 71 of 71 tasks after
the merge.
- **Tests:** `vitest run --project local`: 289 files, 4,197 tests
passed, 1 skipped. `--project repo`: 3 files, 727 tests passed.
- **Typecheck:** `pnpm --filter @objectstack/runtime typecheck` exits 0.
`tsc --listFiles` puts `domains/meta.ts` among `tsconfig.json`'s 82
`src` files. `check:test-typecheck`: 27 files, 190 errors, 68 pinned
signatures held.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 (2026-09-29T17:36:49Z to 17:37:35Z).
- **Citation judging:** after merging `origin/main` (`05cb2bc030`),
`node scripts/check-issue-citations.mjs --base 05cb2bc` read 1 file
and reports "no issue citations added" (exit 0). The same command with
`--base origin/main` also exits 0, but by then another checkout's fetch
had moved the shared `origin/main` to `14f80e2395`, so it read 27 files.
See Acceptance notes.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 48 families. All 48 exit
0, and `--ran` reads "48 derived, 48 run, 0 NOT-MEASURED, 0 UNRUN".
Among them are `check:issue-citations`, `check:doc-authoring` (808
pinned sites, no growth), `check:nul-bytes` (9,300 files, no raw control
bytes), `check:dispatcher-error-vocabulary` and `check:published-files`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0,
including the four the derivation marks as keeping their roster under
one of this diff's paths (`check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`,
`check:route-ledger-census`). The other three need a pull request's
context, and exit 2 without one. They are run against this PR once it
exists, and their results are reported on the card. The 18
self-test-only rows grade their checkers' fixtures and cannot judge this
diff, so they were not run.

## Hypotheses (measured first)

- **H0 holds.** At base `6981abfd26` the filtered census answers 20
sites on 20 lines, 9 numbers. The line numbers equal PR objectstack-ai#20624's list,
apart from `:1960`, which is now `:1974` because PR objectstack-ai#20615 added 14
lines above it.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`domains/meta.ts`. No site was left for an open PR or an unfound anchor.
- **H2 holds, both ways:** the token guard is empty with its controls
firing, and the emitted `dist` is byte-identical with a positive
control. See the guard above.

## Acceptance notes

- **The moving `origin/main`.** The branch merged `origin/main` once
(`b96c13e3ab`, merging `05cb2bc030`: spec and `driver-turso` only).
Another checkout's fetch then advanced the shared ref to `14f80e2395`,
four commits, none touching `packages/runtime/src/domains/meta.ts`. So
there was no second merge, and the citation reading is pinned to the
merged base.
- **A load timeout, not this diff.** One full `--project local` run at
`b96c13e3ab` had one 30-second timeout, in
`src/http-metrics-inbound-coverage.hono.integration.test.ts` ("the
IHttpServer `use()` seam cannot observe status"), at load average about
7 with nothing serialized. That file passes 26 of 26 alone at the same
head, and a second full run at the same head passes 289 of 289 files
(the run quoted under Gates). The diff moves no code token and no `dist`
byte.
- **Still on the card, not this stage:**
`packages/runtime/vitest.config.ts:54` (objectstack-ai#17853), the form-D stage for
strings, and the lane's smaller packages.

## Deviations

- **No changeset, against the dispatch's order.** See Changeset above.
- **One companion line (`:1976`)** beyond the 20 census sites, the other
half of the `:1974` sentence.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…/src to the commits that decided them (objectstack-ai#20703)

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

## What changed

This is stage 6 of the `domain:cli` lane of the dead-citation sweep:
`packages/client/src`. Every comment site there 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 keeps saying in its own words what
that commit decided. PR objectstack-ai#20533 is the method and stages 1 to 5 of this
card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689) are the
precedents. The card stays open for the lane's remaining packages, so
this PR says `Part of`.

That is **43 sites on 43 lines in 7 files, covering 13 numbers**,
rewritten to **12 distinct commits**:
- the census's **19 sites**, all in `src/index.ts` (8 numbers);
- **24 test-file comment sites** in 6 test files (the census defers
`*.test.ts`; stages 1 to 5 took test comments too).

One more line changed: `client.test.ts:2198`, the second half of the
`:2197` sentence ("byte-identical to the pre-(number) behavior" now
reads "byte-identical to the behavior before commit cf74a11").

Only comments changed: **44 lines out, 44 in**, and every touched file
keeps its line count, so no line citation into these files moves. **No
citation number is added**: over the 44 line pairs, added-minus-removed
numbers is empty, and no PR number stands on an added line. No ADR or
ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records any
of these 13 decisions, so every anchor is a commit.

A **`patch` changeset** for `@objectstack/client` rides along, because
the rewritten docblocks reach `dist` (measured below).

## Census: `packages/client`, 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. The
count is its `allocated-but-absent` findings under `packages/client/`.
Both runs enumerated the whole board.

| reading | tree | board | whole-repo `allocated-but-absent` |
`packages/client` sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `3b47a693c7`, run 2026-09-29T18:31:09Z to 18:35:42Z |
enumerated, 186 pages, frontier objectstack-ai#20701, 18,528 numbers | 1,298 | **19**
| 19 | 8 | 1 (`src/index.ts`) |
| after | `221a3f5e63`, run 18:39:47Z to 18:44:31Z | enumerated, 186
pages, frontier objectstack-ai#20702, 18,529 numbers | 1,279 | **0** | 0 | 0 | 0 |

The whole-repo drop of 19 is exactly these sites: a site-by-site diff of
the two JSON outputs has 19 findings gone, all in
`packages/client/src/index.ts`, and none added. `packages/client/src` is
byte-identical at `221a3f5e63` and at the head.

**Supplementary scan (test files included).** The gate's exported
`extractCitations` and `classifyCitation` over all 53 `.ts` files under
`src/`, with the board from the gate's own `probeBoard`: 963 citations
and 61 dead before (src comments 19, test comments 24, src strings 0,
test strings 18), 920 and 18 after (0, 0, 0, 18). Its before list of src
comment sites is identical to the census's. The 18 left are test titles,
the form-D stage (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it, and
each anchor was read in its message, changeset or diff, not only its
subject.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#12195` | `index.ts:684`, `:728`, `:1834`, `:2021`;
`client.test.ts:2869`; `meta-automation-descriptors.test.ts:29` |
`7986d973f`, stage 3 of the compound-name retirement: un-mounts the
three `:section` arities and unifies the SDK's URL spelling on
`encodeURIComponent`. All six lines blame to it. |
| `objectstack-ai#12176` | `client.test.ts:357`;
`meta-automation-descriptors.test.ts:31` | `7986d973f` as well: the
lines say the card "retired compound-name addressing", and that commit
completes the retirement. Both lines blame to it, and the landed stages
give this number the same anchor. |
| `objectstack-ai#12194` | `index.ts:738`, `:1838`, `:2026`; `client.test.ts:360`;
`meta-automation-descriptors.test.ts:43` | `311433f6b`, stage 1: the
item-name grammar, refused at the publish door. |
| `objectstack-ai#12181` | `index.ts:799`, `:820`, `:911`, `:1866`;
`meta-delete-item-carriers.test.ts:4`, `:265` | `cf71d73f8`:
`meta.deleteItem` sends the reset door's `If-Match` pin and
`?state=draft` on both declarations. Its changeset also records the
withholding of `?dropStorage` that `:820` and the test's `:265`
describe. All six lines blame to it; stage 3 gave the number the same
anchor. |
| `objectstack-ai#14879` | `index.ts:3479`, `:3552`, `:3635`, `:7536`;
`client.data-prefix.test.ts:4`; `client.metadata-prefix.test.ts:7`;
`client.test.ts:2165`, `:2197` | `cf74a1128`: the SDK reads the CRUD
data prefix from discovery instead of restating `/data`
(`_dataPrefix()`). Six lines blame to it; `index.ts:3552` and
`client.metadata-prefix.test.ts:7` blame to `032452a54`, the
metadata-prefix sibling, and name the data-prefix change as their
precedent. |
| `objectstack-ai#14313` | `index.ts:4911`; `return-type-precision.test.ts:1031` |
`b1b978c8d`: binds the `auth.*` family to the wire shapes better-auth
sends, and deliberately leaves `auth.deleteUser` unbound (the member
`:4911` describes). Both lines blame to it. |
| `objectstack-ai#14312` | `return-type-precision.test.ts:891`, `:973`, `:981`,
`:1130` | `e944fdb24`: binds four `oauth.*` methods and deliberately
leaves `oauth.applications.delete` unbound, the "open decision" `:973`
and `:981` name. `:891` blames to it; `:973` and `:981` blame to the
later delete binding (`7beaaa32c`) and `:1130` to `b1b978c8d`, and each
refers back to what the `oauth.*` card did. |
| `objectstack-ai#14314` | `return-type-precision.test.ts:1138` | `7092d63e4`: binds
the `organizations.*` family. The line blames to it. |
| `objectstack-ai#6361` | `index.ts:6379`; `client.test.ts:878`, `:1390` |
`90bbf2510`: retires the notification-list `cursor` on both halves.
`:1390` blames to `0b4022b41`, which applies the same retirement one
door over and names this one as its precedent. Stages 1 and 2 and the
spec lane gave the number this anchor. |
| `objectstack-ai#9934` | `index.ts:7406`; `client.test.ts:27` | `79c46da90`: the
producer-side `userMessage` marking. Both lines blame to it; the anchor
the landed stages give this number. |
| `objectstack-ai#6239` | `index.ts:8122` | `f549a0d4a`: the ADR-0049 retirement sweep
that deleted `ViewProtocol` and its schemas. The line blames to it; the
spec lane's stage 2 gave the number this anchor. |
| `objectstack-ai#8480` | `client.test.ts:512` | `caaae2cca`: the typed
`security.explain()` request gains the `recordIds` batch spelling. The
line blames to it, and its changeset names the card. |
| `objectstack-ai#13208` | `return-type-precision.test.ts:1394`, `:1415` |
`74049254d`: `DeleteMetaItemResponseSchema` declares `seq` and
`projectionApplied`. `objectstack-ai#13208` was the pull request that landed as this
commit (its subject carries the number), and it answers 404 too.
`objectstack-ai#13155`, the issue beside it, answers 200 and stays. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 12), is a commit, has
one parent, and is an ancestor of `main` (`merge-base --is-ancestor`
against `31ed067639`, exit 0 for all 12). The checkout is not shallow.
The control leg `6f657f4f5` (2026-08-07, the parent of the oldest anchor
`f549a0d4a` of 2026-08-08) exits 0, and the negative control, this
branch's own `221a3f5e63`, exits 1. Six anchors reuse the landed stages'
(`7986d973f`, `311433f6b`, `cf71d73f8`, `90bbf2510`, `79c46da90`,
`f549a0d4a`), so each number carries one anchor across the tree; six are
new (`cf74a1128`, `b1b978c8d`, `e944fdb24`, `7092d63e4`, `caaae2cca`,
`74049254d`).

**Numbers.** All 13 dropped numbers answer 404 by REST (re-probed
2026-09-29T18:59Z). The three numbers kept on changed lines (`objectstack-ai#8326`,
`objectstack-ai#12104`, `objectstack-ai#13155`) answer 200. Five slash-joined numbers stand in
`packages/client/src`, which the citation grammar does not read
(`objectstack-ai#11925/objectstack-ai#12036`, `objectstack-ai#3431/objectstack-ai#3455`, `objectstack-ai#2567/objectstack-ai#3963`, `objectstack-ai#5449/objectstack-ai#5546`,
`objectstack-ai#5674/objectstack-ai#5787`); their second halves all answer 200, so none is dead.

**Wordings to check, each true of its commit:**
- `index.ts:820` "Maintainer-seat ruling, landed by commit cf71d73":
that commit's changeset states the withholding in the same terms ("no
caller was measured needing it from this client").
- `index.ts:3552` now reads "The defect commit cf74a11 fixed, one key
over"; `client.metadata-prefix.test.ts:7` reads "the `crud.dataPrefix`
defect commit cf74a11 fixed".
- `index.ts:738` keeps "Stage 1" beside the commit, and its "this stage"
is the docblock's own commit, tagged `7986d973f` at `:728`.
- `return-type-precision.test.ts:891`, `:1031`, `:1138` keep "card N of
3 of objectstack-ai#12104" (that number answers 200).

## Mechanical guard: no code token moves

**H2 holds on the comment-stripped reading; the emitted `dist` is NOT
byte-identical, because the docblocks ship.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, JSDoc nodes excluded) of the 7 touched files at base
`3b47a693c7` and at `221a3f5e63`. Controls mutate the head text in
memory only.
- Real run: 68,102 base tokens, 0 differing (exit 0).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: all 7 files differ at token 0 (exit 1).
- String control (`'token'` to `'tokeN'` in the code literal at
`index.ts:4895`): exactly 1 differing `StringLiteral`, at token 14,882
of `index.ts` (exit 1).

**Emitted `dist`.** `pnpm --filter @objectstack/client build` at base
(the base tree of `packages/client/src` restored in place with a
trap-armed restore; blob-equal to HEAD and `git diff HEAD` empty
afterwards) and at the head, with the same dependency builds:
- `index.d.ts`, `index.d.mts`, `index.js` and `index.mjs` differ;
`index.js.map` and `index.mjs.map` are equal.
- The same parser comparison over the four differing `dist` files reads
0 differing tokens (12,637 / 12,637 / 25,487 / 25,184), so the whole
`dist` delta is comment text. Its code control (a code line appended to
`index.mjs`) reads COUNT DIFFERS. A first try at that control appended
the line onto the file's last line, which is the `sourceMappingURL`
comment with no trailing newline, so it landed inside a comment, read 0,
and was void; it was redone after a newline.
- The new wording is in `dist`: for example "commit cf74a11" appears 4
times in `index.d.ts` and `index.js`.
- Code-mutation control (`scripts/ablation-replace.mjs`, anchor
`searchParams.set('token'` hit 1 to 0, blob restored to HEAD
`19305adddb`, `git diff HEAD` empty): changes `index.js` and
`index.mjs`. `dist` was rebuilt to the head bytes and
`scripts/ablation-dist-preflight.mjs` reads the marker absent from all 6
files with a clean tree.

A raw scan of the 8 changed files for control bytes finds none (a
positive probe on a scratch file matched).

## Changeset

**`patch` for `@objectstack/client`**
(`.changeset/client-provenance-anchors.md`), in PR objectstack-ai#20632's form.
`@objectstack/client`'s `files[]` is `dist`, `README.md` and
`CHANGELOG.md`, and the build above emits different `index.d.ts` /
`index.d.mts` / `index.js` / `index.mjs` at base and head, so this diff
publishes. `check-changeset-no-major`, `check-empty-changeset`,
`check-adr-0087-registration` and `check-changeset-fixed` all exit 0.

## Gates (head `afa654081f`)

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

```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 59s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/client...' 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 121s (2m01s) · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 21s · declare it in the PR body · pnpm --filter @objectstack/client exec vitest run --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 8s · declare it in the PR body · pnpm --filter @objectstack/client typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter @objectstack/client build
```

- **Build:** `@objectstack/client`'s closure (35 of 81 workspace
projects), then the whole workspace, `turbo run build
--filter='./packages/*' --filter='./packages/*/*'`, 71 of 71 tasks,
after the merge.
- **Tests:** `vitest run`: 50 files, 641 tests passed (every `*.test.ts`
under `src/`; `tests/integration/**` needs a running server and is
excluded by the package's own config).
- **Typecheck:** `pnpm --filter @objectstack/client typecheck` exits 0.
`tsc --listFiles`: `tsconfig.json` compiles the 3 non-test `src` files,
`tsconfig.test.json` all 53 including the 50 test files.
`check:test-typecheck`: 0 files, 0 errors, 0 pinned signatures.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 (2026-09-29T18:57:45Z to 18:58:13Z).
- **Citation judging:** after merging `origin/main` (`31ed067639`),
`node scripts/check-issue-citations.mjs --base origin/main` reports "no
issue citations added against 31ed067 (1 file(s) read)" (exit 0);
pinned `--base 31ed067` reads the same.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 61 families. All 61 exit
0, and `--ran` with the exit-coded record reads "61 derived, 61 run, 0
NOT-MEASURED, 0 UNRUN" (a derived zero). Among them:
`check:issue-citations`, `check:doc-authoring` (808 pinned sites, no
growth), `check:nul-bytes` (9,315 files, no raw control bytes),
`check:published-files`, `check:type-check-debt`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0,
including the four the derivation marks as keeping their roster under
one of this diff's paths (`check-changeset-fixed`,
`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three need a pull request's
context; they are run against this PR once it exists and reported on the
card. The 18 self-test-only rows grade their checkers' fixtures and
cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `3b47a693c7` the filtered census answers 19
sites on 19 lines, 8 numbers, all in `src/index.ts`. The whole-repo
count is 1,298, as on `0be898499f`.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/client`. No site was left for an open PR (the file lists of
all open PRs were read at 18:36:50Z, 11 PRs, and at 19:01:46Z, 8 PRs:
only the Version Packages PR objectstack-ai#20639 touches `packages/client`, in
`CHANGELOG.md` and `package.json`) or for an unfound anchor.
- **H2 holds on the token reading, not on the `dist` reading.** The
parser leaf-token diff is empty with its controls firing. The emitted
`dist` differs, and the difference is comment text only (token-identical
`dist` with a code control). That is why the changeset ships.

## Acceptance notes

- **Test titles, the form-D stage.** 18 dead numbers remain in test
string literals in `packages/client/src` (`describe` and `it` titles, no
assertion text): `objectstack-ai#12195` 6, `objectstack-ai#12181` 5, `objectstack-ai#14879` 4, `objectstack-ai#9934` 1, `objectstack-ai#8480`
1, `objectstack-ai#6361` 1. They stay on the card for its form-D stage; no string
moved here.
- **Outside `src/**`, a later stage of the card:**
`packages/client/tsconfig.json:8` and
`packages/client/vitest.config.ts:33` cite `objectstack-ai#12181` (404), and
`packages/client/test-typecheck-debt.json:3` cites `objectstack-ai#6083` (404). The
other citations in `packages/client` outside `src/**` (`CHANGELOG.md`
excluded) answer 200.
- **The moving `origin/main`.** The branch merged `origin/main` once
(`afa654081f`, merging `31ed067639`: `lint`, `metadata-protocol` and
`service-datasource`). A later fetch advanced the shared ref to
`a8acee28dd`, two commits in `packages/spec` and a generated reference
page, none touching `packages/client`. There was no second merge; CI
judges the merge ref.

## Deviations

- **One companion line (`client.test.ts:2198`)** beyond the 43 sites,
the second half of the `:2197` sentence.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…o the commits that decided them (objectstack-ai#20713)

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

## What changed

This is stage 7 of the `domain:cli` lane of the dead-citation sweep:
`packages/mcp/src`. Every comment site there 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 keeps saying in its own words what that commit
decided. PR objectstack-ai#20533 is the method, and stages 1 to 6 of this card (PR
objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#20703) are the
precedents. The card stays open for the lane's remaining packages, so
this PR says `Part of`.

That is **17 sites on 17 lines in 9 files, covering 9 numbers**,
rewritten to **9 distinct commits**:
- the census's **10 sites**, in `mcp-server-runtime.ts` (5), `plugin.ts`
(3) and `stdio-data-bridge.ts` (2), 7 numbers;
- **7 test-file comment sites** in 6 test files (the census defers
`*.test.ts`; stages 1 to 6 took test comments too).

One more line changed: `__tests__/plugin-execution-context.test.ts:7`,
the second half of the `:6` sentence ("this face was not in that card's
inventory" now reads "not in that commit's inventory", since the card it
pointed back to is now named as a commit).

Only comments changed: **18 lines out, 18 in**, and every touched file
keeps its line count, so no line citation into these files moves. **No
citation number is added**: over the 18 line pairs, added-minus-removed
numbers is empty, and no PR number stands newly on any line. No ADR or
ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records any
of these 9 decisions (a grep for the 9 numbers there reads 0 hits, with
a control number from the same tree reading 2), so every anchor is a
commit.

**No changeset, and `skip-changeset`:** none of the rewritten comments
reaches `dist` (measured below: base and head emit six byte-identical
files, and a code-mutation control changes four of them). That is stage
5's case (PR objectstack-ai#20689), not stage 6's.

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

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/mcp/`. Both runs enumerated the whole board.

| reading | tree | board | whole-repo `allocated-but-absent` |
`packages/mcp` sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `e4e5222b7b`, run 2026-09-29T19:45:33Z to 19:50:37Z |
enumerated, 186 pages, frontier objectstack-ai#20708, 18,535 numbers | 1,222 | **10**
| 10 | 7 | 3 |
| after | `459ff81088`, run 19:58:39Z to 20:02:47Z | enumerated, 186
pages, frontier objectstack-ai#20709, 18,536 numbers | 1,212 | **0** | 0 | 0 | 0 |

The whole-repo drop of 10 is exactly these sites: a site-by-site diff of
the two JSON outputs has 10 findings gone, all under `packages/mcp/src`,
and none added. The other three tallies (`resolves` 32,968,
`resolves-as-pull-request` 1,984, `cross-repo-unjudged` 994) are equal
in both runs. `packages/mcp/src` is byte-identical at `459ff81088` and
at the head.

**Supplementary scan (test files included).** The gate's exported
`extractCitations` and `classifyCitation` over all 43 `.ts` files under
`src/`, with the board from the gate's own `probeBoard`: 365 citations
and 21 dead before (src comments 10, test comments 7, src strings 0,
test strings 4), 348 and 4 after (0, 0, 0, 4). Its before list of src
comment sites is identical to the census's. The 4 left are test titles,
the form-D stage (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it, and
each anchor was read in its message, changeset or diff, not only its
subject. Where the pull request that landed an anchor still answers, its
body's first line names the dead number, which is noted.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#13318` | `mcp-server-runtime.ts:272` | `3ec8646f1`: the bridged
tools' `readOnlyHint` / `destructiveHint` come from what the definition
declares, and a tool that declares nothing is served neither hint
(omit-when-unsourced). The line blames to `c39369d12`, the
`openWorldHint` sibling, whose changeset calls this the repair "that
preceded it". The PR that landed `3ec8646f1` answers 404 too. |
| `objectstack-ai#6724` | `mcp-server-runtime.ts:625`;
`mcp-server-runtime.metadata-outage.test.ts:289` | `4f3d2322e`: corrects
`diagnoseEmptyRead`'s falsified claim that `MetadataFacade.getObject`
differs from `get('object', n)`, in the TSDoc and in the outage test's
restatement of it. Both lines blame to it; PR objectstack-ai#6948, which landed it,
names objectstack-ai#6724. |
| `objectstack-ai#6745` | `mcp-server-runtime.ts:636` | `7a5ef0008`: adds
`metadata-service-getobject-equivalence.test.ts`, pinning `getObject(n)`
equal to `get('object', n)` across all three implementations. The line's
"PR objectstack-ai#6839 for objectstack-ai#6745" named this commit's PR (answers 200), which stays
beside the sha as a convenience link. The spec lane gave the number this
anchor. |
| `objectstack-ai#6723` | `mcp-server-runtime.ts:637`, `:652`;
`mcp-server-runtime.metadata-outage.test.ts:293` | `8ad609c69`: declares
on `IMetadataService.getObject` that it answers the same as
`get('object', name)`. `objectstack-ai#6723` was the pull request that landed as this
commit (its subject carries the number); `objectstack-ai#6505`, the issue beside it on
`:637`, answers 200 and stays. The spec lane gave the number this
anchor. |
| `objectstack-ai#17114` | `plugin.ts:8`, `:67`;
`stdio-tenancy-posture-api-key-matrix.test.ts:569` | `4af758d47`: the
last two admission doors, this one included, classify the tenancy
rejection through the shared `classifyAdmissionTenancyPosture`. All
three lines blame to it; PR objectstack-ai#17683 names objectstack-ai#17114, and stage 1 gave the
number this anchor. |
| `objectstack-ai#6216` | `plugin.ts:126`;
`__tests__/plugin-execution-context.test.ts:6` | `f586f1a89`: one
`ExecutionContext` assembler for the dispatcher, REST and share-link
sites. Both lines blame to `502dc6fe7`, which converged this stdio face
afterwards and names that convergence as its precedent. Its file list
touches no `packages/mcp` file, which is what `:7` ("not in that
commit's inventory") says. Stages 1 and 2 and the spec lane gave the
number this anchor. |
| `objectstack-ai#8422` | `stdio-data-bridge.ts:85`, `:394`;
`stdio-data-bridge.not-found.test.ts:4` | `4810dd628`: the stdio
bridge's by-id write seams throw the shared `recordNotFoundError`
envelope instead of a bare `Error`. All three lines blame to it; PR
objectstack-ai#8507 names objectstack-ai#8422. |
| `objectstack-ai#17568` | `mcp-record-id-key-mistake-refusal.test.ts:4` |
`9c9e6d08f`: pins that a missing-`recordId` refusal also names the `id`
the caller sent (test-only). The line blames to it; PR objectstack-ai#17650 names
objectstack-ai#17568. |
| `objectstack-ai#13486` | `mcp-tool-bridge-safety-annotations.test.ts:423` |
`6193e576d`: pins the bridge's two hand-copied safety name sets in the
direction the old pin could not see (the docblock's heading is that
commit's subject). The line blames to it; PR objectstack-ai#13888 names objectstack-ai#13486. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 9), is a commit, has
one parent, and is an ancestor of `main` (`merge-base --is-ancestor`
against `5757463712`, exit 0 for all 9). The checkout is not shallow.
The control leg `979ad9575` (2026-08-08, the parent of the oldest anchor
`8ad609c69` of 2026-08-08) exits 0, and the negative control, this
branch's own `459ff81088`, exits 1. Four anchors reuse the landed
stages' (`f586f1a89`, `4af758d47`, `7a5ef0008`, `8ad609c69`), so each
number carries one anchor across the tree; five are new (`3ec8646f1`,
`4f3d2322e`, `4810dd628`, `9c9e6d08f`, `6193e576d`).

**Numbers.** All 9 dropped numbers answer 404 by REST (re-probed
2026-09-29T19:54Z). The numbers kept on changed lines (`objectstack-ai#6839`, a pull
request; `objectstack-ai#6505`, `objectstack-ai#15348`, `objectstack-ai#16013`, `objectstack-ai#4435`, `objectstack-ai#5138`, `objectstack-ai#7867`) answer
200. Four slash-joined groups stand in `packages/mcp/src`, whose later
halves the citation grammar does not read (`objectstack-ai#4435/objectstack-ai#5138/objectstack-ai#7867` twice,
`objectstack-ai#5138/objectstack-ai#5581`, `objectstack-ai#7728/objectstack-ai#7823`); every half answers 200, so none is dead.

## Mechanical guard: no code token moves

**H2 holds on both readings: the parser leaf-token diff is empty, and
the emitted `dist` is byte-identical.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, JSDoc nodes excluded) of the 9 touched files at base
`e4e5222b7b` and at `459ff81088`. Controls mutate the head text in
memory only.
- Real run: 21,192 base tokens, 0 differing (exit 0).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: all 9 files differ at token 0 (exit 1).
- String control (the first character of the `'vitest'` import specifier
in `plugin-execution-context.test.ts` flipped): exactly 1 differing
`StringLiteral`, at token 15 of that file (exit 1).

**Emitted `dist`.** `pnpm --filter @objectstack/mcp build` at the head,
then at base (the base tree of `packages/mcp/src` restored in place
under a trap-armed restore; an on-disk probe read `objectstack-ai#13318` 1 and `commit
3ec8646` 0 before that build; afterwards every touched blob equals its
HEAD blob and `git diff HEAD` is empty), with the same dependency
builds:
- all six files (`index.cjs`, `index.cjs.map`, `index.d.cts`,
`index.d.ts`, `index.js`, `index.js.map`) are **byte-identical** by
sha256. The built files do carry docblocks (14 in `index.js`, 78 in
`index.d.ts`); none of the rewritten ones is on an emitted declaration.
- Code-mutation control (`scripts/ablation-replace.mjs`, anchor: the
sync leg's typed `ctx.getService` call on `'tenancy'` in `plugin.ts`,
hit 1 to 0, its argument renamed to a marker; blob restored to HEAD
`0a1aaa7955`, `git diff HEAD` empty):
`scripts/ablation-dist-preflight.mjs` found the marker in `index.cjs`
and `index.js`, and `index.cjs`, `index.js` and both `.map` files differ
from the head build. `dist` was then rebuilt, its six sha256 values
equal the first head build, and the preflight in `--absent` mode reads
the marker absent from all 6 files with a clean tree.

A raw scan of the 9 changed files for control bytes finds none (a
positive probe on a scratch file matched).

## Changeset

**None, and `skip-changeset`.** `@objectstack/mcp`'s `files[]` is
`dist`, `README.md` and `CHANGELOG.md`, and the build above emits
byte-identical `dist` at base and head, so this diff publishes nothing
from any released package. Stage 5 (PR objectstack-ai#20689) measured the same and
shipped the same; stage 6 (PR objectstack-ai#20703) measured the opposite and carried
a `patch`.

## Gates (head `7a0f15de62`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each run at this head and
from the four `dist` builds (at `459ff81088`, `packages/mcp/src`
byte-identical to 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 25s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/mcp...' 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 116s (1m56s) · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 10s · declare it in the PR body · pnpm --filter @objectstack/mcp exec vitest run --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 4s · declare it in the PR body · pnpm --filter @objectstack/mcp typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 3s · declare it in the PR body · pnpm --filter @objectstack/mcp 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 3s · declare it in the PR body · pnpm --filter @objectstack/mcp 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 3s · declare it in the PR body · pnpm --filter @objectstack/mcp 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 2s · declare it in the PR body · pnpm --filter @objectstack/mcp build
```

- **Build:** `@objectstack/mcp` with its closure (9 of 81 workspace
projects), then the whole workspace, `turbo run build
--filter='./packages/*' --filter='./packages/*/*'`, 71 of 71 tasks,
after the merge. The tree was clean after both.
- **Tests:** `vitest run`: 32 files, 344 tests passed (every `*.test.ts`
under `src/`), at the head and before the merge.
- **Typecheck:** `pnpm --filter @objectstack/mcp typecheck` exits 0.
`tsc --listFiles`: `tsconfig.json` compiles the 11 non-test `src` files,
`tsconfig.test.json` all 43 including the 32 test files.
`check:test-typecheck`: 6 files, 53 errors, 8 pinned signatures, held.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-29T20:18:54Z to 20:19:23Z), and at
`459ff81088` before the merge.
- **Citation judging:** after merging `origin/main` (`9b384f63ae`),
`node scripts/check-issue-citations.mjs --base 9b384f6` judges 5
citations on the changed lines of 3 files (the kept numbers `objectstack-ai#15348`,
`objectstack-ai#16013`, `objectstack-ai#4435`, `objectstack-ai#6505`, and `objectstack-ai#6839` as a pull request) and exits 0:
every one resolves. Against `origin/main` after it moved to
`5757463712`, the same 5 citations, exit 0.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 53 families. All 53 exit
0, and `--ran` with the exit-coded record reads "53 derived, 53 run, 0
NOT-MEASURED, 0 UNRUN" (a derived zero). Among them:
`check:issue-citations`, `check:doc-authoring` (808 pinned sites, no
growth), `check:nul-bytes` (9,331 files, no raw control bytes),
`check:published-files`, `check:type-check-debt`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0,
including the three the derivation marks as keeping their roster under
one of this diff's paths (`check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`). The other three
need a pull request's context; they are run against this PR once it
exists and reported on the card. The 18 self-test-only rows grade their
checkers' fixtures and cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `e4e5222b7b` the filtered census answers 10
sites on 10 lines, 7 numbers, in 3 files, as on the seat's `0be898499f`.
The whole-repo count is 1,222.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/mcp`. No site was left for an open PR (the file lists of all 8
open PRs were read at 20:08:05Z: only the Version Packages PR objectstack-ai#20639
touches `packages/mcp`, in `CHANGELOG.md` and `package.json`) or for an
unfound anchor.
- **H2 holds, on both readings.** The comment-stripped (parser-token)
diff of all 9 touched files is empty with its controls firing, and the
emitted `dist` is byte-identical at base and head with a code control
that changes it.

## Acceptance notes

- **Test titles, the form-D stage.** 4 dead numbers remain in test
string literals in `packages/mcp/src` (`describe` titles, no assertion
text): `objectstack-ai#17568` twice in `mcp-record-id-key-mistake-refusal.test.ts`
(`:151`, `:315`), `objectstack-ai#8422` in `stdio-data-bridge.not-found.test.ts:99`,
`objectstack-ai#17114` in `stdio-tenancy-posture-api-key-matrix.test.ts:592`. They
stay on the card for its form-D stage; no string moved here.
- **Outside `src/**`, a later stage of the card:**
`packages/mcp/vitest.config.ts:18` cites `objectstack-ai#8651` (404).
`packages/mcp/test-typecheck-debt.json:2` cites `objectstack-ai#13470` (404) inside
its `_comment` field, which the file itself says is generated by
`scripts/check-test-typecheck.mts`, so a fix there is at that producer,
in the `scripts/**` lane, not a hand edit. The other citations in
`packages/mcp` outside `src/**` (`CHANGELOG.md` excluded) answer 200.
- **Card-word residue, cited nowhere.** A few docblocks still say "this
card" or "the card" a paragraph away from the rewritten line (for
example `stdio-data-bridge.not-found.test.ts:19`,
`mcp-record-id-key-mistake-refusal.test.ts:19`,
`stdio-tenancy-posture-api-key-matrix.test.ts:580`, `:584`). They cite
no number, so they were left, as the landed stages left theirs; only the
one same-sentence companion (`plugin-execution-context.test.ts:7`) was
changed.
- **The moving `origin/main`.** The branch merged `origin/main` once
(`7a0f15de62`, merging `9b384f63ae`: `service-storage`,
`platform-objects` and `plugin-audit`, nothing in `packages/mcp`). A
later fetch advanced the shared ref to `5757463712`, one commit in
`platform-objects` translations. There was no second merge; CI judges
the merge ref.

## Deviations

- **One companion line (`plugin-execution-context.test.ts:7`)** beyond
the 17 sites, the second half of the `:6` sentence.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…commits that decided them (objectstack-ai#20723)

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

## What changed

This is stage 8 of the `domain:cli` lane of the dead-citation sweep:
`packages/qa`. `packages/qa` is a directory of five private workspace
packages, not one package, so the surface is `packages/qa/*/src/**`.
Every comment site there 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. Each
line still says in its own words what that commit decided. PR objectstack-ai#20533 is
the method, and stages 1 to 7 of this card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR
objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#20703, PR objectstack-ai#20713) are the precedents.
The card stays open for the lane's remaining packages, so this PR says
`Part of`.

That is **12 sites on 12 lines in 5 files, covering 6 numbers**,
rewritten to **6 distinct commits**:
- the census's **8 sites**:
`downstream-contract/src/additional-domains.fixtures.ts` (2),
`http-conformance/src/adapter.ts` (4) and
`vitest-filter-preflight/src/index.ts` (2), 5 numbers;
- **4 test-file comment sites** in 2 test files under
`http-conformance/src/` (the census leaves `*.test.ts` out; stages 1 to
7 took test comments too).

Only comments changed: **12 lines out, 12 in**, and every touched file
keeps its line count, so no line citation into these files moves. **No
citation number is added.** Over the 12 line pairs, added-minus-removed
numbers is empty, and no PR number stands newly on any line. The two
numbers still on changed lines (`objectstack-ai#17978`, `objectstack-ai#14554`) were already on
them, and both answer 200.

**Two numbers have an ADR beside them, and both ADRs stay.**
- `objectstack-ai#6083`'s line already cited ADR-0122 phase 2. The ADR's own amendment
records phase 2, so the line now reads "ADR-0122 phase 2, commit
53068c1", the same pair spec stage 1 wrote in
`contracts/data-engine.ts`.
- `objectstack-ai#10485`'s line cites ADR-0049, the enforce-or-remove principle it was
retired under. No ADR records the theme retirement itself, so the commit
is the anchor, and the ADR stays beside it, as in the landed stages.

None of the other four numbers appears in `docs/adr/` or
`scripts/adr-anchors/`. The grep reads 0 hits for them. The control,
`objectstack-ai#5551`, reads 1 hit in ADR-0122.

**No changeset, and `skip-changeset`.** None of the three touched
packages publishes anything (see Changeset below).

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

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/qa/`. Both runs enumerated the whole board.

| reading | tree | board | whole-repo `allocated-but-absent` |
`packages/qa` sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `679f95ec5c`, run 2026-09-29T21:05:44Z to 21:09:44Z |
enumerated, 186 pages, frontier objectstack-ai#20718, 18,545 numbers | 1,185 | **8** |
8 | 5 | 3 |
| after | `30aae6a3e6`, run 21:18:38Z to 21:22:42Z | enumerated, 186
pages, frontier objectstack-ai#20720, 18,547 numbers | 1,177 | **0** | 0 | 0 | 0 |

The whole-repo drop of 8 is exactly these sites. A site-by-site diff of
the two JSON outputs has 8 findings gone, all under `packages/qa/*/src`,
and none added. The other three tallies are equal in both runs:
`resolves` 32,982, `resolves-as-pull-request` 1,984 and
`cross-repo-unjudged` 995. `packages/qa/*/src` is byte-identical at
`30aae6a3e6` and at the head; the two later commits are merges of
`origin/main` that touch nothing in `packages/qa`.

**Supplementary scan (test files and everything outside `src/`
included).** The gate's exported `extractCitations` and
`classifyCitation` ran over all 221 tracked files under `packages/qa`
(`CHANGELOG.md` excluded), with the board from the gate's own
`probeBoard`. The totals are 1,678 citations and 139 dead before, and
1,666 and 127 after.
- Under `src/`, before: src comments 40 / 8 dead, test comments 42 / 4,
src strings 1 / 0, test strings 9 / 0.
- Under `src/`, after: src comments 32 / 0, test comments 38 / 0,
strings unchanged. Nothing dead is left under any `src/`, strings
included, so there is no form-D residue in this stage's surface.
- Its before list of src comment sites is identical to the census's.
- The 127 left are all outside `src/**` (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it. Each
anchor was read in its message, changeset or diff, not only its subject.
Where the pull request that landed an anchor still answers, its body's
first line names the dead number, and that is noted.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#6083` | `downstream-contract/src/additional-domains.fixtures.ts:11`
| `53068c130`, ADR-0122 phase 2: the bare type name becomes the AUTHOR
state (`z.input`) and the `XInput` synonyms retire. The same commit
moved these frozen fixtures' annotations onto the bare names without
touching a literal, which is what the paragraph says. The line blames to
it, and its subject carries the number. The PR that landed it answers
404, and spec stage 1 gave the number this anchor. |
| `objectstack-ai#10485` |
`downstream-contract/src/additional-domains.fixtures.ts:133` |
`35ad101bc`: retires the `themes` carrier key and `ThemeSchema` whole,
under ADR-0049, and drops `DcTheme` from these fixtures. The line blames
to it, and its subject carries the number. The spec, rest, runtime and
cli stages gave the number this anchor. |
| `objectstack-ai#6143` | `http-conformance/src/adapter.ts:32`, `:189`, `:257`,
`:346`; `http-conformance/src/fallback-seam.conformance.test.ts:4`,
`:27` | `12298c7d6`, which does two things: `NodeHttpServer` implements
the optional `setFallbackHandler` out of its own router, as a field
consulted in the route-miss branch, with the 405 answer extracted for
its second call site; and the cross-adapter suite asserts the contract's
four guarantees on both adapters. All six lines blame to it. PR objectstack-ai#6851,
which landed it, names objectstack-ai#6143 on its first line. |
| `objectstack-ai#6307` |
`http-conformance/src/query-multiplicity.conformance.test.ts:76`, `:296`
| `293476148`: refuses a repeated `?version=` on `GET` / `DELETE
/packages/:id`, and adds `package-routes-query-multiplicity.test.ts`.
Its changeset records the measured read: on `DELETE`, a repeated value
skipped the full-uninstall branch, and the call still reported success.
The lines blame to the later `68feaadd6` and `7cdbcbb30`, which cite
this earlier work by number. PR objectstack-ai#6895, which landed the anchor, names
objectstack-ai#6307 on its first line. The rest stage gave the number this anchor. |
| `objectstack-ai#17853` | `vitest-filter-preflight/src/index.ts:5` | `08f5f0e5a`: a
vitest file filter that selects nothing says so, even when the rest of
the run selects something. This is the first implementation, in
`packages/cli`. The line blames to `c667d8c80`, the shared port for all
eight project-declaring packages; its number is `objectstack-ai#17978`, which answers
200 and stays. PR objectstack-ai#17965, which landed the anchor, names objectstack-ai#17853 on its
first line. |
| `objectstack-ai#13504` | `vitest-filter-preflight/src/index.ts:273` | `44813ba57`:
splits `packages/cli`'s suite into the named `unit` and `integration`
tiers, decided on behaviour, with the partition pin. That is half of the
"tier walk" this sentence names, and its diff heads the new section with
this number. `objectstack-ai#14554`, the derived-population half, answers 200 and
stays. The only commit whose subject carries `objectstack-ai#13504` is `55519d503`,
the comment-only measurement half: PR objectstack-ai#13872 says it lands only that
half. So that commit is not the anchor for this sentence. The PR that
landed `44813ba57` answers 404. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 6). Each is a commit
with one parent, and each is an ancestor of `main` (`merge-base
--is-ancestor` against `cbaf04c1fd`, exit 0 for all 6). The checkout is
not shallow. The control leg `3cc8676e1` (2026-08-08, the parent of the
oldest anchor `53068c130` of 2026-08-08) exits 0, and the negative
control, this branch's own `06b8fbc2d3`, exits 1. Three anchors reuse
the landed stages' (`53068c130`, `35ad101bc`, `293476148`), so each
number carries one anchor across the tree. Three are new (`12298c7d6`,
`08f5f0e5a`, `44813ba57`).

**Numbers.** All 6 dropped numbers answer 404 by REST (probed
2026-09-29T21:16:49Z). The numbers kept on changed lines (`objectstack-ai#17978`,
`objectstack-ai#14554`) answer 200. No slash-joined citation group stands in
`packages/qa/*/src`.

## Mechanical guard: no code token moves

**H2 holds on the parser-token reading.** The emitted-`dist` reading
does not apply, because none of these packages has a build (see
Changeset below).

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, `getChildren` walked to the leaves, JSDoc nodes
excluded) of the 5 touched files at base `679f95ec5c` and at
`30aae6a3e6`. Controls mutate the head text in memory only.
- Real run: 7,917 base tokens, 0 differing (exit 0).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: all 5 files differ at token 0 (exit 1).
- String control (the first character of the `'vitest'` import specifier
in `fallback-seam.conformance.test.ts` flipped): exactly 1 differing
`StringLiteral`, at token 11 of that file (exit 1).

Every one of the 24 changed lines is a `//` or `*` comment line. A raw
scan of the 5 changed files for control bytes finds none (a positive
probe on a scratch file matched).

## Changeset

**None, and `skip-changeset`.** There is no `@objectstack/qa` package.
The three touched packages are `@objectstack/downstream-contract`,
`@objectstack/http-conformance` and
`@objectstack/vitest-filter-preflight`. Each is `"private": true`, has
no `build` script, no `files[]` and no `dist/`. `.changeset/config.json`
versions private packages but never tags or publishes them. This diff
therefore publishes nothing from any released package, so there is no
`dist` to compare and no code-mutation control to run. The measurement
is the packages' own manifests.

## Gates (head `06b8fbc2d3`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each run at this head, and
from the closure build at `527d5dca06` (the first merge; `packages/qa`
is byte-identical between the two):

```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 77s (1m17s) · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/downstream-contract^...' --filter '@objectstack/http-conformance^...' --filter '@objectstack/vitest-filter-preflight^...' 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 11s · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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/downstream-contract exec vitest run --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 3s · declare it in the PR body · pnpm --filter @objectstack/downstream-contract typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 8s · declare it in the PR body · pnpm --filter @objectstack/http-conformance exec vitest run --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 4s · declare it in the PR body · pnpm --filter @objectstack/http-conformance typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 4s · declare it in the PR body · pnpm --filter @objectstack/vitest-filter-preflight exec vitest run --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 1s · declare it in the PR body · pnpm --filter @objectstack/vitest-filter-preflight typecheck
```

- **Build.** The three packages' dependency closure was built: 61 of 81
workspace projects, at `30aae6a3e6` and again at `527d5dca06`. Then the
whole workspace was built with `turbo run build --filter='./packages/*'
--filter='./packages/*/*'`: 71 of 71 tasks at this head, 66 of them
cache hits. The tree was clean after each build.
- **Tests (`vitest run`), at this head and at both earlier commits:**
  - `downstream-contract`: 3 files, 31 tests passed;
  - `http-conformance`: 8 files, 102 tests passed;
  - `vitest-filter-preflight`: 3 files, 111 tests passed.
  - These are every test file each package has.
- **Typecheck.** All three `typecheck` scripts exit 0;
`downstream-contract`'s is also one of CI's consumer-gate type-check
lanes. `http-conformance`'s `check:test-typecheck` holds: 3 files, 27
errors, 10 pinned signatures. `tsc --listFiles` shows every touched file
compiled:
  - `downstream-contract/tsconfig.json`: 11 files, 3 of them tests;
- `http-conformance/tsconfig.test.json`: 11 files, all 8 tests,
including both touched test files and `adapter.ts`;
  - `vitest-filter-preflight/tsconfig.json`: 6 files, 3 of them tests.
- **Lint.** The repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-29T21:48:59Z to 21:49:26Z), and at
`527d5dca06` before the second merge.
- **Citation judging.** `node scripts/check-issue-citations.mjs --base
origin/main`, with `origin/main` at `cbaf04c1fd` and merged, judges 2
citations on the changed lines of 3 files (the kept `objectstack-ai#17978` and
`objectstack-ai#14554`). Both resolve, and the run exits 0. The pinned merged base of
the first merge (`--base 1ab9892`, at `527d5dca06`) gives the same 2
citations and also exits 0.
- **Derived gates.** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 53 families, identical at
`527d5dca06` and at this head. All 53 exit 0 at this head. `--ran` with
the exit-coded record reads "53 derived, 53 run, 0 NOT-MEASURED, 0
UNRUN" (a derived zero). Among them:
  - `check:issue-citations`;
  - `check:doc-authoring` (808 pinned sites, no growth);
  - `check:nul-bytes` (9,342 text files, no raw control bytes);
- `check:published-files`, `check:type-check-coverage` and
`check:type-check-debt`.
- **Artifact rosters.** 36 of the 39 non-self-test roster rows exit 0.
These include the three the derivation marks as keeping their roster
under one of this diff's paths (`check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`). The other three
need a pull request's context; they are run against this PR once it
exists, and the results are reported on the card. The 18 self-test-only
rows grade their checkers' fixtures and cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `679f95ec5c` the filtered census answers 8 sites
on 8 lines, 5 numbers, in 3 files, as on the seat's `0be898499f`. The
whole-repo count is 1,185.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/qa`. No site was left for an open PR or for an unfound anchor.
The file lists of all 12 open PRs were read at 2026-09-29T21:13:53Z:
only the Version Packages PR objectstack-ai#20639 touches `packages/qa`, in
`CHANGELOG.md` and `package.json`.
- **H2 holds on the parser-token reading.** The comment-stripped
(parser-token) diff of all 5 touched files is empty, and its controls
fire. The `dist` reading is not available, because none of the packages
has a build.

## Acceptance notes

- **`packages/qa` outside `src/**`, a later stage of the card.** The
census surface is `packages/**/src/**`, and `packages/qa/dogfood` has no
`src/` at all. The supplementary scan counts 127 dead sites left in
`packages/qa` outside `src/**`:
- `dogfood/test/**` and `dogfood/vitest.config.ts`: 122 sites, 27
numbers, 28 files (94 comments, 28 strings);
- `downstream-contract/test/contract.test.ts:46` (`objectstack-ai#10485`, a comment);
- `vitest-filter-preflight/test/config-wiring-sweep.test.ts:6` and
`test/filter-preflight.test.ts:5` (`objectstack-ai#17853`, comments);
- `vitest-filter-preflight/package.json:6` (`objectstack-ai#17853`, in the package
`description`);
- `http-conformance/test-typecheck-debt.json:2` (`objectstack-ai#13470`, in the
generated `_comment`, whose producer is
`scripts/check-test-typecheck.mts`; that producer is objectstack-ai#20715's, and it is
never fixed by hand).

  None of them is in this stage's surface, and none moved.
- **ADR-0122's own status line and amendment heading** cite `objectstack-ai#6083`
(404). `docs/adr/**` is a governed surface and one of the gate's
deferred surfaces, so it is noted here, not touched.
- **Card-word residue, cited nowhere.**
`vitest-filter-preflight/src/index.ts:117` says "this card" about 150
lines from either rewritten line. It cites no number, so it was left, as
the landed stages left theirs.
- **The moving `origin/main`.** The branch merged `origin/main` twice.
The first merge (`527d5dca06`) took `1ab98926b0` (a spec retirement,
nothing in `packages/qa`). After it, the shared ref advanced to
`cbaf04c1fd`, the plugin-approvals re-anchor (PR objectstack-ai#20717). Against that
moved ref, `--base origin/main` then read the old plugin-approvals lines
as this branch's additions: it judged 31 citations and exited 2, because
the diff was two-dot. That run is not a measurement of this change. The
pinned base answered exit 0. The second merge (`06b8fbc2d3`) took
`cbaf04c1fd`, and every gate above was re-run on it. CI judges the merge
ref.

## Deviations

- **The dispatch's `packages/qa/src/**` and `@objectstack/qa`** do not
exist as spelled. The surface was read as `packages/qa/*/src/**`, the
census's own reading of `packages/**/src/**`. The changeset measurement
was taken per touched package.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. The two
merge commits carry git's default message.

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

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ges/cloud-connection/src to the commits that decided them (objectstack-ai#20735)

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

## What changed

This is stage 9 of the `domain:cli` lane of the dead-citation sweep:
`packages/cloud-connection/src`. Every comment site there 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 keeps saying in its own words what
that commit decided. PR objectstack-ai#20533 is the method, and stages 1 to 8 of this
card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#20703,
PR objectstack-ai#20713, PR objectstack-ai#20723) are the precedents. The card stays open for the
lane's remaining packages, so this PR says `Part of`.

That is **10 sites on 10 lines in 3 files, covering 3 numbers**,
rewritten to **3 distinct commits**:
- the census's **5 sites**, all in
`src/marketplace-install-local-plugin.ts` (3 numbers);
- **5 test-file comment sites** in 2 test files (the census defers
`*.test.ts`; stages 1 to 8 took test comments too).

Only comments changed: **10 lines out, 10 in**, and every touched file
keeps its line count (1,969 / 377 / 308), so no line citation into these
files moves. **No citation number is added**: over the 10 line pairs,
added-minus-removed numbers is empty, and no PR number stands on an
added line. No ADR or ruling-record file in `docs/adr/` or
`scripts/adr-anchors/` records any of these 3 decisions (a grep for the
3 numbers there reads 0 hits, with a control number from the same tree,
`objectstack-ai#7329`, reading 1), so every anchor is a commit.

A **`patch` changeset** for `@objectstack/cloud-connection` rides along,
because the rewritten docblocks reach `dist` (measured below). That is
stage 6's case (PR objectstack-ai#20703), not stages 5 and 7's.

## Census: `packages/cloud-connection`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/cloud-connection/`. Both runs enumerated the whole
board.

| reading | tree | board | whole-repo `allocated-but-absent` |
`packages/cloud-connection` sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `b291fcdae9`, run 2026-09-29T22:34:12Z to 22:38:18Z |
enumerated, 186 pages, frontier objectstack-ai#20731, 18,558 numbers | 1,153 | **5** |
5 | 3 | 1 |
| after | `4a1f38a4e6`, run 22:44:08Z to 22:47:58Z | enumerated, 186
pages, frontier objectstack-ai#20731, 18,558 numbers | 1,148 | **0** | 0 | 0 | 0 |

The whole-repo drop of 5 is exactly these sites: a site-by-site diff of
the two JSON outputs has 5 findings gone, all in
`packages/cloud-connection/src/marketplace-install-local-plugin.ts`, and
none added. The other three tallies (`resolves` 32,994,
`resolves-as-pull-request` 1,984, `cross-repo-unjudged` 995) are equal
in both runs. `packages/cloud-connection/src` is byte-identical at
`4a1f38a4e6` and at the head.

**Supplementary scan (test files included).** The gate's exported
`extractCitations` and `classifyCitation` over all 44 `.ts` files under
`src/`, with the board from the gate's own `probeBoard`: 301 citations
and 14 dead before (src comments 5, test comments 5, src strings 1, test
strings 3), 291 and 4 after (0, 0, 1, 3). Its before list of src comment
sites is identical to the census's. The 4 left are strings, the form-D
stage (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it, and
each anchor was read in its message, changeset or diff, not only its
subject.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#9011` | `marketplace-install-local-plugin.ts:35`, `:1001`, `:1821`;
`marketplace-install-local-capability-enumeration.test.ts:50`, `:303`;
`marketplace-install-local-list-posture.test.ts:4`, `:302` |
`01074e551`: the install-local listing requires an authenticated
principal (anonymous gets 401) and serves `installedBy` / `storageDir`
only to a `manage_metadata` holder, the maintainer's 2026-08-16 "Option
3" that `:1008` still names; it also extracts the one
`refuseUnauthenticated` 401 envelope that `:1821` describes. All seven
lines blame to it. The PR that landed it (PR objectstack-ai#9256, which answers 200)
names objectstack-ai#9011 on its first line. `list-posture.test.ts:302` now reads "The
wire shape before commit 01074e5" for "The pre-(number) wire shape". |
| `objectstack-ai#8919` | `marketplace-install-local-plugin.ts:98`;
`marketplace-install-local-capability-enumeration.test.ts:40` |
`b5378550e`: gates the `/meta` publish and rollback promotion verbs on
`manage_metadata` and adds
`meta-write-door-capability-enumeration.test.ts`, the enumeration pin
`:40` names as its precedent. Both lines blame to `e0695b582`, the
commit that gated the four mutating install-local doors for objectstack-ai#8976 (which
answers 200), whose message cites this gate as the precedent. Stages 2
and 5 gave the number this anchor. The PR that landed `b5378550e`
answers 404 too. |
| `objectstack-ai#13279` | `marketplace-install-local-plugin.ts:1813` | `6a180e42d`: a
failed permission-store read raises `AuthzStoreUnavailableError` instead
of resolving as an unauthenticated or capability-less principal, and
each fail-closed transport `catch` re-raises it. The line blames to it;
PR objectstack-ai#13475 names objectstack-ai#13279. Stages 1, 2 and 4 gave the number this anchor. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 3), is a commit, has
one parent, and is an ancestor of `main` (`merge-base --is-ancestor`
against `36d043be17`, exit 0 for all 3). The checkout is not shallow.
The control leg `818fcafda` (2026-08-16, the parent of the oldest anchor
`b5378550e` of 2026-08-16) exits 0, and the negative control, this
branch's own `16a88d69b2`, exits 1. Two anchors reuse the landed stages'
(`b5378550e`, `6a180e42d`), so each number carries one anchor across the
tree; one is new (`01074e551`).

**Numbers.** All 3 dropped numbers answer 404 by REST (probed
2026-09-29T22:40:35Z). The numbers kept near the changed lines (`objectstack-ai#8976`,
`objectstack-ai#15353`) answer 200. Three slash-joined groups stand in
`packages/cloud-connection/src`, whose later halves the citation grammar
does not read (`objectstack-ai#6603/objectstack-ai#7020`, `objectstack-ai#4127/objectstack-ai#4251` twice); every half answers
200, so none is dead.

## Mechanical guard: no code token moves

**H2 holds on the comment-stripped reading; the emitted `dist` is NOT
byte-identical, because the docblocks ship.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, JSDoc nodes excluded) of the 3 touched files at base
`b291fcdae9` and at `4a1f38a4e6`. Controls mutate the head text in
memory only.
- Real run: 12,349 base tokens (8,351 / 2,246 / 1,752), 0 differing
(exit 0 for each file).
- Comment-insertion control: 0 differing (exit 0).
- Code-insertion control: all 3 files differ (exit 1).
- String control (`'Authentication required.'` to `'Authentication
requireD.'` in `refuseUnauthenticated`): exactly 1 differing
`StringLiteral`, at token 7,973 of `marketplace-install-local-plugin.ts`
(exit 1).

**Emitted `dist`.** `pnpm --filter @objectstack/cloud-connection build`
at the head, then at base (the base tree of
`packages/cloud-connection/src` restored in place under a trap-armed
restore; an on-disk probe read `[objectstack-ai#13279]` 1 and `commit 6a180e4` 0
before that build; afterwards every touched blob equals its HEAD blob
and `git diff HEAD` is empty), with the same dependency builds:
- `index.cjs`, `index.js`, `index.d.ts` and `index.d.cts` differ;
`index.cjs.map` and `index.js.map` are equal.
- The same parser comparison over the four differing `dist` files reads
0 differing tokens (19,465 / 18,741 / 16,868 / 16,868), so the whole
`dist` delta is comment text. Its code control (a code line appended
after a newline) reads COUNT DIFFERS in each.
- The new wording is in `dist`: "commit 01074e5" appears 2 times in
`index.js` and `index.cjs` and 3 times in each declaration file, where
the base build carries `objectstack-ai#9011` in the same places.
- Code-mutation control (`scripts/ablation-replace.mjs`, anchor
`'Authentication required.'` hit 1 to 0, planted marker 0 to 1, blob
`2ef0f0ae8bac` to `77c3cef6324b`; `scripts/ablation-dist-preflight.mjs`
found the marker in `dist`): `index.cjs`, `index.js` and both `.map`
files differ from the head build. The blob was restored to HEAD
`2ef0f0ae8bac` with `git diff HEAD` empty, `dist` was rebuilt, its six
sha256 values equal the first head build, and the preflight in
`--absent` mode reads the marker absent from all 6 files with a clean
tree.

A raw scan of the 4 changed files for control bytes finds none (a
positive probe on a scratch file matched).

## Changeset

**`patch` for `@objectstack/cloud-connection`**
(`.changeset/cloud-connection-provenance-anchors.md`), in PR objectstack-ai#20632's
form. `@objectstack/cloud-connection`'s `files[]` is `dist`, `README.md`
and `CHANGELOG.md`, and the build above emits different `index.js` /
`index.cjs` / `index.d.ts` / `index.d.cts` at base and head, so this
diff publishes. `check-changeset-no-major`, `check-empty-changeset`,
`check-adr-0087-registration` and `check-changeset-fixed` all exit 0.

## Gates (head `16a88d69b2`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its disclosure, verbatim, from each run (the closure
build at `4a1f38a4e6`, whose `packages/cloud-connection` and dependency
closure are byte-identical to this head; the whole-workspace build, the
tests and the typecheck at this head; the three `dist` builds at
`4a1f38a4e6`, whose `packages/cloud-connection/src` is byte-identical to
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 59s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/cloud-connection...' 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 118s (1m58s) · declare it in the PR body · pnpm turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 11s · declare it in the PR body · pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2
os-verify-lock: VERDICT command-exit 2 · 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 exec tsc --noEmit -p tsconfig.json --listFiles
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 3s · declare it in the PR body · pnpm --filter @objectstack/cloud-connection 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 3s · declare it in the PR body · pnpm --filter @objectstack/cloud-connection 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 3s · declare it in the PR body · pnpm --filter @objectstack/cloud-connection build
```

- **Build:** `@objectstack/cloud-connection` with its closure (33 of 81
workspace projects), then the whole workspace, `turbo run build
--filter='./packages/*' --filter='./packages/*/*'`, 71 of 71 tasks,
after the merge. The tree was clean after both, and the package's six
`dist` files after the whole build equal the first head build by sha256.
- **Tests:** `vitest run`: 30 files, 397 tests passed (every `*.test.ts`
under `src/`), at this head and before the merge.
- **Typecheck:** `@objectstack/cloud-connection` has no `typecheck`
script; it is a `DEBT` entry in `scripts/check-type-check-coverage.mjs`
(13 errors: 11 TS2493, 2 config-tier). `tsc --noEmit -p tsconfig.json`
exits 2 with exactly those 13 (11 TS2493, 2 TS2550), all in three test
files this PR does not touch (`cloud-connection-plugin.test.ts` 4,
`connection-credential-store.test.ts` 7,
`marketplace-install-local-bundle.test.ts` 2). `--listFiles` compiles
all three touched files and all 30 test files. `check:type-check-debt`
and `check:type-check-coverage` exit 0, and the `dist` build's DTS step,
this package's type gate, succeeds.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-29T23:01:12Z to 23:01:39Z).
- **Citation judging:** after merging `origin/main` (`36d043be17`),
`node scripts/check-issue-citations.mjs --base origin/main` reports "no
issue citations added against 36d043b (1 file(s) read)" (exit 0);
pinned `--base 36d043b` reads the same.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 61 families. All 61 exit
0, and `--ran` with the exit-coded record reads "61 derived, 61 run, 0
NOT-MEASURED, 0 UNRUN" (a derived zero). Among them:
`check:issue-citations`, `check:doc-authoring`, `check:nul-bytes`,
`check:published-files`, `check:type-check-debt`,
`check-adr-0087-registration`, `check-empty-changeset`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0,
including the four the derivation marks as keeping their roster under
one of this diff's paths (`check-changeset-fixed`,
`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three need a pull request's
context; they are run against this PR once it exists and reported on the
card. The 18 self-test-only rows grade their checkers' fixtures and
cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `b291fcdae9` the filtered census answers 5 sites
on 5 lines, 3 numbers, all in `src/marketplace-install-local-plugin.ts`,
as on the seat's `0be898499f`. The whole-repo count is 1,153.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/cloud-connection`. No site was left for an open PR (the file
lists of all 10 open PRs were read at 2026-09-29T22:41:18Z: only the
Version Packages PR objectstack-ai#20639 touches `packages/cloud-connection`, in
`CHANGELOG.md` and `package.json`) or for an unfound anchor.
- **H2 holds on the token reading, not on the `dist` reading.** The
parser leaf-token diff of all 3 touched files is empty with its controls
firing. The emitted `dist` differs, and the difference is comment text
only (token-identical `dist` with a code control). That is why the
changeset ships.

## Acceptance notes

- **Strings, the form-D stage.** 4 dead numbers remain in string
literals in `packages/cloud-connection/src`: `objectstack-ai#9011` in the three
`describe` titles of `marketplace-install-local-list-posture.test.ts`
(`:206`, `:247`, `:287`, no assertion text), and `objectstack-ai#9011` in the `note`
string of the `GET /api/v1/marketplace/install-local` row of
`cloud-connection-route-ledger.ts` (`:215`), a runtime string already
recorded in `scripts/doc-authoring-prose-id.baseline.json`. They stay on
the card for its form-D stage; no string moved here.
- **Outside `src/**`, a later stage of the card:**
`packages/cloud-connection/vitest.config.ts:64` cites `objectstack-ai#16917` (404).
The other citations in `packages/cloud-connection` outside `src/**`
(`CHANGELOG.md` excluded) answer 200: `README.md:108` (`objectstack-ai#10805`,
`objectstack-ai#12681`) and `vitest.config.ts` (`objectstack-ai#10374`, `objectstack-ai#11480`, `objectstack-ai#7668/objectstack-ai#7778`,
`objectstack-ai#7955`, `objectstack-ai#10374/objectstack-ai#13522`).
- **Card-word residue, cited nowhere.** A few docblocks still say "this
card's ruling" or "That ruling has since landed" a paragraph away from a
rewritten line
(`marketplace-install-local-capability-enumeration.test.ts:48`,
`marketplace-install-local-list-posture.test.ts:12`). They cite no
number, so they were left, as the landed stages left theirs.
- **The moving `origin/main`.** The branch merged `origin/main` once
(`16a88d69b2`, merging `36d043be17`: `service-automation` and two
changesets, nothing in `packages/cloud-connection` or its dependency
closure).

## Deviations

- **The first token-guard run was void.** It looped over the touched
files in a zsh shell, which does not word-split an unquoted variable, so
each invocation received all three paths as one argument and read
nothing; it was rerun under bash before any reading was used.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…kages/plugins/plugin-hono-server/src to the commits that decided them (objectstack-ai#20741)

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

## What changed

This is stage 10 of the `domain:cli` lane of the dead-citation sweep:
`packages/plugins/plugin-hono-server/src`. Every comment site there 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 keeps saying in its own words
what that commit decided. PR objectstack-ai#20533 is the method, and stages 1 to 9 of
this card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR
objectstack-ai#20703, PR objectstack-ai#20713, PR objectstack-ai#20723, PR objectstack-ai#20735) are the precedents. The card
stays open for the lane's remaining packages, so this PR says `Part of`.

That is **24 sites on 24 lines in 5 files, covering 5 numbers**,
rewritten to **5 distinct commits**:
- the census's **5 sites**: `src/adapter.ts` 4,
`src/current-user-endpoints.ts` 1 (4 numbers);
- **19 test-file comment sites** in 3 test files (the census defers
`*.test.ts`; stages 1 to 9 took test comments too):
`ui-plugin-auto-discovery.pin.test.ts` 16,
`handler-throw-declared-envelope.test.ts` 2,
`current-user-endpoints-localization.test.ts` 1.

Only comments changed: **24 lines out, 24 in**, every one of them a site
(no companion line), and every touched file keeps its line count (1,660
/ 1,020 / 335 / 403 / 697), so no line citation into these files moves.
**No citation number is added**: over the 24 line pairs, the added
numbers are a subset of the removed ones (`objectstack-ai#16599` x2, `objectstack-ai#9864`,
`objectstack-ai#16334`, all answering 200, stay where they stood), and no PR number
stands on an added line. No ADR or ruling-record file in `docs/adr/` or
`scripts/adr-anchors/` records any of these 5 decisions (a grep for the
5 numbers there reads 0 hits; the control number `objectstack-ai#7329` reads 1 in the
same tree), so every anchor is a commit.

A **`patch` changeset** for `@objectstack/plugin-hono-server` rides
along, because one rewritten comment reaches `dist` (measured below).
That is stage 6's and stage 9's case (PR objectstack-ai#20703, PR objectstack-ai#20735), not stages
5 and 7's.

## Census: `packages/plugins/plugin-hono-server`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/plugins/plugin-hono-server/`. Both runs enumerated the
whole board.

| reading | tree | board | whole-repo `allocated-but-absent` | package
sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `f927864ea0`, run 2026-09-29T23:42:29Z to 23:46:56Z |
enumerated, 186 pages, frontier objectstack-ai#20735, 18,562 numbers | 1,105 | **5** |
5 | 4 | 2 |
| after | `91ce7e5e8f`, run 23:58:22Z to 2026-09-30T00:02:19Z |
enumerated, 186 pages, frontier objectstack-ai#20737, 18,564 numbers | 1,100 | **0** |
0 | 0 | 0 |

The whole-repo drop of 5 is exactly these sites: a site-by-site diff of
the two JSON outputs has 5 findings gone (`adapter.ts:225`, `:227`,
`:308`, `:349`; `current-user-endpoints.ts:448`) and none added. The
other three tallies (`resolves` 33,003, `resolves-as-pull-request`
1,984, `cross-repo-unjudged` 995) are equal in both runs.
`packages/plugins/plugin-hono-server` is byte-identical at `91ce7e5e8f`
and at the head.

**Supplementary scan (test files, strings and files outside `src/`
included).** The gate's exported `extractCitations` and
`classifyCitation` over all 41 tracked files of the package
(`CHANGELOG.md` excluded), comment-prose and whole-file projections,
with the board from the gate's own `probeBoard`: 347 citations and 32
dead before, 323 and 8 after. Under `src/`: comments 5 dead to 0, test
comments 19 to 0, strings 0 and test strings 2 unchanged. Its before
list of `src/` comment sites equals the census's. The 8 left are 2 test
strings and 6 sites outside `src/` (see Acceptance notes).

## Per-site table

`git blame` at the base ties each line to the commit that wrote it, and
each anchor was read in its message, changeset or diff, not only its
subject.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#13279` | `adapter.ts:225`, `:227`;
`handler-throw-declared-envelope.test.ts:84`;
`current-user-endpoints-localization.test.ts:90` | `6a180e42d`:
`tryFind` in `resolveAuthzContext` raises `AuthzStoreUnavailableError`
(503 `SERVICE_UNAVAILABLE`) when a permission-store read is issued and
throws, instead of answering it as an empty read, and each fail-closed
transport `catch` (`requireDatasourceAdmin` in `service-datasource`
among them) re-raises it: an unreadable store licenses no verdict, the
maintainer's 2026-08-30 ruling recorded in its message. The first three
lines blame to `cefe068702` (the declared-envelope rendering, PR objectstack-ai#17412)
and the fourth to `5f7fa1de0`; both wrote them citing this ruling. PR
objectstack-ai#13475 (200) names objectstack-ai#13279. Stages 1, 2, 4 and 9 gave the number this
anchor. |
| `objectstack-ai#9934` | `adapter.ts:308`;
`handler-throw-declared-envelope.test.ts:152` | `79c46da90`: the
producer-side user-facing marking for hook refusals, the `userMessage`
channel, a text field a producer sets at throw time and every classified
envelope carries. Both lines blame to `cefe068702`. The PR that landed
it (PR objectstack-ai#9992) answers 404 too. Stages 1, 2, 4 and 6 gave the number this
anchor. |
| `objectstack-ai#6307` | `adapter.ts:349` | `293476148`: refuse a repeated
`?version=` on `GET`/`DELETE /packages/:id` rather than pick one value,
through `readSingleQueryValue`, which it introduces. The line blames to
`7cdbcbb306` (surface repeated query parameters as arrays, PR objectstack-ai#7396),
which says it follows that direction. Stages 2 and 8 gave the number
this anchor. |
| `objectstack-ai#6216` | `current-user-endpoints.ts:448` | `f586f1a89`: one
`ExecutionContext` assembler with two named anonymous entries,
`assembleExecutionContext` the default, fail-closed one and
`assembleExecutionContextOrGuest` the explicit guest one, the
maintainer's 2026-08-08 Option A recorded in its docblock. The line
blames to `6615a024c3` (the current-user faces adopt the shared
assembler). Stages 1, 2 and 7 gave the number this anchor. |
| `objectstack-ai#16721` | `ui-plugin-auto-discovery.pin.test.ts:27`, `:37`, `:42`,
`:180`, `:211`, `:217`, `:360`, `:363`, `:495`, `:498`, `:594`, `:596`,
`:604`, `:608`, `:631`, `:646` | `51ae73123`: `LiteKernel.use()` runs
the same `assertPluginContract` as `ObjectKernel.use()` and refuses the
same plugin objects with the same envelope, the maintainer's 2026-09-08
option A (the kernels converge) recorded in its changeset. 15 lines
blame to it; `:180` blames to `3c48234b3`, which re-wrapped that
sentence and keeps its fact. Its `lite-kernel.ts` docblock records the
measurement taken before converging, which `:604` describes ("before
commit 51ae731"). `:363`, `:498` and `:631` said the `hono-plugin.ts`
question was "noted on" the dead number; that note is the text this
commit wrote into this file, so they now say "raised with" / "recorded
with" it. New anchor; no other package has re-anchored this number yet.
|

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 5), is a commit, has
one parent, and is an ancestor of `main` (`merge-base --is-ancestor`
against `f927864ea0`, exit 0 for all 5). The checkout is not shallow.
The control leg `2672f855fa` (2026-08-09, the parent of the oldest
anchor `293476148`) exits 0 against `origin/main`, and the negative
control, this branch's own head, exits 1. Four anchors reuse the landed
stages' (`6a180e42d`, `79c46da90`, `293476148`, `f586f1a89`), so each
number carries one anchor across the tree; one is new (`51ae73123`).

**Numbers.** All 5 dropped numbers answer 404 by REST (probed
2026-09-30T00:14:17Z). The numbers kept on or beside the changed lines
answer 200: `objectstack-ai#16599`, `objectstack-ai#9864`, `objectstack-ai#16334`, `objectstack-ai#16363`, `objectstack-ai#16049`, `objectstack-ai#6878`,
`objectstack-ai#3867`, `objectstack-ai#8086`, `objectstack-ai#16545`, `objectstack-ai#15999`, `objectstack-ai#5090`.
`packages/plugins/plugin-hono-server/src` has no slash-joined `#A/#B`
without spaces; the spaced pairs (`objectstack-ai#3867 / objectstack-ai#8086`, `objectstack-ai#2408 / objectstack-ai#3361`) are
read by the grammar and every half answers 200.

## Mechanical guard: no code token moves

**H2 holds on the parser-token reading; the emitted `dist` is NOT
byte-identical, because one rewritten `//` comment sits inside a
returned object literal and the bundler keeps it.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, `getChildren` walk, JSDoc nodes excluded) of the 5
touched files at base `f927864ea0` and at the head. Controls mutate the
head text in memory only.
- Real run: 15,054 base tokens (4,831 / 2,134 / 3,559 / 2,363 / 2,167),
0 differing, exit 0.
- Comment-insertion control: 0 differing, exit 0.
- Code-insertion control: all 5 files differ, exit 1.
- String control (the first character of the first import specifier
flipped in each file): exactly 1 differing `StringLiteral` per file,
exit 1.

All 48 changed lines (24 out, 24 in) are `//` or `*` comment lines.

**Emitted `dist`.** `pnpm --filter @objectstack/plugin-hono-server
build` at the head, then at base (the base blobs of the 5 touched files
restored in place under a trap-armed restore; an on-disk probe read
`objectstack-ai#9934` 1 and `commit 79c46da` 0 in `adapter.ts` before that build;
afterwards every touched blob equals its HEAD blob, `git diff HEAD` is
empty and the status is clean), with the same dependency builds:
- `index.js` and `index.mjs` differ, in one line each: the
`adapter.ts:308` comment, `refusal text (objectstack-ai#9934)` at base and `refusal
text (commit 79c46da)` at head. `index.d.ts`, `index.d.mts` and both
`.map` files are equal. No docblock of this diff reaches the declaration
files.
- The same parser comparison over the two differing files reads
identical tokens (10,818 in `index.js`, 10,521 in `index.mjs`), so the
whole `dist` delta is comment text. Its code control (a code line
appended) reads COUNT/TOKENS DIFFER in each.
- Code-mutation control (`scripts/ablation-replace.mjs`, wrap mode,
anchor `message: 'No response from handler' }` hit 1 to 0, planted
marker 0 to 1, blob `6a0c10f76282` to `d5223196afeb`;
`scripts/ablation-dist-preflight.mjs` found the marker in `index.js` and
`index.mjs`): `index.js`, `index.mjs` and both `.map` files differ from
the head build. The blob was restored to HEAD `6a0c10f76282` with `git
diff HEAD` empty, `dist` was rebuilt, its six sha256 values equal the
first head build, and the preflight in `--absent` mode reads the marker
absent from all 6 files with a clean tree.

A raw scan of the 6 changed files for ASCII control bytes finds none,
and `check:nul-bytes` exits 0.

## Changeset

**`patch` for `@objectstack/plugin-hono-server`**
(`.changeset/plugin-hono-server-provenance-anchors.md`), in PR objectstack-ai#20632's
form. The package's `files[]` is `dist`, `README.md` and `CHANGELOG.md`,
and the build above emits different `index.js` / `index.mjs` at base and
head, so this diff publishes. `check-changeset-no-major`,
`check-empty-changeset`, `check-adr-0087-registration` and
`check-changeset-fixed` all exit 0.

## Gates (head `03e5f4c0fd`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its official wording, verbatim (printed by every run; the
command line differs per run and is listed in the verdicts below):

> **Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
> could not take the shared verify lock on this host: no usable `flock`.
The shared
> verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
> not ship it), so the command below was run directly, without the lock
—
> a declared narrowing, not a silent one. No serialization guarantee
held for this
> run, nor for any sibling agent in this container while it ran.

Its verdict line from each run (the closure build and the three `dist`
builds at `feaf0c9b73`, whose `packages/plugins/plugin-hono-server` is
byte-identical to this head; the first whole-workspace build, tests and
typecheck at `91ce7e5e8f`; the second whole-workspace build, tests and
typecheck at this head after the merge; the scratch-script paths
shortened to `SCRATCH`):

```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 27s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-hono-server...' 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 3s · declare it in the PR body · bash SCRATCH/base-build.sh SCRATCH
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 3s · declare it in the PR body · node scripts/ablation-replace.mjs --file packages/plugins/plugin-hono-server/src/adapter.ts --anchor "message: 'No response from handler' }" --replacement "message: 'No response from handler ABLMARK20594S10' }" -- bash SCRATCH/mut-inner.sh SCRATCH
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 3s · declare it in the PR body · pnpm --filter @objectstack/plugin-hono-server 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 12s · declare it in the PR body · pnpm --filter @objectstack/plugin-hono-server exec vitest run --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 17s · declare it in the PR body · pnpm --filter @objectstack/plugin-hono-server typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 118s (1m58s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 118s (1m58s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 11s · declare it in the PR body · pnpm --filter @objectstack/plugin-hono-server exec vitest run --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 16s · declare it in the PR body · pnpm --filter @objectstack/plugin-hono-server typecheck
```

- **Build:** `@objectstack/plugin-hono-server` with its closure (7 of 81
workspace projects), then the whole workspace, `turbo run build
--filter='./packages/*' --filter='./packages/*/*' --concurrency=2`, 71
of 71 tasks, before and again after the merge. The tree was clean after
each, and the package's six `dist` files after each whole build equal
the first head build by sha256.
- **Tests:** `vitest run --maxWorkers=2`: 27 files, 324 tests passed
(every `*.test.ts` under `src/`), at this head and at `91ce7e5e8f`.
- **Typecheck:** `pnpm --filter @objectstack/plugin-hono-server
typecheck` exits 0 at this head and at `91ce7e5e8f` (`tsc --noEmit`,
`tsc --noEmit -p tsconfig.typecheck.json`, and `check:test-typecheck` OK
with 0 files / 0 errors / 0 pinned signatures). `--listFiles`:
`tsconfig.json` and `tsconfig.test.json` each compile 33 `src/` files
including all 27 tests and all 5 touched files.
- **Spec artifacts:** `origin/main` brought a `packages/spec` change, so
`pnpm --filter @objectstack/spec check:generated` ran after the rebuild:
"All 15 generated artifacts are up to date" (exit 0).
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-30T00:25:09Z to 00:25:36Z), and at
`91ce7e5e8f`.
- **Citation judging:** after merging `origin/main` (`fbec216e2d`),
`node scripts/check-issue-citations.mjs --base origin/main` reports "no
issue citations added against fbec216 (2 file(s) read)" (exit 0).
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 63 families, the same
list at `91ce7e5e8f` and at this head. All 63 exit 0 at this head in one
pass, and `--ran` with the exit-coded record reads "63 derived, 63 run,
0 NOT-MEASURED, 0 UNRUN" (a derived zero). Among them:
`check:issue-citations`, `check:doc-authoring`, `check:nul-bytes`,
`check:published-files`, `check:dts-closure`,
`check:dual-build-cjs-loads`, `check:type-check-debt`,
`check-adr-0087-registration`, `check-empty-changeset`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0 at
this head, including the four the derivation marks as keeping their
roster under one of this diff's paths (`check-changeset-fixed`,
`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three need a pull request's
context; they are run against this PR once it exists and reported on the
card. The 18 self-test-only rows grade their checkers' fixtures and
cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `f927864ea0` the filtered census answers 5 sites
on 5 lines, 4 numbers, 2 files, as on the seat's `0be898499f`. The
whole-repo count is 1,105.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/plugins/plugin-hono-server`. No site was left for an open PR
(the file lists of all open PRs were read at 2026-09-29T23:48:36Z, 6
PRs, and again at 2026-09-30T00:15:14Z, 9 PRs: only the Version Packages
PR objectstack-ai#20639 touches the package, in `CHANGELOG.md` and `package.json`) or
for an unfound anchor.
- **H2 holds on the token reading, not on the `dist` reading.** The
parser leaf-token diff of all 5 touched files is empty with its controls
firing. The emitted `dist` differs in one comment line of `index.js` and
of `index.mjs`, token-identical with a code control. That is why the
changeset ships.

## Acceptance notes

- **Strings, the form-D stage.** One dead number remains in a string
literal in `packages/plugins/plugin-hono-server/src`: `objectstack-ai#16721` at the
end of the group-F `describe` title of
`ui-plugin-auto-discovery.pin.test.ts` (`:635`, a test title, no
assertion text). It stays on the card for its form-D stage; no string
moved here. The supplementary scan's second test-string hit, `:111`
(`'.os-pin{color:#123456}'`), is a CSS hex colour in a fixture, not a
citation: the whole-file projection reads it as a six-digit number,
while the census blanks strings and defers test files.
- **Outside `src/**`, a later stage of the card:**
`objectstack.config.ts:19` (`objectstack-ai#11332`) and `:26` (`objectstack-ai#10724`),
`tsconfig.test.json:3` and `:61` (`objectstack-ai#13176`),
`tsconfig.typecheck.json:12` (`objectstack-ai#11332` and `objectstack-ai#10724`; `objectstack-ai#4914` in the same
group answers 200). The other citations in the package outside `src/**`
(`CHANGELOG.md` excluded) answer 200: `tsconfig.test.json` (`objectstack-ai#14062`,
`objectstack-ai#5286`, `objectstack-ai#5449`, `objectstack-ai#12542`), `tsconfig.typecheck.json` (`objectstack-ai#13284`,
`objectstack-ai#5475`, `objectstack-ai#10756`), `vitest.config.ts` (`objectstack-ai#10374`, `objectstack-ai#9457`, `objectstack-ai#7378`;
`objectstack-ai#8129` resolves as a pull request). `README.md` carries none.
- **An open question now lives only in this file.** `hono-plugin.ts:521`
and `:523` still carry the `&& plugin.staticPath` conjunct and the
`plugin.slug || plugin.name.split('/').pop()` derivation that, since
`51ae73123`, neither published kernel's `use()` lets an input reach. The
pin file says so and leaves the call to `hono-plugin.ts`; the tracker
note it pointed at is gone, so this file's text (and commit
`3c48234b3`'s message) are the record. Unreachable defensive code, not a
defect: noted, not filed.
- **Card-word residue, cited nowhere.**
`handler-throw-declared-envelope.test.ts` still says "before this card"
(`:85`) and "the card" (`:19`, `:32`) around its rewritten lines. They
cite no dead number, so they were left, as the landed stages left
theirs.
- **The moving `origin/main`.** The branch merged `origin/main` once
(`03e5f4c0fd`, merging `fbec216e2d`: the ADR-0087 migration chain moves
to `@objectstack/spec/migrations`). `packages/spec` is in this package's
dependency closure, so the workspace was rebuilt and the package's
tests, typecheck and every gate above were rerun at the merge head;
nothing in `packages/plugins/plugin-hono-server` changed.

## Deviations

- **Three derived gates first read NOT MEASURED.**
`check:dual-build-cjs-loads`, `check:lean-entry-closure` and
`check:type-check-debt` exited 3 (PREREQUISITE NOT MET: built output
absent) in the first pass, before the whole-workspace build. Rerun after
it, each exits 0, and all 63 exit 0 in the single pass at this head.
- **The first `check:generated` run was void.** This host's global
`pnpm` is a v11 front end that rejects the `-s` each sub-gate passes, so
all 15 rows read "unexpected argument '-s'" (exit 1, nothing measured).
Rerun with the real pnpm 10.31 binary first on `PATH`, it reads all 15
up to date (exit 0).
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…kages/create-objectstack/src to the commits that decided them (objectstack-ai#20748)

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

## What changed

This is stage 11 of the `domain:cli` lane of the dead-citation sweep:
`packages/create-objectstack/src`. Every comment site there 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 keeps saying in its own words what
that commit decided. PR objectstack-ai#20533 is the method, and stages 1 to 10 of this
card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#20703,
PR objectstack-ai#20713, PR objectstack-ai#20723, PR objectstack-ai#20735, PR objectstack-ai#20741) are the precedents. The card
stays open for the lane's remaining packages, so this PR says `Part of`.

That is **25 sites on 25 lines in 10 files, covering 9 numbers**,
rewritten to **8 distinct commits**:
- the census's **3 sites**: `src/banner.ts` 2, `src/index.ts` 1 (2
numbers);
- **22 test-file comment sites** in 8 test files (the census defers
`*.test.ts`; stages 1 to 10 took test comments too):
`starter-comments-self-contained.test.ts` 9,
`scaffold-e2e-boot-probe.test.ts` 3, `banner-version.test.ts` 2,
`blank-readme-validate-disclosure.test.ts` 2,
`scaffold-next-steps-pm.test.ts` 2, `template-consistency.test.ts` 2,
`scaffold-skills-single-copy.test.ts` 1, `template-ci-workflow.test.ts`
1.

Only comments changed: **25 lines out, 25 in**, every one of them a site
(no companion line), and every touched file keeps its line count (147 /
67 / 58 / 617 / 910 / 261 / 357 / 328 / 221 / 745), so no line citation
into these files moves. **No citation number is added**: the added lines
carry no tracker number at all, and no PR number stands on an added
line. No ADR or ruling-record file in `docs/adr/` or
`scripts/adr-anchors/` records any of these 9 decisions (a grep for the
9 numbers there reads 0 hits; the control number `objectstack-ai#7329` reads 2 in the
same tree), so every anchor is a commit.

**No changeset; `skip-changeset`.** The rewritten comments do not reach
the published `dist` (measured below), as in stages 5 and 7 (PR objectstack-ai#20689,
PR objectstack-ai#20713).

**Scaffold output is untouched.** No site sits inside a template literal
or in a file the scaffolder copies: `src/templates/**` carries zero
tracker citations in either projection, and all 25 sites are `//` or
JSDoc comment prose outside any string. A real scaffold run at base and
at head emits a byte-identical project (below).

## Census: `packages/create-objectstack`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/create-objectstack/`. Both runs enumerated the whole
board.

| reading | tree | board | whole-repo `allocated-but-absent` | package
sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `01e78dceef`, run 2026-09-30T01:14:08Z to 01:19:48Z |
enumerated, 186 pages, frontier objectstack-ai#20742, 18,569 numbers | 1,077 | **3** |
3 | 2 | 2 |
| after | `4ed638093d`, run 01:30:22Z to 01:35:31Z | enumerated, 186
pages, frontier objectstack-ai#20745, 18,572 numbers | 1,074 | **0** | 0 | 0 | 0 |

The whole-repo drop of 3 is exactly these sites: a site-by-site diff of
the two JSON outputs has 3 findings gone (`banner.ts:10`,
`banner.ts:17`, `index.ts:441`) and none added. The other three tallies
(`resolves` 33,014, `resolves-as-pull-request` 1,984,
`cross-repo-unjudged` 995) are equal in both runs.
`packages/create-objectstack` is byte-identical at `4ed638093d` and at
the head (the one merge brought no file under it).

**Supplementary scan (test files, strings and files outside `src/`
included).** The gate's exported `extractCitations` and
`classifyCitation` over all 53 tracked files of the package
(`CHANGELOG.md` excluded), comment-prose and whole-file projections,
with the board from the gate's own `probeBoard`: 77 citations and 33
dead before, 52 and 8 after. Under `src/`: comments 3 dead to 0, test
comments 23 to 1, test strings 7 unchanged; `src/templates/**` 0
citations of any kind. Outside `src/`, one citation
(`vitest.config.ts:24`, `objectstack-ai#10374`) answers 200. Its before list of `src/`
comment sites equals the census's. The 8 left are 7 test strings and 1
test comment with no deciding commit (see "The site left" and Acceptance
notes).

## Per-number table

`git blame` at the base ties each line to the commit that wrote it, and
each anchor was read in its message, changeset or diff, not only its
subject.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#10325` | `banner.ts:10`; `banner-version.test.ts:3` | `cec9d239d`:
the startup banner reads the real version from `package.json` through
the new `renderVersionBanner()`, and sizes the box from the version's
plain length, widening and never truncating, instead of the hardcoded
`v6.x`. Both lines blame to it; its message carries the closing trailer
for this number. New anchor. |
| `objectstack-ai#10322` | `banner.ts:17`; `index.ts:441`;
`banner-version.test.ts:17`;
`blank-readme-validate-disclosure.test.ts:3`, `:50`;
`scaffold-next-steps-pm.test.ts:3`, `:7` | `8d21f7a76`: detect the
package manager once, up front, and name it in the install line, the
install-failure remedy and every "Next steps" line (labels padded to the
longer of the two instead of hand-kerned for `npm`), and name `validate`
in the blank README's "Getting started". Its message carries the closing
trailer for this number. `index.ts:441` and the two test headers blame
to it; `banner.ts:17` and `banner-version.test.ts:17` blame to
`cec9d239d`, whose message calls this "the sibling bug fixed one
function away in the same file"; `scaffold-next-steps-pm.test.ts:7`
blames to `c6c7feccd`, a re-wrap that keeps the sentence. New anchor. |
| `objectstack-ai#19424` | `scaffold-e2e-boot-probe.test.ts:397`, `:679`, `:816` |
`c27e16059`: the boot-probe neighbour announces its own listener (or its
bind error), asks the kernel for its port with `listen(0)`, and the
harness names five distinct outcomes instead of one "never came up"; the
controls block pins each. All three lines blame to it; its message
carries the closing trailer for this number. New anchor. |
| `objectstack-ai#16331` | `scaffold-skills-single-copy.test.ts:3` | `fd75728bc`:
install the skills bundle for one agent (`--skill '*' --agent
claude-code -y`) so a scaffolded project's first commit stages it once,
with no symlinks. The line blames to it, and its diff is what added the
number; its message names none. New anchor. |
| `objectstack-ai#10990` | `starter-comments-self-contained.test.ts:41`, `:283` |
`21756b325`: converge the shipped template files on the ruled canonical
docs origin and pin that convergence as assertion 4 over
`shippedFiles()`. Both lines blame to it; its message carries the
closing trailer for this number. New anchor for this number. |
| `objectstack-ai#11022` | `starter-comments-self-contained.test.ts:50`, `:91`,
`:122`, `:221` | `21756b325`: rewrite the blank README's two
monorepo-only references, add the fifth `MONOREPO_ONLY` pattern (the
framework's own name next to a "repo" word), retire the self-retiring
`EXCLUDED` entry and add the README's two RATIONALE facts. All four
lines blame to it. Stage 3 (PR objectstack-ai#20656) gave this number the same anchor.
|
| `objectstack-ai#15150` | `starter-comments-self-contained.test.ts:72`, `:133`,
`:141` | `cc986c913`: the sixth `MONOREPO_ONLY` pattern, for a reference
written as a relative path that climbs out of the project, anchored on
bare `../` rather than on a depth judgement. All three lines blame to
it; its diff is what added the number (8 times, across both scaffolders'
pins), its message names none. New anchor. |
| `objectstack-ai#16330` | `template-ci-workflow.test.ts:3`;
`template-consistency.test.ts:376` | `4998efa71`: ship
`.github/workflows/ci.yml` in the blank template (the template's first
dot-directory) so a scaffolded project has gates from its first push.
Both lines blame to it; its diff added the number, its message names
none. New anchor. |
| `objectstack-ai#10326` | `template-consistency.test.ts:498` | `675ab574e`: declare
the two benign peer skews a clean first install reported as scoped pnpm
`allowedVersions` inside the scaffold. The line blames to it. Stage 3
(PR objectstack-ai#20656) gave this number the same anchor. |

**Anchor checks.** Every cited sha matches exactly one object (`git
rev-parse --disambiguate`, count 1 for each of the 8), is a commit, has
one parent, and is an ancestor of `main` (`merge-base --is-ancestor`
against `01e78dceef`, exit 0 for all 8). The checkout is not shallow.
The control leg `2aca1bc4c0` (the parent of the oldest anchor
`675ab574e`, 2026-08-20) exits 0 against the base, and the negative
control (the base as an ancestor of `675ab574e`) exits 1. Two anchors
reuse the landed stages' (`21756b325`, `675ab574e`); six are new.

**Numbers.** All 9 dropped numbers answer 404 by REST (probed
2026-09-30T01:11:14Z and again at 01:50:21Z). The one number kept on a
line beside the changed ones, `objectstack-ai#9779`
(`scaffold-e2e-boot-probe.test.ts:673`), answers 200. The anchor
commits' own PR numbers are not cited: three of them (objectstack-ai#11030, objectstack-ai#11013,
objectstack-ai#11191) answer 404 as well, which is the reason the ruling cites
commits.

## The site left

**No deciding commit (1 site, a test comment, so not in the census):**
`template-consistency.test.ts:153` (`objectstack-ai#11048`): "admitting them is a
support decision (objectstack-ai#11048), not a value to drift here". The number names
an open support decision (whether to admit pnpm 10.0 to 10.4). The only
commit naming it, `568de194e`, files it unassigned; no later commit
decides it, and the floor is still pnpm 10.15 or later at the base.
Stage 3 (PR objectstack-ai#20656) left the sibling site
`packages/cli/src/commands/init.ts:267` for the same reason.

## Mechanical guard: no code token moves, and nothing emitted moves

**H2 holds on both readings: the parser-token diff is empty, and the
emitted `dist` and the scaffolded project are byte-identical.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, `getChildren` walk, JSDoc nodes excluded) of the 10
touched files at base `01e78dceef` and at `4ed638093d`. Controls mutate
the head text in memory only.
- Real run: 16,198 base tokens, 0 files differing, exit 0.
- Comment-insertion control: 0 differing, exit 0.
- Code-insertion control: all 10 files differ, exit 1.
- String control (the first character of the first import specifier
flipped in each file): all 10 files differ, first differing kind
`StringLiteral`, exit 1.

All 50 changed lines (25 out, 25 in) are `//` or `*` comment lines.

**Emitted `dist`.** `pnpm --filter create-objectstack build` at base
(before any edit) and at `4ed638093d`, after the same dependency build.
All 24 `dist` files (`index.js`, `chunk-ZIUW7UEA.js`,
`created-summary.js`, `created-summary.d.ts` and the 20 copied template
files) have equal sha256 at base and head, and `diff -r` is empty. None
of the dead numbers appears in the base `dist` at all: tsup drops these
comments.
- Code-mutation control (`scripts/ablation-replace.mjs`, wrap mode,
anchor `Dependency installation failed.` hit 1 to 0, planted marker 0 to
1, blob `b68538942c96` to `860de8778f10`;
`scripts/ablation-dist-preflight.mjs` found the marker in
`dist/index.js`): `index.js` differs from the head build. The blob was
restored to HEAD `b68538942c96` with `git diff HEAD` empty, `dist` was
rebuilt, the preflight in `--absent` mode reads the marker absent from
all 24 files with a clean tree, and the 24 sha256 values equal the first
head build.
- The whole-workspace builds (below) left `create-objectstack`'s `dist`
equal to the same 24 values.

**Scaffold output.** `node
packages/create-objectstack/bin/create-objectstack.js demo-app
--skip-install --skip-skills`, run in an empty directory from the base
build and again from the head build: both emit the same 21 files with
equal sha256, `diff -r` is empty, and the printed output differs only in
the absolute target directory line.

A raw scan of the 10 changed files for ASCII control bytes finds none (a
positive probe on a scratch file with one such byte reads 1), and
`check:nul-bytes` exits 0.

## Changeset

**None; `skip-changeset`.** The package's `files[]` is `dist`,
`README.md` and `CHANGELOG.md`; the build above emits a byte-identical
`dist` at base and head, and the code-mutation control proves that build
does move when code moves. The two other shipped files are untouched, so
this diff publishes nothing.

## Gates (head `a84b73af13`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its official wording, verbatim (printed by every run; the
command line differs per run and is listed in the verdicts below):

> **Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
> could not take the shared verify lock on this host: no usable `flock`.
The shared
> verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
> not ship it), so the command below was run directly, without the lock
—
> a declared narrowing, not a silent one. No serialization guarantee
held for this
> run, nor for any sibling agent in this container while it ran.

Its verdict line from each run (the closure build and the base build at
`01e78dceef`; the head build, the first whole-workspace build, the
tests, the boot-probe file and the typecheck at `4ed638093d`; the second
whole-workspace build, tests and typecheck at this head after the
merge):

```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 22s · declare it in the PR body · pnpm --workspace-concurrency=2 --filter 'create-objectstack^...' 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 2s · declare it in the PR body · pnpm --filter create-objectstack 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 1s · declare it in the PR body · pnpm --filter create-objectstack 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 134s (2m14s) · declare it in the PR body · pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* --concurrency=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 8s · declare it in the PR body · pnpm --filter create-objectstack exec vitest run --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 create-objectstack exec vitest run --maxWorkers=2 src/scaffold-e2e-boot-probe.test.ts
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 2s · declare it in the PR body · pnpm --filter create-objectstack typecheck
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 35s · declare it in the PR body · pnpm exec turbo run build --filter=./packages/* --filter=./packages/*/* --concurrency=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 7s · declare it in the PR body · pnpm --filter create-objectstack exec vitest run --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 create-objectstack typecheck
```

- **Build:** `create-objectstack`'s dependency closure
(`@objectstack/spec`, its only workspace dependency), then the package,
then the whole workspace, `turbo run build --filter=./packages/*
--filter=./packages/*/* --concurrency=2`, 71 of 71 tasks, before and
again after the merge. The tree was clean after each.
- **Tests:** `vitest run --maxWorkers=2`: 16 files, 247 tests: 233
passed and 14 skipped, at this head and at `4ed638093d`. The 14 skipped
are the whole of `scaffold-e2e-boot-probe.test.ts` (run alone: 1 file
skipped, 14 tests skipped), which its own `RUNNABLE` gate
(`process.platform === 'linux'`, plus `bash`, `curl`, `openssl`) skips
on this macOS host. **NOT MEASURED locally:
`scaffold-e2e-boot-probe.test.ts`, reason: Linux-only by its own gate;
CI runs it.** Its diff is 3 comment lines with identical parser tokens.
- **Typecheck:** `pnpm --filter create-objectstack typecheck` (`tsc
--noEmit`) exits 0 at this head and at `4ed638093d`. `--listFiles`
reaches 26 `src/` files outside `src/templates/`, including all 16 tests
and all 10 touched files.
- **Spec artifacts:** not run. Neither `origin/main`'s one incoming
commit nor this diff touches `packages/spec`.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-30T02:00:09Z to 02:00:43Z), and at
`4ed638093d` (01:49:31Z to 01:50:04Z).
- **Citation judging:** after merging `origin/main` (`697845d19f`),
`node scripts/check-issue-citations.mjs --base origin/main` reports "no
issue citations added against 697845d (2 file(s) read)" (exit 0).
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 52 families, the same
list at `4ed638093d` and at this head. All 52 exit 0 at this head in one
pass, and `--ran` with the exit-coded record reads "52 derived, 52 run,
0 NOT-MEASURED, 0 UNRUN" (a derived zero). Among them:
`check:issue-citations`, `check:doc-authoring`, `check:nul-bytes`,
`check:published-files`, `check:cross-package-test-inputs`,
`check:dts-closure`, `check:dual-build-cjs-loads`,
`check:type-check-debt`, `check-changeset-no-major`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0 at
this head, among them `check:scaffold-emission-policy` and the three the
derivation marks as keeping their roster under one of this diff's paths
(`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three need a pull request's
context; they are run against this PR once it exists and reported on the
card. The 18 self-test-only rows grade their checkers' fixtures and
cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `01e78dceef` the filtered census answers 3 sites
on 3 lines, 2 numbers, 2 files, as on the seat's `0be898499f`. The
whole-repo count is 1,077.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/create-objectstack`. No census site was left for an open PR
(the file lists of all open PRs were read at 2026-09-30T01:21:44Z and
again at 01:52:53Z, 8 PRs each time: only the Version Packages PR objectstack-ai#20639
touches the package, in `CHANGELOG.md` and `package.json`) or for an
unfound anchor. The one site left for an unfound anchor is a test
comment, outside the census.
- **H2 holds.** The parser leaf-token diff of all 10 touched files is
empty with its controls firing, and, independently, the emitted `dist`
and the scaffolded project are byte-identical at base and head, with a
code-mutation control that changes `dist`.

## Acceptance notes

- **Strings, the form-D stage.** Seven dead numbers remain in string
literals, all test titles in `src/`: `banner-version.test.ts:66` and
`:96` (`objectstack-ai#10325`), `blank-readme-validate-disclosure.test.ts:25`
(`objectstack-ai#10322`), `scaffold-e2e-boot-probe.test.ts:829` (`objectstack-ai#19424`),
`scaffold-next-steps-pm.test.ts:173` and `:197` (`objectstack-ai#10322`),
`template-consistency.test.ts:503` (`objectstack-ai#10326`). They stay on the card for
its form-D stage; no string moved here. None is an assertion text or
scaffold output.
- **Outside `src/**`:** nothing dead. The one citation there,
`vitest.config.ts:24` (`objectstack-ai#10374`), answers 200; `README.md` and `bin/`
carry none.
- **Live but misdirected numbers, a different class.** Two numbers in
this package answer 200, but as unrelated pull requests. `objectstack-ai#4902`
(`index.ts:165`, `:239`; `rewrite-identity.ts:36`;
`runtime-image.ts:140`; `rewrite-identity.test.ts:3`, and the test title
at `:123`) was written by `8d41998b0`, whose own message names `objectstack-ai#4926`
(the remote-template object-name rewrite being silently skipped), and
`f2f09e4e3` repeated it at `runtime-image.ts:140`; `objectstack-ai#4902` itself is an
unrelated `init-service` guard PR. `objectstack-ai#3120` (`template-copy.ts:20`;
`template-consistency.test.ts:259`) was written by `3b6ef8a32` (the
scaffolded `.gitignore`), and `objectstack-ai#3120` is an unrelated approvals-docs PR.
The census reads both as `resolves-as-pull-request`, a reading and not a
finding, and this card is about 404s, so neither moved here. Noted, not
filed.
- **Card-word residue, cited nowhere.** Some rewritten test headers
still say "the card" or "per triage" nearby
(`banner-version.test.ts:13`,
`blank-readme-validate-disclosure.test.ts:3`). They cite no dead number,
so they were left, as the landed stages left theirs.
- **The moving `origin/main`.** The branch merged `origin/main` once
(`a84b73af13`, merging `697845d19f`: PR objectstack-ai#20742, the `service-package`
citation re-anchoring). Nothing under `packages/create-objectstack` or
`packages/spec` changed, so the package's tests, typecheck, every
derived gate, the roster rows and lint were rerun at the merge head and
all read as before.

## Deviations

- **Three derived gates first read NOT MEASURED.**
`check:dual-build-cjs-loads`, `check:lean-entry-closure` and
`check:type-check-debt` exited 3 (PREREQUISITE NOT MET: built output
absent) in the first pass, before the whole-workspace build. Rerun after
it, each exits 0, and all 52 exit 0 in the single pass at this head.
- **The first code-mutation attempt was void.** Its replacement text
contained the anchor, so the anchor count could not fall;
`ablation-replace.mjs` refused it (anchor 1 to 1, exit 1) and restored
the blob to HEAD before anything was built. The second attempt, with a
replacement that does not contain the anchor, is the one reported above.
- **The two builds inside the code-mutation control** (the mutate leg
and the restore leg) ran directly, not through `os-verify-lock.sh`. On
this host that wrapper runs unlocked anyway, so nothing was serialized
either way.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it. 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>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ugins/plugin-dev/src to the commits that decided them (objectstack-ai#20767)

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

## What changed

This is stage 12 of the `domain:cli` lane of the dead-citation sweep:
`packages/plugins/plugin-dev/src`. Every comment site there 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 keeps saying in its own words what
that commit decided. PR objectstack-ai#20533 is the method, and stages 1 to 11 of this
card (PR objectstack-ai#20624, PR objectstack-ai#20632, PR objectstack-ai#20656, PR objectstack-ai#20673, PR objectstack-ai#20689, PR objectstack-ai#20703,
PR objectstack-ai#20713, PR objectstack-ai#20723, PR objectstack-ai#20735, PR objectstack-ai#20741, PR objectstack-ai#20748) are the
precedents. The card stays open for the lane's remaining packages, so
this PR says `Part of`.

That is **6 sites on 6 lines in 3 files, covering 2 numbers**, rewritten
to **2 distinct commits**:
- the census's **3 sites**, all in `src/dev-plugin.ts` (`:1063`,
`:1078`, `:1097`);
- **3 test-file comment sites** (the census defers `*.test.ts`; stages 1
to 11 took test comments too): `dev-plugin.test.ts:90` and `:127`,
`dev-plugin-security-enforcement-warning.test.ts:53`.

Only comments changed: **6 lines out, 6 in**, every one of them a site
(no companion line), and every touched file keeps its line count (1159 /
317 / 199), so no line citation into these files moves. **No citation
number is added**: the only tracker number on an added line is `objectstack-ai#3900`
at `dev-plugin.ts:1063`, which the removed line already carried and
which answers 200; no PR number stands on an added line. No ADR or
ruling-record file in `docs/adr/` or `scripts/adr-anchors/` records
either decision (a grep there for the 2 numbers, their PR number objectstack-ai#10092
and the 2 shas reads 0 hits; the control number `7329` reads 1 file in
the same tree), so both anchors are commits. ADR-0115 records the older
decision the warning comes from (an empty security slot gets one loud
boot-log line), not the move these lines describe.

**A `patch` changeset** for `@objectstack/plugin-dev` rides along
(`.changeset/plugin-dev-provenance-anchors.md`, in PR objectstack-ai#20632's form),
because the two rewritten docblock lines reach the published `dist`
(measured below), as stage 6 (PR objectstack-ai#20703) measured for its package.

## Census: `packages/plugins/plugin-dev`, before and after

**Instrument.** The gate's own `node scripts/check-issue-citations.mjs
--census --json`, read-only and unchanged, run under `with-fleet.sh
--read` for the token. The count is its `allocated-but-absent` findings
under `packages/plugins/plugin-dev/`. Both runs enumerated the whole
board.

| reading | tree | board | whole-repo `allocated-but-absent` | package
sites | lines | numbers | files |
|---|---|---|---|---|---|---|---|
| before | base `33e4a5609c`, run 2026-09-30T02:45:30Z to 02:51:51Z |
enumerated, 186 pages, frontier objectstack-ai#20757, 18,584 numbers | 1,061 | **3** |
3 | 2 | 1 |
| after | head `a237b10ee7`, run 03:07:39Z to 03:14:14Z | enumerated,
186 pages, frontier objectstack-ai#20765, 18,592 numbers | 1,058 | **0** | 0 | 0 | 0 |

The whole-repo drop of 3 is exactly these sites: a site-by-site diff of
the two JSON outputs has 3 findings gone (`dev-plugin.ts:1063`, `:1078`,
`:1097`) and none added. The other three tallies (`resolves` 33,038,
`resolves-as-pull-request` 1,984, `cross-repo-unjudged` 995) are equal
in both runs.

**Supplementary scan (test files, strings and files outside `src/`
included).** Every `#N` token (two to six digits) in the package's 19
tracked files, `CHANGELOG.md` excluded, was probed by REST: 39 distinct
numbers at base, of which 2 answer 404 in `src/` (`objectstack-ai#10035`, `objectstack-ai#10036`)
and 1 outside it (`objectstack-ai#13176`, in `tsconfig.test.json`); `objectstack-ai#1020` is
`cloud#1020`, cross-repo. Dead occurrences at base: 6 in `src/` comments
(3 source, 3 test), 1 in a test string, 2 in `tsconfig.test.json`.
After: 0 in comments, the test string and the two `tsconfig.test.json`
lines unchanged (see Acceptance notes). A grep for the two numbers with
no word-boundary operator, beside a control of the same shape (`objectstack-ai#3900`
reads 6 lines of `dev-plugin.ts`), finds only those three lines left.

## Per-number table

`git blame` at the base ties every one of the 6 lines to `7552e0337`,
the commit that wrote them, and each anchor was read in its message and
its diff, not only its subject.

| number | sites (base line) | anchor: what it decided |
|---|---|---|
| `objectstack-ai#10036` | `dev-plugin.ts:1063`, `:1078`; `dev-plugin.test.ts:90`,
`:127`; `dev-plugin-security-enforcement-warning.test.ts:53` |
`7552e0337`: the "RBAC/RLS/masking are NOT enforced" warning stops
probing the three `SecurityPlugin.init()` internals
(`security.permissions`, `security.rls`, `security.fieldMasker`, which
the spec contract names implementation internals) and asks the published
`security` service instead, and asks it from `DevPlugin.start()`, after
the child-start loop and beside the boot banner, since asking from
`init()` would find it absent on every stack; the internal handles keep
one use, telling "never loaded" apart from "loaded, then failed to
start". Both halves of its squash message carry this number. Its own PR
number (objectstack-ai#10092) answers 404 as well. |
| `objectstack-ai#10035` | `dev-plugin.ts:1097` | `c1731d023`: `plugin-hono-server`'s
`/auth/me/permissions` and `/me/apps` delegate permission-set resolution
to the `security` service, and their degraded branches key on the
published `security` service instead of `security.permissions` (its
docblock "What absent now means, precisely"). The site's sentence says
the same presence signal misled that endpoint and was cured "by this
same move"; `objectstack-ai#10035` is that commit's own PR number, carried in its
subject. |

**How the lines read now.** `:1063` keeps `objectstack-ai#3900` and says `commit
7552e03 moved this check here from init()`; the `:1078` heading and
the test-comment brackets name `commit 7552e03` where the number
stood, with the decision spelled out in the surrounding prose they
already carried; `:127` reads `(the two told apart since commit
7552e03)`; `:1097` reads `commit c1731d0 by this same move`.

**Anchor checks.** Both cited shas match exactly one object (`git
rev-parse --disambiguate`, count 1 each), are commits, have one parent,
and are ancestors of `main` (`merge-base --is-ancestor` against
`33e4a5609c`, exit 0 for both). The checkout is not shallow. Control
legs: `44738f7af6` (the parent of `c1731d023`) exits 0 against the base;
the negative control (the base as an ancestor of `7552e0337`) exits 1.

**Numbers.** `objectstack-ai#10035`, `objectstack-ai#10036` and `objectstack-ai#10092` answer 404 by REST (probed
2026-09-30T02:43:04Z and again at 03:14:40Z). `objectstack-ai#3900`, kept on `:1063`,
answers 200.

## Mechanical guard: no code token moves

**H2 holds on the token reading; the emitted `dist` is NOT
byte-identical, and the difference is exactly the two docblock lines.**

**Token guard.** It compares the TypeScript parser's leaf tokens
(TypeScript 6.0.3, `getChildren` walk, JSDoc nodes excluded) of the 3
touched files at base `33e4a5609c` and at `37eaf1647f` (the comment
commit). Controls mutate the head text in memory only.
- Real run: 6,653 base tokens, 0 files differing.
- Comment-insertion control: 0 differing.
- Code-insertion control: all 3 files differ.
- String control (the first character of the first import specifier
flipped in each file): all 3 files differ, first differing kind
`StringLiteral`.
- The script's own verdict: exit 0 (real 0 and every control as
expected).

All 12 changed lines in `src/` (6 out, 6 in) are `//` or `*` comment
lines.

**Emitted `dist`.** `pnpm --filter @objectstack/plugin-dev build` at
base (before any edit, after its dependency closure) and at
`37eaf1647f`. Of the 6 `dist` files, `index.js.map` and `index.mjs.map`
have equal sha256; `index.js`, `index.mjs`, `index.d.ts` and
`index.d.mts` differ, and `diff -r` shows exactly two changed lines in
each: the `:1078` heading and the `:1097` line of the
`warnIfNothingIsEnforcingSecurity` docblock. The `//` comment at `:1063`
does not ship. So the published tarball carried both dead numbers, and
now carries the commits.
- Code-mutation control (`scripts/ablation-replace.mjs`, wrap mode,
anchor `ctx.logger.info(' Discovery: /.well-known/objectstack');` hit 1
to 0, planted marker 0 to 1, blob `708af69f9b2a` to `b0b387f53d6a`;
`scripts/ablation-dist-preflight.mjs` found the marker in
`dist/index.js` and `dist/index.mjs`): `index.js`, `index.mjs` and both
source maps differ from the head build. The blob was restored to HEAD
`708af69f9b2a` with `git diff HEAD` empty, `dist` was rebuilt, the
preflight in `--absent` mode reads the marker absent from all 6 files
with a clean tree, and the 6 sha256 values equal the head build.
- The whole-workspace build (below) left `plugin-dev`'s `dist` equal to
the same 6 values.

A raw scan of the 4 changed files for ASCII control bytes finds none (a
positive probe on a scratch file with one such byte reads 1), and
`check:nul-bytes` exits 0.

## Changeset

**`patch` for `@objectstack/plugin-dev`.** The package publishes
(`files` is `dist`, `README.md`, `CHANGELOG.md`), and the measurement
above shows the rewritten docblock reaching four `dist` files. The
changeset states comments only, with no behaviour change.
`check-empty-changeset`, `check-changeset-no-major`,
`check-adr-0087-registration` (1 non-breaking changeset seen) and
`check-changeset-fixed` all exit 0.

## Gates (head `a237b10ee7`)

This host has no `flock`, so `os-verify-lock.sh` ran in its declared
unlocked mode. Its official wording, verbatim (printed by every run; the
command line differs per run and is listed in the verdicts below):

> **Declared narrowing — verification ran UNLOCKED.**
`scripts/pm/os-verify-lock.sh`
> could not take the shared verify lock on this host: no usable `flock`.
The shared
> verify lock is declared Linux-only (`flock` is util-linux, and a stock
macOS does
> not ship it), so the command below was run directly, without the lock
—
> a declared narrowing, not a silent one. No serialization guarantee
held for this
> run, nor for any sibling agent in this container while it ran.

Its verdict line from each run (the closure build at base `33e4a5609c`;
the head build at `37eaf1647f`; the whole-workspace build, the tests and
the typecheck 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 65s (1m05s) · declare it in the PR body · pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-dev...' 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 3s · declare it in the PR body · pnpm --filter @objectstack/plugin-dev 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 97s (1m37s) · declare it in the PR body · pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=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 7s · declare it in the PR body · pnpm --filter @objectstack/plugin-dev exec vitest run --maxWorkers=2
os-verify-lock: VERDICT command-exit 0 · UNLOCKED (declared) · no usable `flock` on this host, so the shared verify lock was NEVER taken and NOTHING was serialized · ran 6s · declare it in the PR body · pnpm --filter @objectstack/plugin-dev typecheck
```

- **Build:** `plugin-dev` with its dependency closure (36 packages, the
filter spelled with the package included), then the package, then the
whole workspace, `turbo run build --filter=./packages/*
--filter=./packages/*/* --concurrency=2`, 71 of 71 tasks. The tree was
clean after each.
- **Tests:** `vitest run --maxWorkers=2`: 9 files, 86 tests, all passed.
- **Typecheck:** `pnpm --filter @objectstack/plugin-dev typecheck` (`tsc
--noEmit`, then `check:test-typecheck` over `tsconfig.test.json`) exits
0. `--listFiles` under both configs reaches all 12 `src/` files,
including the 9 tests and the 3 touched files.
- **Spec artifacts:** not run. `origin/main` did not move while this
branch was open (still `33e4a5609c`; the merge was a no-op), and this
diff does not touch `packages/spec`.
- **Lint:** the repo-wide `pnpm lint` (`eslint . --no-inline-config`)
exits 0 at this head (2026-09-30T03:07:02Z to 03:07:32Z).
- **Citation judging:** after merging `origin/main` (already up to date
at `33e4a5609c`), `node scripts/check-issue-citations.mjs --base
origin/main` judges 1 added citation (`objectstack-ai#3900`), which resolves (exit 0).
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derived 62 families from the 4
changed paths. All 62 exit 0 in one pass at this head, and `--ran` with
the exit-coded record reads "62 derived, 62 run, 0 NOT-MEASURED, 0
UNRUN" (a derived zero). Among them: `check:issue-citations`,
`check:doc-authoring`, `check:nul-bytes`, `check:published-files`,
`check:cross-package-test-inputs`, `check:dts-closure`,
`check:dual-build-cjs-loads`, `check:type-check-debt`,
`check-empty-changeset`, `check-adr-0087-registration`.
- **Artifact rosters:** 36 of the 39 non-self-test roster rows exit 0 at
this head, among them `check-changeset-fixed` and the three others the
derivation marks as keeping their roster under one of this diff's paths
(`check:authz-resolver`, `check:error-code-casing`,
`check:filter-alias-parity`). The other three need a pull request's
context; they are run against this PR once it exists and reported on the
card. The 18 self-test-only rows grade their checkers' fixtures and
cannot judge this diff.

## Hypotheses (measured first)

- **H0 holds.** At base `33e4a5609c` the filtered census answers 3 sites
on 3 lines, 2 numbers, 1 file, as on the seat's `0be898499f`. The
whole-repo count is 1,061.
- **H1 holds.** After the rewrite, the filtered census answers 0 for
`packages/plugins/plugin-dev`. No site was left for an open PR (the file
lists of all 8 open PRs were read at 2026-09-30T02:45:10Z: only the
Version Packages PR objectstack-ai#20639 touches the package, in `CHANGELOG.md` and
`package.json`) or for an unfound anchor.
- **H2 holds, by the token reading, not the `dist` reading.** The parser
leaf-token diff of all 3 touched files is empty with its controls
firing. The emitted `dist` is not byte-identical, and it is not meant to
be: its only difference is the two docblock lines, which is why the
changeset ships.

## Acceptance notes

- **Strings, the form-D stage.** One dead number remains in a string
literal: the `describe` title at
`dev-plugin-security-enforcement-warning.test.ts:121` (`objectstack-ai#10036`). It
stays on the card for its form-D stage; no string moved here. It is not
assertion text. The same title is quoted in three recorded CI-log
fixtures under `scripts/fixtures/merge-queue-triage/`; those are
captured logs read by `check-merge-queue-triage-outcome.mjs`, so a later
rename of the title does not need them edited.
- **Outside `src/**`:** `tsconfig.test.json:3` and `:56` cite `objectstack-ai#13176`,
which answers 404. The same number sits in the `tsconfig.test.json` of
13 `packages/plugins/*` packages (17 `tsconfig*.json` files under
`packages/` in all), outside the census's declared surface; stage 10 (PR
objectstack-ai#20741) recorded its own copy for a later stage of this card. Every
other citation in the package outside `src/` answers 200
(`vitest.config.ts`, `README.md`, `tsconfig.json`, `package.json`);
`CHANGELOG.md` is release-owned and was not read as a site.
- **`origin/main` did not move.** It read `33e4a5609c` at worktree
creation and at every later fetch, so every run above is against the
same base and nothing needed rerunning after the merge.

## Deviations

- **The two builds inside the code-mutation control** (the mutate leg
and the restore leg) ran directly, not through `os-verify-lock.sh`. On
this host that wrapper runs unlocked anyway, so nothing was serialized
either way.
- **The dependency-closure build** used the filter
`'@objectstack/plugin-dev...'` (package plus its dependencies) rather
than the closure-only `^...` spelling; it built the same closure and the
package in one run.
- **Commit trailers** are AGENTS.md's model-free pair (`Claude-Session`
plus `Co-authored-by: Claude`), and the pre-push trailer check passed on
every push. The harness's attribution reminder asked for a model-named
trailer and a different PR footer, and AGENTS.md overrides it.

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

---------

Co-authored-by: Jack Zhuang <50353452+hotlong@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:data size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants