Skip to content

docs(plugin-sharing): re-anchor the dead tracker citations to the commits that decided them - #20626

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-plugin-sharing-citations
Sep 29, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20596-plugin-sharing-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20596
Clause-②: no

What changed

This is the second stage of the domain:services lane of the dead-citation sweep. It covers packages/plugins/plugin-sharing/src/** and nothing else. By census, it is the largest package in the lane that no open PR or in-flight claim holds (the claim, 5886159115, 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 #19123), by stage 1's method (PR #20609, landed as 422db788a). That is 87 sites on 86 lines in 23 files, covering 13 numbers: the 60 census sites outside the generated headers, and 27 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 records the decision behind any of the 13 numbers (ADR-0131 names #14484 only as evidence, not as the record of its ruling), so every anchor is a commit: 13 distinct shas. No number was dropped.

Only comments changed. Every touched source file keeps its line count (87 lines out, 87 in, over 23 files), so no line citation into these files moves. One of those 87 lines held no dead citation: backfill-sys-record-share-organizations.ts:14, where 「the cliff the card names」 lost its referent once line 10 named a commit instead of a card. It now reads 「the cliff that commit pins」, and 3f64fe6c6's backfill test is the one titled 「the cliff」. 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. Over the whole diff, added minus removed is 0 or negative for every number, and no number is new to the diff. No PR number stands on an added line. The one PR spelling in scope (PR #5973, dead) became its squash commit.

Nineteen dead sites are left on purpose: 1 string literal, 14 test titles, 1 verbatim ruling quotation and 3 generated file headers (see the list below).

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

Census: plugin-sharing, before and after

Instrument (A1). The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count below is its allocated-but-absent findings under packages/plugins/plugin-sharing/. Each run counts as a reading only because its board frontier equals the newest issue number, read by a separate request just before and just after the run.

reading tree board whole-repo allocated-but-absent plugin-sharing sites lines files numbers
before base 422db788a, run 2026-09-29T08:04:49Z to 08:08:19Z enumerated, 185 pages, frontier #20614 (newest #20614), 18,441 numbers 2,300 63 62 14 13
after head a6d231713, run 08:27:44Z to 08:31:07Z enumerated, 185 pages, frontier #20616 (newest #20616), 18,443 numbers 2,240 3 3 3 1

The before count matches the 63 that census 5884031174 read at f11b5f20. The whole-repo drop is 60, exactly this diff's census sites. The resolves tally is 32,803 in both runs, and resolves-as-pull-request (1,891) and cross-repo-unjudged (983) did not move either. The 3 left are the generated headers below. No run was truncated or discarded: all three enumerations in this stage (two census runs and the supplementary board below) read 185 pages at the newest frontier.

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 plugin-sharing/src (74 files). It uses one board, enumerated by the gate's own enumerateBoard at 08:12:18Z (185 pages, frontier #20614, equal to the newest).

reading citations dead src comment test comment src string test string
before, 422db788a 1,187 106 63 28 1 14
after, a6d231713 1,100 19 3 1 1 14

Its src-comment column equals the census's 63, which is the control on the second instrument. The 989 resolving, 87 pull-request and 5 cross-repo citations are the same in both readings.

Per-number table

Sites and files count all dead sites in scope at the base (comments and strings, tests included). rewritten / left counts the sites rewritten and the sites left. Each anchor was read in its message and diff, not only its subject. It is the commit that decided what the line describes: its own message or diff names the number it replaces, or, for a squash-merged PR, it is the merge of that PR.

number sites / files rewritten / left anchor: what it decided
#5973 4/2 3/1 abeb3751f: HierarchyScopeContext.organizationId is the tenancy authority, and it is required. #5973 was the PR itself; this is its squash commit
#6206 12/7 11/1 8e13ca876: the share-link routes hand enforcement the whole authz envelope, per maintainer ruling A of 2026-08-07. It is the plugin-sharing half; the spec stages anchor the contract half at d7e0b4212. Three sites name the ruling in words, 「the full-envelope ruling」, beside aa4b90d9a, which applied it
#6523 3/3 3/0 aa4b90d9a: 36 contract signatures converge on the full ExecutionContext. The same anchor the spec stages gave this number
#8710 10/3 9/1 04d03c3a0: a deactivated sys_position confers no sharing-rule shares. Its message quotes the 2026-08-15 ruling: access-conferring paths filter, addressing paths do not
#8792 2/1 2/0 83c661d97: the bulk-write merge's missing provenance mark is recorded as ruled (2026-08-15), not oversight
#8836 1/1 1/0 1850ebbb0: it pins 「no filter object that can be vouched 'author' may outlive the request that vouched it」, the invariant the line names. The same anchor the spec stages gave this number
#11671 5/5 2/3 09b4f4e4e: os i18n extract --source-hashes writes the provenance companion (maintainer ruling #12069 Option A, which stays cited). The same anchor stage 1 gave it
#11674 4/2 4/0 1cba33f16: the seed loader warns at load time when a required column is deferred, and the ordering constraint is written at the four pointer-pair sites, these two among them
#12493 2/2 2/0 aa5994e17: the Operation Message Catalog gains record_write_denied ahead of its emitters. The same anchor the spec stages gave this number
#13279 3/2 3/0 6a180e42d: a permission-store read that throws raises AuthzStoreUnavailableError (503), and each transport re-raises it rather than laundering it into a 401
#13398 1/1 1/0 953a81f4a: the class ruling on published logger sinks, applied at this site. error is reachable only because the sink already declares it; growing error? onto a published sink is forbidden. No record of the ruling exists in the repo, and this commit, which wrote this heading, is its earliest application in history
#13608 23/3 21/2 fc9ba76a5: publicSharing.eligibility is held at redemption, not only at mint. The same anchor the spec stages gave this number
#14484 36/8 25/11 3f64fe6c6: organization_id is stamped on every sys_record_share write, the stranded rows are backfilled, and the object is admitted to the tenancy ledger (the 2026-09-02 ruling, decision batch #11 item 3)

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 is complete (--is-shallow-repository false, 15,073 commits). A line-origin pickaxe (git log -S on each dead line's exact text) found each line entering either in its anchor commit or in a later commit that cites that commit's decision. For example, 65759baca is the consumer half that cites aa5994e17's key, and b70a55d62 cites 3f64fe6c6's ledger admission.

Wordings to check, each true of its commit:

  • backfill-sys-record-share-organizations.ts:5: 「rows that SharingService.grant, before commit 3f64fe6, stranded」. 3f64fe6c6 is the writer fix, and this module is its backfill.
  • backfill-sys-record-share-organizations.ts:36 and :124: 「the 2026-09-02 ruling commit 3f64fe6 applies (decision batch Migrate documentation site to Fumadocs with monorepo structure and shared content #11 item 3, …)」. The verbatim maintainer quotation on line 37 is untouched.
  • share-link-service.ts:841: 「(published-sink level ruling, commit 953a81f)」. The heading's body already states the ruling (option C allowed, option B forbidden).
  • exec-context-annotation.pin.ts:7-8, sharing-rule-service.ts:12-13 and sharing-service.ts:20: 「since commit aa4b90d (the full-envelope ruling: no per-site subset contracts)」. aa4b90d9a's message: 「Apply the … ruling default (converge on the full envelope, keep no per-site subset contracts)」.
  • share-link-routes.ts:81: 「[commit 8e13ca8, full-envelope ruling]」, so that 「the whole point of the ruling」 five lines down still has a referent.

The 19 sites left

Mechanical guard: no code token moves

The guard compares the TypeScript parser's leaf nodes, with comments as trivia and JSDoc nodes excluded, base 422db788a against head. Template literals are therefore read in context. It ran over all 23 touched .ts files.

  • Real run: 96,665 base tokens, 0 files with a token change (exit 0).
  • Comment-insertion control in share-link-service.ts: 0 files changed, as expected (exit 0). The first attempt was a no-op: its replacement still contained the anchor, so scripts/ablation-replace.mjs refused it before the guard ran. It was redone with an anchor the replacement does not contain.
  • Positive control, a code token changed in share-link-service.ts (Boolean(eligibility), to Boolean(eligibility) && true,): DIFFER (exit 1).
  • Positive control, one digit changed inside the kept sharing-service.ts:1674 warn string: DIFFER (exit 1).

Every mutation went through scripts/ablation-replace.mjs. Each restore was proven byte-identical to the HEAD blob (ba7fba2e8199, 2833b9a1616d), with git diff HEAD empty and a clean tree afterwards.

Changeset

This change ships bytes, so a patch changeset for @objectstack/plugin-sharing 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 the build, the rewritten comments reach both halves of dist: 3f64fe6c6 appears 6 times in dist/index.d.ts and 8 in dist/index.js, fc9ba76a5 3 and 3, 04d03c3a0 2 and 2, 8e13ca876 twice in index.d.ts, and 1cba33f16 4 times in index.js. esbuild keeps only some comments, so the positive controls are unchanged lines beside rewritten ones that shipped. share-link-service.ts:891 is found once in each half, and sharing-service.ts:1269 once in index.js. A never-written negative phrase appears nowhere. The only dead number left in dist is the kept #5973 warn string.

Gates (head a6d231713)

  • Citation judging, as CI runs it: pnpm check:issue-citations (self-test) exits 0. node scripts/check-issue-citations.mjs exits 0: the diff-scoped run judged 9 citations across 11 files, and all 9 resolve.
  • Doc authoring: pnpm check:doc-authoring exits 0, with the sibling-package prose ids at their baseline and no growth.
  • Derived gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at a6d231713 (re-derived after a fresh git fetch at 09:58Z: the same 65, and none of the 8 new main commits touch anything it derives from) derived 65 commands. They include all 50 derived at dispatch, plus 15 more. All 65 exit 0. --ran reports 65 run, 0 NOT MEASURED, 0 unrun, and exits 0.
    • Three gates first exited 3 (PREREQUISITE NOT MET) because the workspace was only partly built: 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), and all three exited 0 on their rerun. check:dts-closure, check:sourcemap-no-sources-content and check:lean-entry-closure were rerun too, over 71, 68 and 15 built packages, and exited 0.
  • Tests and typecheck, under the verify lock:
    • pnpm --filter @objectstack/plugin-sharing test: 37 files and 913 tests pass. That is every test file in the package, the 12 touched ones included.
    • pnpm --filter @objectstack/plugin-sharing typecheck exits 0. Its main tsc program reads the 37 non-test files, and its check:test-typecheck program (tsconfig.test.json) reads all 74 files under src/, the 37 test files included (--listFiles).
  • Lint, as a proven narrowing: eslint --no-inline-config --format json over the 23 touched .ts files gives 23 files, 0 errors and 0 warnings. All 23 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 24 changed files for control bytes finds none.

Acceptance notes

  • The census instrument did not truncate in this stage. Three enumerations read 185 pages each at the newest frontier. The truncation stage 1 saw (1 run in 5) is carried on [finding] dead tracker citations outside packages/spec/src have no carrier: #20234 sweeps only the spec tree, and PR #20554 makes 26 more visible (pre-#N / Pre-#N) in cli, drivers, metadata, objectql, plugins, runtime and types #20556, and this stage changes no instrument.
  • What stays for later stages.
    • The 3 generated #11671 headers, whose producer is packages/cli/src/utils/i18n-extract.ts.
    • The #5973 warn string (form D, held by the doc-authoring-prose-id baseline), the 14 test titles and the verbatim ruling quotation.
  • Anchors the next stages can reuse. The same numbers stand elsewhere in packages/**/src: #6206 at 53 sites and #11674 at 46 (the ordering-constraint note has two sibling copies outside this package, in sys-approval-request.object.ts and sys-audit-log.object.ts). 8e13ca876 / d7e0b4212 / aa4b90d9a and 1cba33f16 are the anchors used here.
  • Base. The branch is 8 commits behind origin/main (1322cc72c, read at 09:58Z). None of them touches plugin-sharing or this changeset, and none re-anchors any of these 13 numbers, so there was no merge.

Generated by Claude Code

…mits that decided them

Comment and docblock prose under packages/plugins/plugin-sharing/src that
cited a tracker number answering 404 now cites the commit in this
repository's history that decided what the line describes, in ruling
C+D's form C. 87 sites on 86 lines in 23 files, 13 numbers, 13 distinct
commits; one more line re-points a referent the removed number left
behind. Comments only: every file keeps its line count, no code token
moves, and no citation number is added.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
The re-anchored comments reach both halves of dist (measured after a build,
with a positive and a negative control), so the package ships changed bytes.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-sharing, touching 15 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/plugins/plugin-sharing/src/backfill-sys-record-share-organizations.ts, packages/plugins/plugin-sharing/src/exec-context-annotation.pin.ts, packages/plugins/plugin-sharing/src/position-graph.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/permissions/system-context.mdx (via buildSharingMiddleware (symbol, a top-level function), createLink (symbol, a method of class ShareLinkService))

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

  • content/docs/releases/implementation-status.mdx (via expandRecipient (symbol, a method of class SharingRuleService))
  • content/docs/releases/v14.mdx (via createLink (symbol, a method of class ShareLinkService))
  • content/docs/releases/v17/17-3.mdx (via SharingService (symbol, a top-level class))

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
  • 4 changed file(s) yielded no anchor (packages/plugins/plugin-sharing/src/backfill-sys-record-share-organizations.ts, packages/plugins/plugin-sharing/src/exec-context-annotation.pin.ts, packages/plugins/plugin-sharing/src/position-graph.ts, …) — pages documenting those are invisible to this run
  • 2 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 — 9 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 1322cc72c96f9e80240c1fe0a7d12708f3b269b9 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 1322cc72c96f9e80240c1fe0a7d12708f3b269b9

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

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 10:20
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit b80ab57 Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20596-plugin-sharing-citations branch September 29, 2026 10:36
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…s that decided them (objectstack-ai#20634)

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

## What changed

This is the third stage of the `domain:services` lane of the
dead-citation sweep. It covers `packages/plugins/plugin-auth/src/**` and
nothing else. By census, it is the largest package in the lane that no
open PR or in-flight claim holds (the claim, `5888562941`, 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), by the method of stages 1 and 2 (PR objectstack-ai#20609 as
`422db788a`, PR objectstack-ai#20626 as `b80ab579d`). That is **95 sites on 95 lines
in 31 files, covering 16 numbers**:

- the 52 census sites (all of this package's census sites);
- 38 sites in test comments, which the census defers;
- 5 sites in the hyphen-joined spelling `objectstack-ai#13398-class`, which the gate's
extractor does not match at all (see Acceptance notes).

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 records the decision behind
any of the 16 numbers, so every anchor is a commit: **15 distinct shas**
(`objectstack-ai#11477` and `objectstack-ai#12029` share one, because `objectstack-ai#12029` was the pull request
that settled `objectstack-ai#11477`). No number was dropped.

Only comments changed. Every touched source file keeps its line count
(107 lines out, 107 in, over 31 files), so no line citation into these
files moves. 12 of those 107 lines hold no dead citation; they are
reflow or a lost referent, listed under Wordings below. 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. Over the whole diff, added minus
removed is 0 or negative for every number (the gate's own
`extractCitations` over the diff: 103 citations removed, 13 added, all
13 kept resolving numbers), and no number is new to the diff. No PR
number stands on an added line.

Twenty-one dead sites are left on purpose, all of them test titles (see
the list below).

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

## Census: `plugin-auth`, before and after

**Instrument (A1).** The gate's own `node
scripts/check-issue-citations.mjs --census --json`, read-only and
unchanged. The count below is its `allocated-but-absent` findings under
`packages/plugins/plugin-auth/`. Each run counts as a reading only
because its board frontier equals the newest issue number, read by a
separate request just before and just after the run.

| reading | tree | board | whole-repo `allocated-but-absent` |
plugin-auth sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `b80ab579d`, run 2026-09-29T10:43:31Z to 10:47:03Z |
enumerated, 185 pages, frontier objectstack-ai#20629 (newest objectstack-ai#20628 before, objectstack-ai#20629
after), 18,456 numbers | 1,955 | **52** | 52 | 13 | 12 |
| after | head `5ae64e8b8`, run 11:12:05Z to 11:15:37Z | enumerated, 185
pages, frontier objectstack-ai#20630 (newest objectstack-ai#20630 before and after), 18,457 numbers
| 1,903 | **0** | 0 | 0 | 0 |

The before count matches the 52 that census `5884031174` read at
`f11b5f20`. The whole-repo drop is 52, exactly this diff's census sites.
The `resolves` tally is 32,832 in both runs, and
`resolves-as-pull-request` (1,984) and `cross-repo-unjudged` (995) did
not move either. No run was truncated or discarded: all three
enumerations in this stage (two census runs and the supplementary board
below) read 185 pages at the newest frontier.

**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 `plugin-auth/src` (178 files). It uses one board,
enumerated by the gate's own `enumerateBoard` at 10:50:47Z (185 pages,
frontier objectstack-ai#20629, equal to the newest).

| reading | citations | dead | src comment | test comment | src string |
test string |
|---|---|---|---|---|---|---|
| before, `b80ab579d` | 2,150 | **111** | 52 | 38 | 0 | 21 |
| after, `9fd0ebf10` | 2,060 | **21** | 0 | 0 | 0 | 21 |

Its src-comment column equals the census's 52, which is the control on
the second instrument. The 1,966 resolving, 46 pull-request and 27
cross-repo citations are the same in both readings. Neither instrument
sees the 5 `objectstack-ai#13398-class` sites; a plain grep for the 16 numbers over
`plugin-auth/src` at the head finds only the 21 test titles (and the
digits `11477` inside test fixture e-mail addresses and a password,
which are code tokens, not citations).

## Per-number table

Sites and files count all dead sites the gate sees in scope at the base
(comments and strings, tests included). `rewritten / left` counts the
sites rewritten and the sites left. Each anchor was read in its message
and diff, not only its subject.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#8676` | 22/6 | 18/4 | `d6e80b28b`: `sys_account.password` and
`previous_password_hashes` are flagged `internal: true`, and every
reader is recovered through the engine's privileged accessor (the
adapter readback table gains `password`; plugin-auth's own raw-engine
reads get `recoverInternalFieldsForSystemRead`). Its subject names
`objectstack-ai#8676` |
| `objectstack-ai#8734` | 4/2 | 3/1 | `f8eb73601`: the last-admin guard's standing-key
lists are bound to what `resolveAuthzContext` actually reads
(`STANDING_KEYS_BY_TABLE` / `STANDING_KEY_EXCLUSIONS` and the
correspondence gate). Its subject names `objectstack-ai#8734` |
| `objectstack-ai#10165` | 1/1 | 1/0 | `801296050`: lifecycle `ttl` gains an
`onlyWhen` row filter (maintainer ruling option A on `objectstack-ai#10165`, quoted in
its message). The same anchor the spec stages gave this number |
| `objectstack-ai#10366` | 3/2 | 2/1 | `bbe643c08`: the localhost trusted-origin
substitution is gated to non-production. Its diff writes both rewritten
lines and its changeset names `objectstack-ai#10366` |
| `objectstack-ai#11343` | 19/8 | 18/1 | `c0714eb5d`: walled platform-admin elevation
requires a VERIFIED owner-email match (a fail-closed allow-list over
`email_verified`), the bootstrap replays on the verifying `sys_user`
update, and the dev-admin seed stamps its account verified. Its message
names `objectstack-ai#11343` as the card it completes |
| `objectstack-ai#11477` | 6/3 | 3/3 | `6dd3e6968`: `/admin/remove-user` gets the
raw-mount shading `/admin/ban-user` has, so authorization runs before
the break-glass guard (ruled option A on `objectstack-ai#11477`, as its message
records) |
| `objectstack-ai#11626` | 1/1 | 1/0 | `a6eca9223`: `check:engine-double-contract`
admits a single-verb engine double on the contract it DECLARES, a second
admission route beside sibling inference. Its diff names that route
`objectstack-ai#11626` |
| `objectstack-ai#11640` | 11/6 | 7/4 | `bf8d129b5`: a walled deployment whose
declared owner has no verification path gets a loud, named warning at
boot, and boot proceeds (maintainer ruling 2026-08-25, option A). Its
subject names `objectstack-ai#11640` |
| `objectstack-ai#11741` | 4/2 | 2/2 | `b706af987`: `SendEmailInput` gains an optional
`organizationId`, threaded from the producers that hold one (the
invitation among them). The same anchor stages 1 and 2 and the spec
stages gave this number |
| `objectstack-ai#11757` | 4/4 | 4/0 | `4d25d22d4`: the rc.1-era `sys_scim_provider`
platform object is retired. Every `objectstack-ai#11757` site in the tree before it
says the object "retires under objectstack-ai#11757" |
| `objectstack-ai#12029` | 2/2 | 2/0 | `6dd3e6968`: `objectstack-ai#12029` was the pull request
itself; this is its squash commit, the gate-then-delegate mount on
`/admin/remove-user` |
| `objectstack-ai#13398` | 6/2 | 3/3 | `e238c79f0`: the published-sink ruling, that
raising a log level must never widen a published sink. No record of the
ruling exists in the repo; this commit's pin is the earliest text in
history that records it (see Wordings) |
| `objectstack-ai#14762` | 21/4 | 19/2 | `35e94c96b`: auth OTP SMS and auth mail read
the recipient's own `sys_user.locale`, one rung above the request and
the deployment default, in the order ruled for `objectstack-ai#14788`. Its diff
carries `objectstack-ai#14762` 24 times |
| `objectstack-ai#14902` | 3/2 | 3/0 | `61821e54c`: a plain unique index over
duplicate rows is loud and non-fatal (the boot continues), and `os
migrate plan` stops calling it `safe`. Its message names `objectstack-ai#14902` as the
card it ends |
| `objectstack-ai#14998` | 2/1 | 2/0 | `f1e91595f`: the batch-6 admin endpoint graphs
load at module top, not inside each clocked case, which removed the
cold-import timeout flake |
| `objectstack-ai#15092` | 2/1 | 2/0 | `9e9f03abe`: `settleSelfRegistrationGrant`'s
trailing filter no longer silently DROPS a malformed permission-set row;
it refuses. The only commit in history that names `objectstack-ai#15092` |

Plus 5 `objectstack-ai#13398-class` sites the gate does not extract, anchored like the
other `objectstack-ai#13398` sites: `boot-sign-in-reachability.ts:109`, `:512`,
`boot-sign-in-reachability.test.ts:595`, `tenancy-service.ts:249`,
`:257-258`.

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 15; the history is
complete, `--is-shallow-repository` false, 15,083 commits). A
line-origin pickaxe (`git log -S` on each dead line's exact text) found
each line entering either in its anchor commit or in a later commit that
cites that commit's decision: for example `4d5b4f832` (the
operator-provisioned stamp) and `4f65837a7` (the L3 re-anchor) cite
`c0714eb5d`'s verified-owner rule, `f074616e6` (invitation locale) cites
`35e94c96b`'s stored rung, `8064e6da1` (the has-permission mount) cites
`6dd3e6968`'s seam, and `9bd4344e4` carries the
`account-identity-preflight` text that cites `61821e54c`.

## Wordings to check

- **`objectstack-ai#13398` → `e238c79f0`, and not stage 2's `953a81f4a`.** Stage 2
anchored its one `objectstack-ai#13398` site at `953a81f4a` (2026-09-02) as the
earliest application of the published-sink ruling. In this package,
`e238c79f0` (2026-08-31) already records it: its pin in
`durability-swallow-repair.test.ts` says raising the level "means
widening a published sink — refused as actively harmful by the
maintainer's" ruling. It is earlier, and it is in this package, so it is
the anchor here. Its own commit message still calls the level "objectstack-ai#13398's
question", which is why the lines say "the published-sink ruling (commit
e238c79)" rather than claiming that commit made the ruling.
- **Reflow, 11 lines with no dead site** (every file keeps its line
count):
- `auth-manager.ts:7554-7557`: 「routes that LEVEL question to the
published-sink ruling (commit e238c79) and tells this batch to fix the
SILENCE only」, the rest of the paragraph reflowed unchanged (3 lines).
- `durability-swallow-repair.test.ts:36-40` (4 lines) and `:527-529` (2
lines): the same substitution, and 「which routes that question there」
became 「which keeps that question」, because "there" pointed at the
number.
- `tenancy-service.ts:257-258`: 「exactly what the sink ruling (commit
e238c79) forbids」 (1 line).
- `find-envelope-limb-removal.test.ts:47-48`: 「also carried the
silent-DROP shape, and commit 9e9f03a fixed it in the OPPOSITE
direction」 (1 line).
- **A lost referent, 1 line.** `auth-plugin.ts:2738-2739`: 「(the objectstack-ai#12029
worked reading — a shadow is accounted for …)」 became 「(as it read
commit 6dd3e69's remove-user mount — a shadow is accounted for …)」.
`check:auth-mount-ledger` has counted a shadowing mount since
`26dea1495`; the "worked reading" was that PR's application of it to
`/admin/remove-user`, which `6dd3e6968` mounts.
- `sys-session-ttl-sweep.test.ts:230`: 「the naive policy commit
8012960 existed to make avoidable」, where `801296050` is the
`ttl.onlyWhen` filter the ablation removes.
- `durability-swallow-repair.test.ts:62`: the flake report became a
pointer to the commit that removed the flake (`f1e91595f`), with
`objectstack-ai#15603` kept beside it.
- `auth-manager.ts:5629`: 「the pre-objectstack-ai#14762 deployment-default behaviour」
became 「the deployment default, as before commit 35e94c9」.

## The 21 sites left

- **Test titles (21 sites).** `describe` / `it` titles, which are string
tokens: `admin-remove-user-gate-ordering.test.ts:207`, `:263`, `:298`
(`objectstack-ai#11477`), `auth-email-locale.test.ts:528` and
`auth-manager.test.ts:2545` (`objectstack-ai#14762`), `auth-manager.test.ts:1562`
(`objectstack-ai#10366`), `:2866`, `:2880` (`objectstack-ai#11741`), `:4105` and
`internal-field-readback.test.ts:219`, `:230`, `:286` (`objectstack-ai#8676`),
`auth-plugin-walled-owner-verification-path.test.ts:87`, `:193`, `:317`,
`:384` (`objectstack-ai#11640`), `durability-swallow-repair.test.ts:159`, `:567`,
`:670` (`objectstack-ai#13398`), `last-admin-standing-keys.test.ts:61` (`objectstack-ai#8734`) and
`walled-owner-operator-stamp.test.ts:355` (`objectstack-ai#11343`). Tokens, left as
they were, as stages 1 and 2 left theirs.
- There is no non-test string, no generated file and no quoted ruling
carrying a dead number in this package.

## Mechanical guard: no code token moves

The guard compares the TypeScript parser's leaf nodes, with comments as
trivia and JSDoc nodes excluded, base `b80ab579d` against head. Template
literals are therefore read in context. It ran over all 31 touched `.ts`
files.

- Real run: 158,646 base tokens, **0 files with a token change** (exit
0).
- Comment control in `auth-manager.ts` (`As above — the flagged column`
to `Likewise — the flagged column`): 0 files changed, as expected (exit
0).
- Positive control, a code token changed in `auth-manager.ts` (a fourth
element added to the `fields` projection of the password-reuse read):
DIFFER (exit 1).
- Positive control, one digit changed inside a kept test title
(`admin-remove-user-gate-ordering.test.ts:207`): DIFFER (exit 1).

Every mutation went through `scripts/ablation-replace.mjs`, and each
landed (anchor 1 to 0, blob changed). Each restore was proven
byte-identical to the HEAD blob (`c0bdef025a39`, `ec83f09f556e`), with
`git diff HEAD` empty and a clean tree afterwards.

## Changeset

This change ships bytes, so a `patch` changeset for
`@objectstack/plugin-auth`
(`.changeset/20596-plugin-auth-provenance-anchors.md`) 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 the build, the rewritten comments reach `dist`:
`35e94c96b` appears 8 times in each of `dist/index.d.ts`, `index.d.mts`,
`index.js` and `index.mjs`; `f8eb73601` twice in each declaration file;
`bf8d129b5` and `e238c79f0` once in each of the four; `d6e80b28b` and
`4d25d22d4` twice in each runtime file; `c0714eb5d` and `61821e54c` once
in each declaration file; `b706af987` once in each runtime file.
Positive control: the unchanged line 「read best-effort off the identity
row.」 beside a shipped rewrite is found once in `index.d.ts` and once in
`index.js`. A never-written negative phrase appears nowhere. No dead
number of the 16 is left anywhere in `dist`.

## Gates (head `5ae64e8b8`)

- **Citation judging, as CI runs it:** `pnpm check:issue-citations`
(self-test) exits 0. `node scripts/check-issue-citations.mjs` exits 0:
the diff-scoped run judged 5 citations across 14 files, and all 5
resolve.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0, with the
sibling-package prose ids at their baseline and no growth.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `5ae64e8b8` derived 65 commands:
all 57 derived at dispatch, plus `check:duration-unit-keys`,
`check:engine-double-contract`, `check:logger-receiver-detach`,
`check:objectql-double-limit`, `check:query-options-erasure`,
`check:type-check-coverage`, `check:type-check-debt` and
`check:where-matcher`. It was re-derived after a fresh `git fetch`
(`origin/main` `a918fe7fd`, 2 commits ahead, neither touching
`plugin-auth`): the same 65. Each ran with its exit code captured before
any pipe, and all 65 exit 0. `--ran`, fed each command with its exit
code, reports 65 run, 0 NOT MEASURED (a derived zero), 0 unrun, and
exits 0. A full `turbo run build` of `./packages/*` and `./packages/*/*`
ran first under the shared verify lock (71 of 71 tasks, exit 0), so no
gate hit an unbuilt workspace.
- **Tests and typecheck, under the verify lock:**
- `pnpm --filter @objectstack/plugin-auth test`: 115 files and 2,464
tests pass. That is every test file in the package, the 17 touched ones
included.
- `pnpm --filter @objectstack/plugin-auth typecheck` exits 0 (`tsc`
main, `tsconfig.examples.json`, and `check:test-typecheck` held at its
ledger). The main program reads 63 non-test files; the
`tsconfig.test.json` program reads all 178 files under `src/`, the 115
test files included, and all 31 touched files are in it (`--listFiles`).
- **Lint, as a proven narrowing:** `eslint --no-inline-config --format
json` over the 31 touched `.ts` files gives 31 files, 0 errors and 0
warnings. All 31 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 32 changed files for control bytes finds none.

## Acceptance notes

- **The gate's extractor does not see a hyphen-joined number.**
`CITATION_RE` ends in a lookahead that refuses a following hyphen, so
`objectstack-ai#13398-class` is not a citation to either the diff gate or the census,
dead or alive. This stage rewrote the 5 such sites in `plugin-auth`
because they are the same dead number in the same comment prose. At the
head, 10 dead `#N-word` sites remain in `packages/**/src` (a raw line
scan of `.ts` files against the cached board): `service-automation` 5
(all `objectstack-ai#13398-class`), `rest` 2, `plugin-security` 1, `runtime` 1, `spec`
1. The census cannot count them, so a later stage reaching those
packages has to look for them by hand. No instrument change here.
- **The census instrument did not truncate in this stage.** Three
enumerations read 185 pages each at the newest frontier.
- **Anchors the next stages can reuse.** These numbers stand elsewhere
on the census at the head: `objectstack-ai#11343` in `plugin-security` (6) and `types`
(2), anchor `c0714eb5d`; `objectstack-ai#14902` in `driver-sql` (7) and `cli` (1),
anchor `61821e54c`; `objectstack-ai#13398` in `service-automation` (4, plus the 5
hyphen-joined sites), anchor `e238c79f0`; `objectstack-ai#8734` in `core` (2), anchor
`f8eb73601`; `objectstack-ai#10165` in `objectql` (2) and `platform-objects` (1),
anchor `801296050`; `objectstack-ai#11757` in `platform-objects` (2), anchor
`4d25d22d4`; `objectstack-ai#11741` in `plugin-email` (2), anchor `b706af987`; `objectstack-ai#8676`
in `platform-objects` (1), anchor `d6e80b28b`.
- **Base.** The branch is 2 commits behind `origin/main` (`a918fe7fd`,
read at 11:20Z). Neither touches `plugin-auth`, this changeset or any of
these 16 numbers, so there was no merge.

---
_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
… 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
…mmits and ADRs that decided them (objectstack-ai#20658)

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

## What changed

This is the fourth stage of the `domain:services` lane of the
dead-citation sweep. It covers `packages/plugins/plugin-security/src/**`
and nothing else. By census it is the largest package in the lane; it
waited while its own fixes were in flight, and the claim (`5890784382`)
records that they have all landed. 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), by the method of stages 1 to 3 (PR objectstack-ai#20609 as
`422db788a`, PR objectstack-ai#20626 as `b80ab579d`, PR objectstack-ai#20634 as `4d04b6be3`). That
is **266 sites on 258 lines in 51 files, covering 40 numbers**:

- 140 census sites (all of this package's census sites except the 3
generated headers, see below);
- 123 sites in test comments, which the census defers;
- 3 sites in comment prose that the gate's extractor does not match at
all: one hyphen-joined (`objectstack-ai#8919-era`) and two slash-joined second numbers
(`objectstack-ai#6483/objectstack-ai#6608`, `objectstack-ai#11184/objectstack-ai#11343`), see Acceptance notes.

Each rewritten line now cites the record in this repository that decided
what the line describes, and says in its own words what was decided. Two
numbers have an in-repo decision record, and it is preferred: `objectstack-ai#11082`
cites **ADR-0055's amendment** (2026-09-07, transitive chains compose),
and `objectstack-ai#6609` cites **ADR-0094 D5-R**, which records that conflict ruling
(option A, accept the tightening). Every other number cites the commit
in `origin/main` history that decided it: **36 distinct shas**. Three
pairs share one anchor because one number was the pull request that
settled the other (`objectstack-ai#6483` and `objectstack-ai#6608`, `objectstack-ai#16607` and `objectstack-ai#16722`, `objectstack-ai#16608`
and `objectstack-ai#16805`); `objectstack-ai#12143` was itself a pull request, and its squash commit
`f64668d3c` is also where route A (`objectstack-ai#11374`) reached this plugin's key
columns. No number was dropped.

Only comments changed. Every touched source file keeps its line count
(266 lines out, 266 in, over 51 files), so no line citation into these
files moves. 8 of those 266 lines hold no dead citation; they are
reflow, listed under Wordings below. 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. Over the whole diff, added minus
removed is 0 or negative for every number (the gate's own
`extractCitations` over the diff: 277 citations removed, 14 added, all
14 kept resolving numbers on the lines they already stood on), and no
number is new to the diff. No PR number stands on an added line.

Sixty-five dead sites are left on purpose: 60 test strings, 2 operator
log strings and 3 generated headers (see the list below).

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

## Census: `plugin-security`, before and after

**Instrument (A1).** The gate's own `node
scripts/check-issue-citations.mjs --census --json`, read-only and
unchanged. The count below is its `allocated-but-absent` findings under
`packages/plugins/plugin-security/`. Each run counts as a reading only
because its board frontier equals the newest issue number, read by a
separate request just before and just after the run.

| reading | tree | board | whole-repo `allocated-but-absent` |
plugin-security sites | lines | files | numbers |
|---|---|---|---|---|---|---|---|
| before | base `cd901d7a5`, run 2026-09-29T13:00:53Z to 13:04:37Z |
enumerated, 185 pages, frontier objectstack-ai#20647 (newest objectstack-ai#20646 before, objectstack-ai#20647
after), 18,474 numbers | 1,707 | **143** | 140 | 22 | 28 |
| after | head `aa067dad3`, run 13:29:02Z to 13:32:37Z | enumerated, 185
pages, frontier objectstack-ai#20649 (newest objectstack-ai#20649 before and after), 18,476 numbers
| 1,567 | **3** | 3 | 3 | 1 |

The before count matches the 143 that census `5884031174` read at
`f11b5f20`. The 3 left are the generated `objectstack-ai#11671` headers. The
whole-repo drop is 140, exactly this diff's census sites. The `resolves`
tally is 32,878 in both runs, and `resolves-as-pull-request` (1,984) and
`cross-repo-unjudged` (995) did not move either. The after run was taken
on `aa067dad3`; the head `f90c9b123` adds only the changeset. No run was
truncated or discarded: all three enumerations in this stage (two census
runs and the supplementary board below) read 185 pages at the newest
frontier.

**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 `plugin-security/src` (216 files). It uses one
board, enumerated by the gate's own `enumerateBoard` at 13:08:21Z (185
pages, frontier objectstack-ai#20647, equal to the newest).

| reading | citations | dead | src comment | test comment | src string |
test string |
|---|---|---|---|---|---|---|
| before, `cd901d7a5` | 2,746 | **327** | 143 | 123 | 2 | 59 |
| after, `aa067dad3` | 2,483 | **64** | 3 | 0 | 2 | 59 |

Its src-comment column equals the census's 143, which is the control on
the second instrument. The 2,307 resolving, 88 pull-request and 24
cross-repo citations are the same in both readings. A third, raw reading
(every `#` followed by digits, judged against the same board, whatever
surrounds it) finds 331 dead occurrences before and 65 after: the 4 it
sees beyond the gate are the three prose sites above and one more second
number inside a kept test title.

## Per-number table

Sites and files count every dead occurrence in scope at the base
(comments and strings, tests included, gate-invisible spellings
included). `rewritten / left` counts the sites rewritten and the sites
left. Each anchor was read in its message and diff, not only its
subject.

| number | sites / files | rewritten / left | anchor: what it decided |
|---|---|---|---|
| `objectstack-ai#6206` | 2/1 | 1/1 | `8e13ca876`: share-link enforcement takes the
whole authz envelope (option-A ruling); it adds this package's
`group`-posture repro. Stage 2's anchor |
| `objectstack-ai#6216` | 1/1 | 1/0 | `f586f1a89`: one `ExecutionContext` assembler,
with the closed-field-set pin. The anchor the spec, runtime and rest
stages gave it |
| `objectstack-ai#6483` | 14/5 | 14/0 | `ee58392e1`: ADR-0005's allow-list enforced,
nine unapproved types (`permission` among them) rolled back to
`allowOrgOverride: false`. Its message records the zero-row measurement,
the `allowRuntimeCreate` boundary and this suite's stub blind spot. The
spec stages' anchor |
| `objectstack-ai#6564` | 1/1 | 1/0 | `54299caad`: the per-row `ISharingService` write
verdict becomes tri-state (allow / abstain / deny); `objectstack-ai#6564` was that
pull request |
| `objectstack-ai#6608` | 11/5 | 11/0 | `ee58392e1`: `objectstack-ai#6608` was the pull request
itself; this is its squash commit |
| `objectstack-ai#6609` | 3/2 | 3/0 | ADR-0094 D5-R: the record of that conflict
ruling (option A, accept the tightening), executed by objectstack-ai#6858 |
| `objectstack-ai#8692` | 10/3 | 9/1 | `712e185db`: the 2026-08-15 ruling, option A:
the seed insert stamps `managed_by: 'platform'` explicitly, forward
only, and the resync skip warn stops claiming intent |
| `objectstack-ai#8714` | 15/2 | 10/5 | `42b05af89`: explain reports a deactivated
permission set or position through the shared held-state vocabulary. The
anchor the spec stage gave it |
| `objectstack-ai#8757` | 14/3 | 10/4 | `6feac910b`: the 2026-08-15 ruling: the master
gate is the sole row-write authority for a `controlled_by_parent`
detail; delegated writes keep both floors |
| `objectstack-ai#8772` | 5/2 | 5/0 | `8abada3ba`: the freeze note, Direction 4 of the
2026-08-16 master-reference ruling; it names the two ramp legs and the
three shapes this guard alone refuses |
| `objectstack-ai#8778` | 2/1 | 1/1 | `7901b2dd2`: option A, a stamp-only,
read-neutral `tenancy.organizationField`. The spec stage's anchor |
| `objectstack-ai#8804` | 2/1 | 2/0 | `db923a3a8`: `objectstack-ai#8804` was the measurement pull
request: a seeder-created row is stored `'admin'`, and resync reports
resynced 0 / resyncSkipped 8 |
| `objectstack-ai#8839` | 7/3 | 6/1 | `c25b2d52a`: the 2026-08-15 ruling, reading 1:
one per-object `sys_comment` delete policy, so moderation stops being
dead behind the floor |
| `objectstack-ai#8865` | 14/2 | 12/2 | `498f4e884`: the 2026-08-15 ruling, direction
1: leg 1 of the master gate drops the platform ownership floor on a
sharing `allow` |
| `objectstack-ai#8919` | 1/1 | 1/0 | `b5378550e`: `/meta` publish and rollback gated
on `manage_metadata`; it created the write-door census whose count rule
the line applies. The rest stage's anchor |
| `objectstack-ai#11082` | 18/2 | 13/5 | ADR-0055's amendment (2026-09-07):
`controlled_by_parent` composes across a chain, bounded, failing closed.
One implementation-only line (the factory split) cites `61713314e`, the
commit that landed it |
| `objectstack-ai#11343` | 13/6 | 12/1 | `c0714eb5d`: walled elevation requires a
VERIFIED owner-email match, and the bootstrap replays on the verifying
`sys_user` update. Stage 3's anchor |
| `objectstack-ai#11374` | 3/2 | 2/1 | `3954fb7df`: route A, the 2026-08-24 ruling to
declare a sourced `maxLength` on every keyed text column; the
object-file line cites `f64668d3c`, which applied it to this plugin's
key columns |
| `objectstack-ai#11451` | 20/4 | 18/2 | `c33f18592`: the curated half's existence
read becomes one batched `$in` carrying the `objectstack-ai#8470` predicate; the
reconcile is equality-gated; the derived half's batching is filed, not
decided |
| `objectstack-ai#11518` | 35/7 | 31/4 | `e1d773eb7`: the unscoped existence page cap
is measured, not trusted: one row more than the budget, and an
overflowing page degrades loudly to the per-item read |
| `objectstack-ai#11520` | 17/2 | 15/2 | `1a6855226`: the derived half is batched too,
unnarrowed, on its own index; a derived name whose read cannot answer is
declined |
| `objectstack-ai#11671` | 4/4 | 1/3 | `09b4f4e4e`: generated translation leaves
record the source revision they were filled from. Stages 1 and 2's
anchor |
| `objectstack-ai#11702` | 1/1 | 0/1 | a test title only; nothing to rewrite |
| `objectstack-ai#11703` | 15/3 | 13/2 | `5cb62d88b`: `clone_permission_set` carries
all five copied facets; the params list is the payload. The runtime
stage's anchor |
| `objectstack-ai#11725` | 3/1 | 2/1 | `1e79aa4f8`: the probe of the trash and restore
door, which pinned its unreachability and measured the residual |
| `objectstack-ai#11753` | 2/2 | 2/0 | `0e4e51b0a`: `ActionParamSchema.carryOver`, the
carry-over ruling's schema half. The spec stage's anchor |
| `objectstack-ai#11843` | 5/4 | 4/1 | `5619aace3`: the 2026-08-25 ruling, option B:
the packaged-permission-set lock registered at the metadata door. The
verbatim quotation 「11843 同意」 is kept as written |
| `objectstack-ai#12020` | 7/2 | 7/0 | `9cfc1f7e9`: the lock extended to the restore
leg, refusing on the durability channel; the residual tripwire inverted
in the same change |
| `objectstack-ai#12143` | 2/1 | 2/0 | `f64668d3c`: `objectstack-ai#12143` was the pull request
itself: each plugin's keyed-text-bounds pin reads the widths off its own
registration path |
| `objectstack-ai#12144` | 11/1 | 6/5 | `3a04b0125`: the shared identifier schemas
pinned to the storage columns that bound them; the ceiling is
storage-owned |
| `objectstack-ai#12147` | 1/1 | 1/0 | `945e91a13`: the class-level keyed-text-bounds
gate over every `*.object.ts`, superseding the per-package pins |
| `objectstack-ai#13176` | 6/5 | 6/0 | `a68c61267`: this package's test files put in
front of tsc through the sibling `tsconfig.test.json` |
| `objectstack-ai#14484` | 2/1 | 1/1 | `3f64fe6c6`: `organization_id` stamped on every
`sys_record_share` write, with the backfill and the tenancy-ledger
admission; it adds this test file. Stage 2's anchor |
| `objectstack-ai#16518` | 7/2 | 3/4 | `470746ae4`: `current_user.accessible_org_ids`
resolved into the RLS variable bag |
| `objectstack-ai#16607` | 8/3 | 4/4 | `1d73d45c1`: RLS membership staged on the write
`check` path, so a membership-keyed check resolves on a bare insert |
| `objectstack-ai#16608` | 13/4 | 6/7 | `a016f08b8`: the insert-side RLS `check`
judges the row that will be stored, after `beforeInsert` |
| `objectstack-ai#16682` | 22/4 | 18/4 | `9b9581b11`: the `single`-posture promotion
target is chosen, not sampled: the order stated to the driver, the
declared owner preferred and required verified, bounded pages with a
loud ceiling |
| `objectstack-ai#16722` | 1/1 | 1/0 | `1d73d45c1`: `objectstack-ai#16722` was the pull request
itself |
| `objectstack-ai#16805` | 1/1 | 1/0 | `a016f08b8`: `objectstack-ai#16805` was the pull request
itself; its message records the contract review's findings |
| `objectstack-ai#16861` | 7/2 | 6/1 | `1c83ca226`: the `already_have_admin` guard
stops letting the org-admin row count decide: two ordered, bounded legs
that warn with the number examined |
| `objectstack-ai#19307` | 5/3 | 4/1 | `8f6d83147`: the duplicate-name refusal on
`sys_permission_set` carries `UNIQUE_VIOLATION`, and the packaged-set
lock answers first. The spec stage's anchor |

Every cited sha matches exactly one commit (`git rev-parse
--disambiguate`, count 1 for each of the 36), and every one is an
ancestor of the base (`merge-base --is-ancestor`, exit 0 for all 36; the
history is complete, `--is-shallow-repository` false, 15,092 commits).
Where an earlier stage already anchored a number, this stage reuses that
anchor after checking it against this package's lines.

## Wordings to check

- **Two ADR anchors.** `objectstack-ai#11082`: ADR-0055's only amendment (2026-09-07)
is the in-repo record of the chain decision, so the tags read `[ADR-0055
amendment]`; `security-plugin.ts:8027` (the thrower split into a
factory) is an implementation detail the ADR does not record, so it
cites `61713314e`. `objectstack-ai#6609`: the lines already named ADR-0094 D5-R, and
now say it records ruling A (`permission-set-projection.ts:32`, `:535`,
`permission-set-projection.test.ts:498`).
- **A stale claim corrected, `errors.ts:184-185`.** The line said the
publish-time lint was 「open and unruled」. The ruling of 2026-08-16 made
that false; `8abada3ba` corrected the sibling paragraph in
`security-plugin.ts` and missed this one. It now says the ruling (commit
`8abada3ba`) orders the lint ramp and that the ramp has not landed,
which matches the `security-plugin.ts` paragraph (1 reflow line).
- **A vanished pull-request body,
`permission-set-projection.test.ts:14-16`.** The lines quoted the body
of the pull request, which answers 404. They now state what
`ee58392e1`'s own message records about the same blind spot: this suite
stubs `saveMetaItem`, and the real gate is pinned by the dogfood cases
and a dedicated 403 suite (2 reflow lines).
- **The same, `packaged-permission-set-restore-leg.test.ts:47`.**
「recorded on objectstack-ai#12020's PR」 became 「was measured for commit 9cfc1f7」;
the line itself already states the measurement.
- **A referent, `bootstrap-system-capabilities.test.ts:1148`.** 「this
file's own objectstack-ai#8919-era rule」: the count rule it applies lives in the
write-door census that `b5378550e` created (the rule's text is
`bb920ee08`'s), not in this file. The line now says so.
- **Dead comment ids dropped with their issues.** `comment 5306089973`
(`security-plugin.ts:8093`) and `comment 5587754690`
(`bootstrap-platform-admin-walled-owner.test.ts:482`). The verbatim
maintainer quotation under the second is untouched.
- **Words where the anchor is one line away.**
`bootstrap-platform-admin.ts:630` (「a pre-ruling install」, anchor on
`:628`), `bootstrap-platform-admin-walled-owner.test.ts:493` (「the
TRIAGE seat's」, anchors on `:463` and `:482`), `security-plugin.ts:8137`
(「that ruling」, anchor on `:8132`),
`identifier-storage-ceiling-pin.test.ts:51` (「the triage fence at the
top of this file」, anchors on `:13` and `:25`),
`packaged-permission-set-lock.test.ts:94` (anchor on `:95`).
- **Reflow, 8 lines with no dead site** (every file keeps its line
count): `bootstrap-platform-admin.ts:267-268`, `errors.ts:185`,
`identifier-storage-ceiling-pin.test.ts:26` (「dispatch」 became 「scope」,
because the dispatch was the card's),
`packaged-permission-set-lock.test.ts:95`,
`permission-set-projection.test.ts:15-16`, `security-plugin.ts:8094`.
- **Box-drawing rulers.** `security-plugin.ts:3078` and
`bootstrap-platform-admin.ts:715`, `:1110`, `:1149` gave up as many
trailing rule characters as the anchor added, keeping at least one.

## The 65 sites left

- **Test titles, 57 sites.** `describe` / `it` titles, which are string
tokens, left as stages 1 to 3 left theirs:
`bootstrap-declared-capabilities.test.ts:454`;
`bootstrap-platform-admin-existing-holder-scan.test.ts:297`;
`bootstrap-platform-admin-promotion-selection.test.ts:281`;
`bootstrap-platform-admin-seeded-provenance.test.ts:184`;
`bootstrap-platform-admin-walled-owner.test.ts:504`, `:569`, `:587`;
`bootstrap-seed-round-trips.test.ts:795` (two numbers), `:980`;
`bootstrap-system-capabilities.test.ts:968`, `:1096`;
`controlled-by-parent-chain.test.ts:460`, `:540`, `:578`;
`controlled-by-parent-detail-write-authority.test.ts:594`, `:650`,
`:669`, `:708`, `:724`, `:813`; `explain-engine.test.ts:171`, `:203`,
`:853`, `:873`, `:893`; `identifier-storage-ceiling-pin.test.ts:125`,
`:143`, `:160`; `insert-check-post-image.test.ts:573`, `:612`, `:662`,
`:775`, `:838`, `:875`, `:939`;
`objects/default-permission-sets.test.ts:299`;
`packaged-permission-set-lock-gate.test.ts:183`;
`packaged-permission-set-lock.test.ts:647`, `:812`, `:813`;
`packaged-permission-set-restore-leg.test.ts:264`, `:265`;
`permission-set-duplicate-name-refusal.test.ts:195`;
`plugin-keyed-text-bounds.test.ts:67`;
`record-share-tenant-wall.test.ts:149`;
`rls-accessible-org-ids-plumbing.test.ts:191`, `:256`, `:325`, `:382`;
`rls-check-membership-staging.test.ts:388`, `:400`, `:439`, `:505`;
`security-plugin.test.ts:153`; `share-link-tenant-wall.test.ts:239`;
`tenant-layer.test.ts:237`.
- **Test assertion messages, 3 sites.** String literals passed to
`expect`: `identifier-storage-ceiling-pin.test.ts:172`, `:193`
(`objectstack-ai#12144`) and `packaged-permission-set-lock.test.ts:694` (`objectstack-ai#11703`).
- **Operator log strings, 2 sites.** `security-plugin.ts:7864` and
`:7872`, the two `logger.error` lines of the chain guards (`objectstack-ai#11082`).
Runtime strings are form D, and the shrink-only `doc-authoring-prose-id`
baseline already holds both (`security-plugin.ts`, `objectstack-ai#11082: 2`).
- **Generated headers, 3 sites.**
`translations/{es-ES,ja-JP,zh-CN}.source-hashes.generated.ts:8`
(`objectstack-ai#11671`). Their producer is a string literal in `packages/cli`,
outside this lane; the pointer is on objectstack-ai#20594.
- There is no quoted ruling carrying a dead number in this package: the
one verbatim quotation, 「11843 同意」, carries no `#`.

## Mechanical guard: no code token moves

The guard compares the TypeScript parser's leaf nodes, with comments as
trivia and JSDoc nodes excluded, base `cd901d7a5` against head. Template
literals are therefore read in context. It ran over all 51 touched `.ts`
files.

- Real run: 221,086 base tokens, **0 files with a token change** (exit
0).
- Comment control in `seed-name-lookup.ts` (`TWO events, ONE
consequence` to `TWO events, ONE result`): 0 files changed, as expected
(exit 0).
- Positive control, a code token added in `seed-name-lookup.ts` (an
extra key in the batched read's `where`): DIFFER (exit 1).
- Positive control, one digit changed inside a kept test title
(`bootstrap-system-capabilities.test.ts:1096`): DIFFER (exit 1).

Every mutation went through `scripts/ablation-replace.mjs`, and each
landed (anchor 1 to 0, blob changed). Each restore was proven
byte-identical to the HEAD blob (`36e6be731e6a`, `e1f66feaa6fc`), with
`git diff HEAD` empty and a clean tree afterwards.

## Changeset

This change ships bytes, so a `patch` changeset for
`@objectstack/plugin-security`
(`.changeset/20596-plugin-security-provenance-anchors.md`) 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 the build, the rewritten comments reach `dist`:
`ADR-0055 amendment` appears 4 times in each of `dist/index.d.ts`,
`index.d.mts`, `index.js` and `index.mjs`; `6feac910b`, `498f4e884`
twice in each of the four; `c0714eb5d`, `e1d773eb7`, `db923a3a8`,
`470746ae4`, `1d73d45c1`, `9b9581b11` once in each of the four;
`ee58392e1` 3 times and `1c83ca226` twice in each declaration file;
`5cb62d88b` 4 times and `c25b2d52a` 3 times in each runtime file.
Positive control: the unchanged line 「declared the key's SHAPE」 beside a
shipped rewrite (`rls-compiler.ts:88-89`) is found once in `index.d.ts`,
beside 「Until commit 470746a nobody did」. A never-written negative
phrase appears nowhere. The only dead numbers left in `dist` are the two
kept `objectstack-ai#11082` log strings in the runtime files.

## Gates (head `f90c9b123`)

- **Citation judging, as CI runs it:** `pnpm check:issue-citations`
(self-test) exits 0. `node scripts/check-issue-citations.mjs` exits 0:
the diff-scoped run judged 9 citations across 19 files, and all 9
resolve.
- **Doc authoring:** `pnpm check:doc-authoring` exits 0, with the
sibling-package prose ids at their baseline and no growth.
- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` at `f90c9b123` derived 66 commands:
all 57 derived at dispatch, plus `check:duration-unit-keys`,
`check:dispatcher-error-vocabulary`, `check:engine-double-contract`,
`check:logger-receiver-detach`, `check:objectql-double-limit`,
`check:query-options-erasure`, `check:type-check-coverage`,
`check:type-check-debt` and `check:where-matcher`. It was re-derived
after a fresh `git fetch` (`origin/main` `c6b37cd08`, 3 commits ahead):
the same 66. Each ran with its exit code captured before any pipe, and
all 66 exit 0. `--ran`, fed each command with its exit code, reports 66
run, 0 NOT MEASURED (a derived zero), 0 unrun, and exits 0. A full
`turbo run build` of `./packages/*` and `./packages/*/*` ran first under
the shared verify lock (71 of 71 tasks, exit 0), so no gate hit an
unbuilt workspace.
- **Roster families the derivation lists outside its commands** (their
rosters sit in directories this diff touches): `node
scripts/check-changeset-fixed.mjs`, `pnpm check:error-code-casing` and
`pnpm check:filter-alias-parity`, each exit 0.
- **Tests and typecheck, under the verify lock:**
- `pnpm --filter @objectstack/plugin-security test`: 147 files pass,
3,202 tests pass and 23 skip. That is every test file in the package,
the 32 touched ones included.
- `pnpm --filter @objectstack/plugin-security typecheck` exits 0 (`tsc`
main, `tsconfig.scripts.json`, and `check:test-typecheck` at zero). The
main program reads 69 non-test files; the `tsconfig.test.json` program
reads all 216 files under `src/`, the 147 test files included, and all
51 touched files are in it (`--listFiles`).
- **Lint, as a proven narrowing:** `eslint --no-inline-config --format
json` over the 51 touched `.ts` files gives 51 files, 0 errors and 0
warnings. All 51 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 52 changed files for control bytes finds none.

## Acceptance notes

- **The gate's extractor does not see a number after a slash either.**
`CITATION_RE` opens with a lookbehind that refuses a `/` before the `#`
(`scripts/check-issue-citations.mjs:449`), so in `#A/#B` only `#A` is a
citation to the diff gate and the census, dead or alive. It is the same
blind-spot family as the hyphen spelling (objectstack-ai#20636). This stage rewrote
the two such prose sites in `plugin-security`
(`permission-set-overlay-discard.ts:25`,
`platform-owner-wall-bypass.ts:69`) because they are the same dead
numbers in the same comment prose. A raw scan of `packages/**/src` `.ts`
files against the board finds 33 dead second numbers of this shape at
the base and 31 at the head (one of them the kept title
`bootstrap-seed-round-trips.test.ts:795`), in 14 packages. The census
cannot count them, so a later stage has to look for them by hand. No
instrument change here.
- **The hyphen spelling in this package** (objectstack-ai#20636 names 1 here on
`main`) was `bootstrap-system-capabilities.test.ts:1148`, rewritten. 9
dead `#N-word` sites remain in `packages/**/src` at the head, none in
this package.
- **The census instrument did not truncate in this stage.** Three
enumerations read 185 pages each at the newest frontier.
- **Anchors the next stages can reuse.** These numbers stand elsewhere
on the census at the head: `objectstack-ai#11374` in `drivers` (16),
`platform-objects` (14) and `plugin-audit` (2), anchor `3954fb7df`
(route A); `objectstack-ai#6216` in `core` (8), `mcp` (1) and `plugin-hono-server`
(1), anchor `f586f1a89`; `objectstack-ai#6483` (8) and `objectstack-ai#6608` (4) in
`metadata-protocol`, anchor `ee58392e1`; `objectstack-ai#6206` in `core` (2),
`plugin-approvals` (3), `plugin-audit` (1) and `service-storage` (1),
anchor `8e13ca876`; `objectstack-ai#8778` in `metadata-core`, `plugin-approvals` and
`service-storage`, anchor `7901b2dd2`; `objectstack-ai#16608` (4) and `objectstack-ai#16805` (2) in
`objectql`, anchor `a016f08b8`; `objectstack-ai#11343` in `types` (2), anchor
`c0714eb5d`; `objectstack-ai#8692` in `cli` (2), anchor `712e185db`; `objectstack-ai#12144` in
`metadata-protocol` (1), anchor `3a04b0125`; `objectstack-ai#16682` in `core` (1),
anchor `9b9581b11`.
- **Base.** The branch is 3 commits behind `origin/main` (`c6b37cd08`,
read at 13:54Z). None touches `plugin-security` or any of these numbers;
they add three unrelated changesets and move one row of
`scripts/doc-authoring-prose-id.baseline.json` (a `packages/lint`
entry), so there was no merge.

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

---------

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants