Skip to content

docs(service-messaging): re-anchor the dead tracker citations to the commits that decided them - #20609

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20596
Clause-②: no

What changed

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

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

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

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

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

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

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

Census: service-messaging, before and after

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

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

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

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

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

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

Per-number table

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

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

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

Wordings to check, each true of its commit:

The 15 sites left

Mechanical guard: no code token moves

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

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

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

Changeset

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

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

Gates (head 267c11562)

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

Acceptance notes


Generated by Claude Code

…commits that decided them

Every comment and docblock site in packages/services/service-messaging/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, and says in its own words what that commit decided. 127 comment
sites on 109 lines in 28 files, 13 numbers, 13 distinct commits.

Comments only: every touched file keeps its line count, so no line
citation into these files moves. No citation number is added. The three
generated *.source-hashes.generated.ts headers (their producer is the CLI's
i18n extract template) and the twelve string-literal sites are left as they
were.

Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H
Co-authored-by: Claude <noreply@anthropic.com>
The rewritten docblocks reach the published dist: the built index.d.ts and
index.js carry the new commit anchors, so the change ships 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/service-messaging, touching 20 documentable anchor(s). ⚠️ 4 changed file(s) yielded no anchor (packages/services/service-messaging/src/inbox-caller.ts, packages/services/service-messaging/src/index.ts, packages/services/service-messaging/src/outbox-dispatcher-scope.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/api/environment-routing.mdx (via HttpDispatcher (symbol, a top-level class))
  • content/docs/automation/email-templates.mdx (via sendTemplate (symbol, a method of interface EmailSenderSurface))
  • content/docs/automation/webhooks.mdx (via HttpDispatcher (symbol, a top-level class), in_flight (literal, a string literal in a comment in INotificationOutbox))
  • content/docs/kernel/cluster.mdx (via HttpDispatcher (symbol, a top-level class))
  • content/docs/kernel/index.mdx (via sendTemplate (symbol, a method of interface EmailSenderSurface))
  • content/docs/kernel/runtime-services/email-service.mdx (via sendTemplate (symbol, a method of interface EmailSenderSurface))
  • content/docs/kernel/services-checklist.mdx (via listInbox (symbol, a method of class MessagingService))
  • content/docs/plugins/packages.mdx (via HttpDispatcher (symbol, a top-level class))

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

  • content/docs/releases/v17/17-2.mdx (via IHttpOutbox (symbol, a top-level interface), MessagingService (symbol, a top-level class), SqlHttpOutbox (symbol, a top-level class))
  • content/docs/releases/v17/17-3.mdx (via INotificationOutbox (symbol, a top-level interface))
  • content/docs/releases/v17/17-5.mdx (via HttpDispatcher (symbol, a top-level class), NotificationDispatcher (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/services/service-messaging/src/inbox-caller.ts, packages/services/service-messaging/src/index.ts, packages/services/service-messaging/src/outbox-dispatcher-scope.ts, …) — pages documenting those are invisible to this run
  • 4 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 — 5 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 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 0f6dcac5e99d0c6211f0d8a0e150a112d78a776f

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 267c1156211c9351d9963594808cf6db1f675d14
Local-runs: none

Reviewed for card #20596 (stage service-messaging) against the ruling it cites, C+D (comment 5749154545 on #19123). Inputs: the card body and its three comments, the PR body, its file list and the net diff against main at the head, and the head's check-runs. The head equals the PR object's head.sha; the head repo is the base repo; 29 files, +126 / −116; no governed surface in the file list. Reads only — git diff, git show, git grep, git merge-base against the shared object store, and REST GETs. Nothing built, run or re-run.

① Derived judgments

Accept set: unchanged — right. Every one of the 232 changed lines in the 28 .ts files (116 out, 116 in) opens with a comment marker (*, //, /**); the only non-comment added lines in the whole diff are the changeset's ten. index.ts's two changed lines are comments inside the export type block. No export, type, schema, refusal text or runtime token moves. Per-file line counts are preserved (--stat reads 116/116), so no line citation into these files moves. The dev's leaf-token guard (0 files changed) agrees with this reading and was not re-run.

Citations: 0 added — right. The #N tokens on added lines (#6071, #6551, #10753, #11009 twice, #12069, #17634, #18567) each stand on the removed line they replace, so added minus removed is 0 for every number, and no PR number stands on an added line. The 14 dead numbers count 142 tokens under service-messaging/src at base 7a1faf1a5 and 15 at the head: 127 removed, the PR's 127 sites.

13 anchors — each right. Every sha resolves to exactly one commit (rev-parse --disambiguate, count 1) and every one is an ancestor of the base (merge-base --is-ancestor, 13 of 13). Each commit's message and file stat were read against the line it now anchors:

Ruling C's order, ADR or ruling record first — the commit rung is right. git grep of the 14 numbers over docs/adr/** and scripts/adr-anchors/** at origin/main hits one line, ADR-0131 line 474, which cites #11741 as a writer fact ("#11741 widened SendEmailInput") and does not decide it; so b706af987 is the correct rung, and no ADR records the other 13 decisions. The [commit 9-hex] spelling is the one packages/spec/src already carries on main (for example [commit d2619fd0c]).

The #11374 rewrite — right, with a note. "(route A, ruling 2026-08-24)" beside [commit e4902d2b9] leaves no tracker number; the in-repo record of that ruling is the header of scripts/check-keyed-text-bounds.mjs (lines 25 to 26, "route A, the maintainer's 2026-08-24 ruling") and commit 3954fb7df's message. A stricter spelling would have named 3954fb7df beside the date; a note, not a defect.

Seven reflow / referent lines — right. All comment lines; each hunk keeps its count. sql-outbox.ts:281 now reads "the race that commit describes", and 1a47a5368's message does describe the ack-versus-claim race it replaced the card pointer with.

Fifteen sites left — right to leave. Verified at the head: outbox.ts:187 and :210, three tokens inside the two NotificationAckError refusal messages — runtime strings, form D, outside the claim's comment-prose surface, and already ledgered in scripts/doc-authoring-prose-id.baseline.json under outbox.ts (#11453 twice, #11859 once); nine describe titles on eight lines (string literals); and line 8 of the three *.source-hashes.generated.ts headers, whose producer is packages/cli/src/utils/i18n-extract.ts:2294 on origin/main — a hand edit would be undone by the next os i18n extract.

② Semver level

patch for @objectstack/service-messaging — right; skip-changeset would have been wrong. The package publishes (publishConfig.access: public; files is dist, README.md, CHANGELOG.md). The rewritten JSDoc sits on exported declarations — ClaimedDeliveryRecord, NotificationAckError, INotificationOutbox.ack and reap, MessagingService.listInbox, the sys_notification_* object literals — and tsup.config.ts emits dts, so dist/index.d.ts changes bytes and the tarball differs. The dev's dist/index.js counts are its own measurement, not re-run here; this verdict rests on the .d.ts half. Same treatment the landed spec stages received (.changeset/spec-remainder-provenance-anchors.md, patch).

Clause-②: no — right. PR body line 2 carries it; no key, code or accept-set movement, so no arm and no ADR-0087 marker is owed. The changeset names one package, carries no tracker number and no model identifier, and states the change is comments only. Check Changeset on the head: success.

Nit, not verdict-bearing. The file is service-messaging-provenance-anchors.md where the dispatch runbook asks for an issue-number-then-slug name. No gate enforces it, the name is descriptive rather than a random word pair, and the landed spec stages used the same unnumbered shape.

③ Boundary flags

os-dev-report (comment 5885673193): five deviations, three out_of_scope_findings, no open_questions.

  • D1 — 30 test-comment sites included. Within the claim's surface, comment and docblock prose under src/**, where the test files live; counted by the supplementary instrument since the census defers tests. Answered: right.
  • D2 — seven lines without a dead site reflowed. Verified in ①. Answered: right.
  • D3 — five census enumerations, one truncated run discarded. A reading discipline, not a diff matter; the before-count used (100) equals the card's census. Answered: accepted.
  • D4 — a direct rebuild for check:type-check-debt. Local build state only; outbox.ts at the head is blob 80618f871, the one the diff's index line names. Answered: accepted.
  • D5 — no labels added. The PR's labels (documentation, size/m, tests, tooling) are the labeler's; skip-changeset was rightly not applied. Answered: right.
  • F1 — enumerateBoard in scripts/check-issue-citations.mjs returned an 85-page board at exit 0, once in five runs. A verifier that degrades silently is the class AGENTS.md's "Absence must be loud" names; the instrument is outside this diff's file surface. The seat's ACCEPT (comment 5885704534) records a pointer 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; that carrier must hold it, or a card is owed. Escalated to the seat; verdict-neutral here.
  • F2 — the three generated #11671 headers. Producer verified at i18n-extract.ts:2294; the fix is at the producer plus a regeneration of every copy, never a hand edit. Carrier: the stage that takes packages/cli. Answered: right to leave.
  • F3 — the two outbox.ts refusal strings. Form D, outside this stage's form-C surface, already baselined. Ruling C+D orders D first, and the report names no carrier for the services lane's runtime strings. Escalated: a form-D card or carrier for domain:services. Verdict-neutral for this PR.

Check-runs on the head, as read in the act that wrote this record (2026-09-29T07:38Z): 25 success — among them Build Core, Dogfood Regression Gate (and its three shards), Temporal Conformance (live PG + MySQL), Governed Surface Queue Guard, Check Changeset, Check PR Size, Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Test Core 1, 2, 5 and 6 of 6; 3 skipped — Console Pin Gate, Build Docs, Packed-tarball smoke; 4 in progress — Lint & Repo Gates, Type Check · workspace, Test Core 3 and 4 of 6; 0 failure. The seat checks convergence before landing; this verdict judges ①②③.

Implemented-by: claude/issue-20596-service-messaging-citations
Reviewed-by: session_01XY5uCwTjZj7884yYtyur4H

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 29, 2026 07:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 29, 2026
Merged via the queue into main with commit 422db78 Sep 29, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20596-service-messaging-citations branch September 29, 2026 07:58
veigajoao pushed a commit to veigajoao/objectstack that referenced this pull request Sep 29, 2026
…me/src to the commits that decided them (objectstack-ai#20624)

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

## What changed

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

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

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

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

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

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

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

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

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

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

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

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

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

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

## Per-number table

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

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

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

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

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

## The sites left

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

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

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

## Mechanical guard: no code token moves

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

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

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

## Changeset

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

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

## Gates (head `a5cdfd8a46`)

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

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

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

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

## Hypotheses (measured first)

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

## Acceptance notes

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

## Deviations

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

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

---------

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

Part of objectstack-ai#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 objectstack-ai#19123), by stage 1's method (PR objectstack-ai#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 objectstack-ai#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 objectstack-ai#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 objectstack-ai#20614 (newest objectstack-ai#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 objectstack-ai#20616 (newest objectstack-ai#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
objectstack-ai#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 |
|---|---|---|---|
| `objectstack-ai#5973` | 4/2 | 3/1 | `abeb3751f`:
`HierarchyScopeContext.organizationId` is the tenancy authority, and it
is required. `objectstack-ai#5973` was the PR itself; this is its squash commit |
| `objectstack-ai#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 |
| `objectstack-ai#6523` | 3/3 | 3/0 | `aa4b90d9a`: 36 contract signatures converge on
the full `ExecutionContext`. The same anchor the spec stages gave this
number |
| `objectstack-ai#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 |
| `objectstack-ai#8792` | 2/1 | 2/0 | `83c661d97`: the bulk-write merge's missing
provenance mark is recorded as ruled (2026-08-15), not oversight |
| `objectstack-ai#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 |
| `objectstack-ai#11671` | 5/5 | 2/3 | `09b4f4e4e`: `os i18n extract --source-hashes`
writes the provenance companion (maintainer ruling objectstack-ai#12069 Option A,
which stays cited). The same anchor stage 1 gave it |
| `objectstack-ai#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 |
| `objectstack-ai#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 |
| `objectstack-ai#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 |
| `objectstack-ai#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 |
| `objectstack-ai#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 |
| `objectstack-ai#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 objectstack-ai#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 objectstack-ai#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

- **Non-test string (1 site).** `sharing-service.ts:1674` sits inside
the operator-facing `warn` text for a hierarchy scope that was not
widened (「… resolveOwnerIds, objectstack-ai#5973); …」). It is a runtime string, so it
is form D, not form C, and the shrink-only `doc-authoring-prose-id`
baseline already holds it (`sharing-service.ts` → `objectstack-ai#5973: 1`). Left and
listed, as stage 1 left its refusal strings.
- **Test titles (14 sites).** `describe` titles in
`backfill-sys-record-share-organizations.test.ts:185`, `:274`, `:324`,
`:367`, `record-share-organization-stamp.test.ts:194`, `:239`, `:278`,
`:315`, `:353`, `:436`, `sharing-service.test.ts:1798` (the `objectstack-ai#14484`
titles), `share-link-eligibility.test.ts:607` (`objectstack-ai#13608`),
`share-link-enforcement-context.test.ts:226` (`objectstack-ai#6206`) and
`sharing-rule.test.ts:1898` (`objectstack-ai#8710`). Tokens, left as they were.
- **A verbatim ruling quotation (1 site).**
`share-link-service.test.ts:478` is point 2 of the maintainer's
2026-09-01 ruling, quoted verbatim and untranslated. It carries 「沿
objectstack-ai#13608 先例」. AGENTS.md keeps a quoted Chinese ruling in its original
words, and rewriting the quote would rewrite the ruling. Left.
- **Generated headers (3 sites).** Line 8 of the `es-ES`, `ja-JP` and
`zh-CN` `.source-hashes.generated.ts` files carries 「(objectstack-ai#11671, maintainer
ruling objectstack-ai#12069 Option A, extending objectstack-ai#8765 Option B)」. `os i18n extract`
writes that line from `packages/cli/src/utils/i18n-extract.ts`, so the
fix belongs at the producer, the carrier stage 1 named. The hand-written
`translations/index.ts:26` is rewritten here, with the same wording
stage 1 used.

## 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
`objectstack-ai#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 objectstack-ai#20556, and this stage changes no
instrument.
- **What stays for later stages.**
- The 3 generated `objectstack-ai#11671` headers, whose producer is
`packages/cli/src/utils/i18n-extract.ts`.
- The `objectstack-ai#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`: `objectstack-ai#6206` at 53 sites and `objectstack-ai#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](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
…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
…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