Skip to content

docs(metadata-protocol): re-anchor the dead tracker citations to the commits and ADR that decided them - #21233

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20595-metadata-protocol-citations
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-20595-metadata-protocol-citations

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #20595
Clause-②: no

What changed

Stage 1 of the domain:engine lane of the dead-citation sweep: packages/metadata-protocol/**, comment and docblock prose only, per the claim (5938223120). The next stages (objectql, driver-sql, driver-memory, then the rest) are separate claims, so #20595 remains open.

Every comment or docblock site in the package that cited a tracker number answering 404 is rewritten in ruling C+D's form C (record 5749154545 on #19123): the ADR or ruling record when one exists, otherwise the commit in this repository's history that made the decision the sentence describes. That is 293 sites on 279 lines in 54 files, covering 56 numbers:

  • 163 census sites (155 lines, 8 files under src/): the whole allocated-but-absent population of the gate's own census in this package at the base;
  • 1 site in tsup.config.ts (:15, #11235): same number, outside the census glob but inside the claimed file surface;
  • 129 test-comment sites (123 lines, 45 test files), which the census defers, found by the supplementary reading below. Each cites a number the census itself reads as dead (41 of the 43 numbers are among the census set; #10485 and #8600 are dead elsewhere in the repository).

Anchors: 55 numbers by commit sha, 1 by ADR (#13185, ADR-0005's design-principle-3 correction), 0 by words alone. #11674 is split across two commits, one per half of what it named (see the table). Two #12176 sites (protocol.item-name-grammar.test.ts:6, :12) drop the number without a new citation, because line :4 of the same docblock now cites the commit (311433f6b) that both sentences describe.

Only comments changed. Every file keeps its line count (280 lines out, 280 in, plus the changeset), so no line citation into any of them moves. No code token moves (the guard below). No citation number is added: every number on an added line already stood on its line, and 20 of those 23 are live by the census's own judgement. Of the 3 it never reads (they stand only in test files), #11099 and #8390 answer as pull requests and #14767 answers 404 (see Sites left).

A patch changeset: 52 of the rewritten non-test lines are in the published dist (the .d.ts keeps JSDoc on exported members, and esbuild keeps a few comments in the JS), and dist is not byte-identical with the base text (see Changeset).

Census: metadata-protocol, before and after

Instrument (A1). The gate's own node scripts/check-issue-citations.mjs --census --json, read-only and unchanged (it carries #20989's wider extractor, merge a5bce40888, an ancestor of the base). The count is its allocated-but-absent findings under packages/metadata-protocol/.

reading tree board whole-repo allocated-but-absent sites lines files numbers
before base e47355be5, run ended 18:52:32Z enumerated, 191 pages, frontier #21227, 19,048 records 755 163 155 8 54
after 06d41e512, run 19:21:20Z to 19:25:08Z enumerated, 191 pages, frontier #21228, 19,049 records (newest number #21228 read just before and just after the run) 592 0 0 0 0

The whole-repo drop is 163, and the two finding sets differ by exactly the 163 rows of this package, removed; none was added. resolves (34,516), resolves-as-pull-request (2,092) and cross-repo-unjudged (1,139) did not move. The card's 162 was taken at f11b5f20a2 with the older extractor; the base here reads 163, which includes the slash-joined partial-index-probe.ts:395 #16657 that the post-landing census (5923084795) named. The only commit after 06d41e512 adds the changeset file, which is outside the census surface.

Supplementary instrument, the whole package. The census reads neither test files nor strings nor files outside src. A second reading runs the gate's own exported extractCitations (whole-file and comment-prose projections) and namesThisRepository over every tracked file in the package (235 .ts, 2 .md, 2 .json). A number is dead when the before census reported it allocated-but-absent, and live when the census's own scope extraction judged it and did not report it. The 82 numbers neither covers (they stand only in test files, strings or the changelog) were each read on their own (issues endpoint, which also answers pull requests): 27 issues, 38 pull requests, 17 answer 404. Controls: #10888, #11674 and #16657 (the card's and the census's named sites) answer 404, #5286 and #12624 answer 200.

reading citations dead src comment test comment test string changelog
before, e47355be5 6,383 434 164 151 63 56
after, head 6,090 141 0 22 63 56

src comment here includes tsup.config.ts. Its before value is the census's 163 plus that one site, which is the control on the second instrument. The drop of 293 citations is exactly the rewritten sites. A third, raw reading (every # followed by 2 to 6 digits, whatever surrounds it) counts 6,479 before and 6,186 after: the same drop of 293. The 22 test-comment sites left all carry numbers outside the census's surface (see Sites left).

Per-number table

src counts census sites (plus tsup.config.ts for #11235), test counts test-comment sites. Every sha below matches exactly one commit (git rev-parse --disambiguate, count 1) and is an ancestor of the base (git merge-base --is-ancestor, exit 0 for all 56; the clone was unshallowed first, --is-shallow-repository false, 15,415 commits at the base). Each one's message or diff names the number it replaces (diff counts are the added lines naming it), and for every sentence that credits a ruling, a measurement or a note to the number, the commit's own message carries that ruling, measurement or note: ee58392e1 (the 2026-08-08 three-part ruling), c74aefe63 (ruling 2026-08-22, option A), 65846bc46 (ruling A, 2026-09-03), 75e66fc8e (Option B, diff raw then redact), 96326040f (the idempotence proof the direction-A ruling was conditional on), 8744de9e9 (the second-rung ablation), 82cb6e849 (the two faces left open), 376c70f98 (the measured shims: true consequence). The one exception is 2a29caa53 (#9741): its message records the decision itself (environmentId recorded as transport-level) but not the 2026-08-18 ruling, so that site keeps its own date and now reads 「recorded 2026-08-18, landed as commit 2a29caa」. 37 of the 56 numbers were already re-anchored by other lanes' stages, and for every one of them this stage uses a commit those stages used (none differs; #11674 adds 9a884c6e4 beside their 1cba33f16, because 25 of its 32 sites here describe the write-back half, which git blame puts in 9a884c6e4). The other 19 had no prior anchor and were measured here.

number src test anchor kind what it decided
#6037 1 1 18189983d commit validate-only data operation — DataProtocol.validateData
#6307 1 0 293476148 commit refuse a repeated ?version= on GET/DELETE /packages/:id instead of handing the array to PackageService
#6478 1 4 474f131cf commit rolls flow's allowOrgOverride back to false per ADR-0005's original call, the write path refusing loudly
#6483 8 10 ee58392e1 commit enforces the ADR-0005 whitelist: nine unratified allowOrgOverride: true flags rolled back to false; its message records the 2026-08-08 three-part maintainer ruling it executes
#6608 4 2 ee58392e1 commit the same commit: #6608 was the pull request whose squash it is
#8600 0 1 018d22cc3 commit require authored OWD at the runtime object door; retire ADR-0094 R2 external-wider arm; declare object in runtimeTypes
#8648 2 0 e5eeb499c commit pin the SEARCH-axis remedy agreement, and correct the three comments that claimed word-identity
#8671 1 2 75e66fc8e commit stop the meta diff endpoint serving credential values
#8818 1 1 fd6bdf89f commit saveMetaItem's missing-item refusal declares 400 INVALID_REQUEST instead of answering 500
#9740 1 0 11b779e0f commit declare MetadataProtocol.getMetaItemLayered; drop the dead 'overlay' lockSource arm
#9741 1 0 2a29caa53 commit declare previewDrafts/state on meta-read requests; record environmentId as transport-level; retire REST door casts
#9798 1 0 c7655d472 commit restore the #4630 unscoped multi-delete refusal on sys_comment through the wired engine
#9817 1 1 855591fe7 commit discriminate a failed sys_organization probe from a genuinely empty one
#9934 13 2 79c46da90 commit producer-side user-facing marking for hook refusal messages — userMessage channel
#9967 2 1 8f266f1cd commit serve a sandboxed body's declared HTTP status on /api/v1/data
#10063 5 1 9e04c3e35 commit let the publish door state the package it is promoting
#10159 1 0 1ec36b730 commit refuse a settings write issued before the engine is bound
#10340 3 4 26f3588fb commit decide /meta org scope on the folded type, not the raw URL spelling
#10350 5 3 490879ad0 commit declare packageId on publishMetaItem's request type, and correct three comments that say the per-item door names no package
#10382 1 4 ee09d2119 commit derive each live-MySQL suite's database from its own file, and enforce it repo-wide
#10485 0 10 35ad101bc commit retire the themes carrier key and ThemeSchema — app.branding is the one colour surface
#10788 1 1 3a7ec2d3b commit a raw-SQL seam that cannot answer is absent, not empty
#10789 6 1 38bc74ed1 commit a seam that cannot answer is absent, not empty
#10842 1 3 f334d662e commit watch(_, since) replays from sys_metadata_history, and what a bare watch() owes is written down
#10886 3 10 809e61221 commit inventory the DESTRUCTIVE_CHANGE 409's faces and pin the sole carrier
#10888 5 4 d806081dd commit render the spec-validation 422 findings clause per write face
#10895 1 1 a79bd3561 commit Publish refusals: declare failed[].issues + seedApplied.issues, then trim error to a headline
#11003 5 1 c74aefe63 commit thread packageId into both resolveDraftOrgScopeForPublish probes
#11014 1 2 2d8b92ff1 commit the destructive gate's reachable type set is object alone
#11015 6 9 82cb6e849 commit make the destructive-change remedy clause face-aware — stop prescribing ?force=true on the duplicate door
#11021 3 1 7d81c889f commit close() terminates watch iterators instead of emitting a drain event
#11235 6 1 376c70f98 commit derive discovery version instead of the hardcoded '1.0' literal
#11350 2 0 ece4dad31 commit re-export the three types the root entry's own inferred types mention
#11674 20 12 9a884c6e4 + 1cba33f16 commit seed pass 2 writes back by the internal id captured at insert time, healing keyless datasets / warn at load time when a seed defers a required column, and document the ordering constraint at the four pointer-pair sites
#12144 1 0 3a04b0125 commit pin the shared identifier schemas to the storage columns that bound them
#12176 2 3 311433f6b commit Declare the metadata item-name grammar in spec and refuse it loudly at the publish door
#12194 6 5 311433f6b commit Declare the metadata item-name grammar in spec and refuse it loudly at the publish door
#12195 1 0 7986d973f commit Retire compound-name metadata addressing — un-mount the three :section arities and unify SDK URL spelling
#13185 1 1 ADR-0005, design principle 3, its Correction note ADR the field-level patch model retired and deleted whole under ADR-0049 (executed as 9e0ba21a1)
#13186 1 1 9e0ba21a1 commit Retire the paper metadata-customization protocol with its full coupling set
#13259 1 1 2a75270b1 commit honour hidden on getUiView's list priority pass
#13324 4 2 4cda78c9b commit require a missing-table error to name the table that was read
#14390 1 0 9d7f7259f commit update answers a driver unique violation with the DUPLICATE_RECORD envelope, on every driver
#14403 1 0 93d2d679b commit pin the batch-row sink's disclose/withhold log coherence
#14409 2 3 3ecb7dc1a commit measure what each dialect materialises for a datetime JS cannot hold
#14541 1 0 6d178a408 commit consult the bespoke structured arms before the declared-status passthrough, so both error doors answer one refusal with one body
#14683 6 5 96326040f commit apply the allowOrgOverride read gate inside getMetaItems, so multi-type sweeps are scoped per type
#14723 3 1 65846bc46 commit a batch/import ROW reports a unique-constraint refusal as UNIQUE_VIOLATION, the route's one wire spelling
#14770 3 3 d5cbb44f3 commit gate getMetaItem's overlay read on the metadata registry
#14907 1 2 e1d4f9e3f commit getMetaItemLayered gates the org read, bound after the canonical fold
#14938 2 1 c383352cb commit listDrafts emits the ISO-8601 string updatedAt declares
#15068 1 0 8744de9e9 commit collapse the published-seed read to the single env-wide read its gate produces
#16488 5 1 460d4b807 commit render a composite externalId in seed diagnostics instead of its NUL-joined key
#16657 3 1 5a95b0e93 commit read the dialect text out of cause for operator-facing records
#17167 4 5 dc709b2cf commit the organization probe records the operator channel as is, empty included
#19306 1 1 f9e16d856 commit a packaged permission set's DELETE stops reporting a deletion it did not perform

#13185: the ADR rung is not empty there. ADR-0005's design principle 3 carries a dated Correction that records the 2026-08-29 retirement of the field-level patch model, so ruling C's first rung applies. protocol.ts:8618 already names that record on the same line (「recorded as a correction inside principle 3 itself」), so there the number is dropped beside commit 9e0ba21a1. get-meta-item-org-read-gate.test.ts:40 now names it (「ADR-0005 principle 3's correction」). For the other 55 numbers, git grep over docs/adr and scripts/adr-anchors finds no ADR or anchor that records the decision a site describes. ADR-0094 D5-R and ADR-0086 mention the #6483 rollback, but only as a pointer to it; the narrative and the ruling are in ee58392e1's message. So ruling C's commit rung applies.

Wordings to check

Most rewrites swap a tag in place ((#N) to (commit SHA), [#N] to [commit SHA], #N's X to commit SHA's X), the form the landed stages use. These are the ones that say more than the tag:

Sites left

  • In src comments and tsup.config.ts: none.
  • Test comments: 22 sites carry 13 numbers that answer 404 and that the census never reads (they stand only in test files). By the dispatch's rule they are not this stage's population, so they are counted and not edited: #6287, #10058, #10064 (2), #10420, #10978 (2), #11017, #13214, #13244, #13258, #14389, #14431 (5), #14767, #17621 (4). #14767 stands on a line this PR rewrote (get-meta-item-org-read-gate.test.ts:10): it is the pull-request number of 96326040f's squash, kept beside the new anchor as it stood.
  • String literals: 63 test-string sites (describe and it titles, assertion arguments) carry dead numbers: 56 with census-dead numbers (#12194 6, #10789 5, #10886 5, #11014 4, #11674 4, #16488 4, and 20 more numbers once to three times) and 7 with #17621. Non-test source strings carry none. Strings are outside this stage's file surface.
  • Outside src: the release-owned CHANGELOG.md names dead numbers on 56 sites (45 census-dead, 11 among the 404 reads); left. README.md, package.json and tsconfig.json name no number; vitest.config.ts's two are live.

Mechanical guard: no code token moves

The guard compares, base e47355be5 against the working tree, over all 54 touched .ts files:

  • Reading 1: the TypeScript parser's leaf nodes, from a forEachChild walk. Comments are trivia there, and JSDoc is never visited.
  • Reading 2: the full token stream in parser context, from a getChildren walk. Punctuation and keywords are included and JSDoc nodes are skipped. String, template and numeric literals are compared in full on both readings.

Results:

  • Real run at the head: 213,265 base tokens, 0 files with a token change on either reading (exit 0).
  • Comment control (「The derived」 to 「The DERIVED」 on protocol.ts:13): 0 files changed (exit 0).
  • Positive control, a code identifier (postureEnforcesWall to postureEnforcesWallX in protocol.ts's import): DIFFER in both readings (exit 1).
  • Positive control, a string literal ('dashboard' to 'dashboardX' in sys-metadata-repository.contract.test.ts): DIFFER in both readings (exit 1).
  • Positive control, a numeric literal (BULK_BATCH_SIZE = 200 to 201 in seed-loader.ts): DIFFER in both readings (exit 1).

Every mutation went through scripts/ablation-replace.mjs (wrap mode) under a shell trap that restores by absolute path from HEAD. Each landed: anchor count 1 to 0, blob changed. Each restore was proven equal to its HEAD blob (5be50ab59075, 99ef73ef7562, 41b999ca3ecc), with git diff HEAD empty and a clean tree afterwards.

Changeset: patch (dist measured)

files[] is dist, README.md and CHANGELOG.md, and the package is not private. The dependency closure was built first (turbo run build --filter='@objectstack/metadata-protocol^...', 12 tasks). Then the package's own build (tsup plus check-dts-emitted) ran three times under the shared verify lock:

  • Leg 1, at the head: 24 dist files hashed. Of the 154 rewritten non-test lines, 52 appear verbatim in dist: 36 from protocol.ts, 7 from sys-metadata-repository.ts, 5 from seed-loader.ts and 4 from migrations/seed-tenancy-backfill.ts. Most are in index.d.ts / index.d.cts; two from seed-tenancy-backfill.ts are in index.js / index.cjs, where esbuild keeps a comment inside an expression.
  • Leg 2, with the base text put back in the 8 non-test files (each proven equal to its base blob): index.d.ts, index.d.cts, index.js and index.cjs differ from leg 1, and so do the content-hashed chunk names, including the seed-loader chunks.
  • Leg 3, after the proven restore: all 24 files are byte-identical to leg 1, so the build is deterministic and the difference is the rewrite.

So the rewrite ships, and .changeset/20595-metadata-protocol-provenance-anchors.md declares a patch for @objectstack/metadata-protocol, comment text only, with the claim's Clause-②: no line.

Gates (head 3265b142f)

  • Citation judging, as CI runs it: node scripts/check-issue-citations.mjs exits 0 (「every citation this change adds resolves」, 19 citations judged across 8 files). pnpm check:issue-citations exits 0 (self-test, 173 cases, 9 batteries).
  • Doc authoring: pnpm check:doc-authoring exits 0 (17,085 spec strings clean; the sibling-package prose-id baseline holds, no growth).
  • Derived gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at 3265b142f (change set derived from git: 55 paths against merge base e47355be5) derived 62 commands. All 62 ran, each with its exit code captured before any pipe, and all 62 exit 0. --ran reports 62 derived, 62 run, 0 NOT-MEASURED (a derived zero), 0 unrun, and exits 0. A full turbo run build over ./packages/* and ./packages/*/* ran first under the shared verify lock (71 of 71 tasks), so no gate hit an unbuilt workspace.
  • Roster families the derivation lists outside its commands whose roster sits in a directory this diff touches: node scripts/check-changeset-fixed.mjs, pnpm check:authz-resolver and pnpm check:error-code-casing. Each exits 0.
  • Tests and typecheck, under the verify lock, at 3265b142f:
    • pnpm --filter @objectstack/metadata-protocol test: 200 test files pass and 3 skip (203); 2,973 tests pass and 19 skip.
    • pnpm --filter @objectstack/metadata-protocol typecheck exits 0, and tsc --noEmit --listFiles puts all 203 tracked test files in the program (233 package files).
  • Lint, as a proven narrowing: eslint with inline config disabled, over the 54 touched .ts files plus dist/index.js as the control, gives 55 results, 0 errors and 1 warning: the control's ignore notice. Its --format json output reports none of the 54 ignored. eslint.config.mjs never enables type-aware linting (its lines 327-328 say so), so a comment edit 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 55 changed files for control bytes finds none.

Acceptance notes

  • Base. The branch is on main at e47355be5. main has since moved five commits (to 62b90d74f), and dispatch-gates flags that as a stale tree. None of the five touches a file in this diff, scripts/check-issue-citations.mjs or scripts/pm/dispatch-gates.mjs. One edits protocol.meta-types-degenerate-derivation.test.ts in this package, adding a citation beside a live one. The one derivation input that moved, scripts/doc-authoring-prose-id.baseline.json, lost 63 lines, none of them naming this package. No merge was taken; the merge queue rebuilds on the merged generation.
  • The before census was not bracketed by newest-number reads. It enumerated 191 pages at frontier driver-sql on MySQL: create() answers the insert id (0) instead of the inserted record, so sign-up answers 400 FAILED_TO_CREATE_USER, the dev admin seed fails and no user can sign in #21227; the newest number read at 19:21:20Z, before the after run, was fix(mcp)!: the MCP stdio engine-only reader joins the stored-metadata-body family (exit one) #21228.
  • Comment ids are outside the grammar. comment 5299845282 stands twice in this package (protocol.ts:22793, protocol.diff-credential-redaction.test.ts:19) and names a comment on the deleted #8671, so it no longer resolves either. Neither instrument reads it, and 75e66fc8e, now cited beside it, carries the ruling's text in its message. Left as it is.
  • Wording only: 「the card」 / 「this card」 stands on 377 comment lines in 105 files under this package. It carries no number, neither instrument sees it, and this diff removes no antecedent except the one repaired at protocol-publish-drafts-package-scope.test.ts:400.
  • A first guard reading was void. The guard's first version read the token stream with a bare scanner, which has no parser context. It loses its place at template literals and reported 27 files changed; its parser-context reading reported 0 on that same run. That bare-scanner reading was replaced by the forEachChild walk above, and every figure in the guard section is from the replacement.

Generated by Claude Code

claude added 4 commits October 1, 2026 19:13
…ns to the commits that decided them

Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp
Co-authored-by: Claude <noreply@anthropic.com>
…er citations to the commits that decided them

Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp
Co-authored-by: Claude <noreply@anthropic.com>
…ions to the commits that decided them

Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/metadata-protocol, touching 58 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/metadata-protocol/src/discovery-version.ts, packages/metadata-protocol/src/migrations/live-mysql-database.testkit.ts, packages/metadata-protocol/tsup.config.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

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

  • content/docs/api/client-sdk.mdx (via diffItem (sdk, the bare tail of client method meta.diffItem, bound to GET /api/v1/meta/:type/:name/diff), getAudit (sdk, the bare tail of client method meta.getAudit, bound to GET /api/v1/meta/:type/:name/audit), getReferences (sdk, the bare tail of client method meta.getReferences, bound to GET /api/v1/meta/:type/:name/references), getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), meta.diffItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:name/diff, selected by route anchor /:type/:name/diff), meta.getAudit (sdk, the route ledger binds it to GET /api/v1/meta/:type/:name/audit, selected by route anchor /:type/:name/audit), meta.getReferences (sdk, the route ledger binds it to GET /api/v1/meta/:type/:name/references, selected by route anchor /:type/:name/references), meta.getView (sdk, the route ledger binds it to GET /api/v1/ui/view/:object/:type, selected by route anchor /view/:object/:type), meta.publishItem (sdk, the route ledger binds it to POST /api/v1/meta/:type/:name/publish, selected by route anchor /meta/:type/:name/publish), publishItem (sdk, the bare tail of client method meta.publishItem, bound to POST /api/v1/meta/:type/:name/publish))
  • content/docs/api/metadata-api.mdx (via /view/:object/:type (route, bridged from symbol getUiView — its route source's handler names it))
  • content/docs/api/plugin-endpoints.mdx (via /view/:object/:type (route, bridged from symbol getUiView — its route source's handler names it))
  • content/docs/concepts/metadata-lifecycle.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class), SysMetadataRepository (symbol, a top-level class), getMetaItemLayered (symbol, a method of class ObjectStackProtocolImplementation), saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/data-modeling/drivers.mdx (via getMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/data-modeling/validation.mdx (via SeedLoaderService (symbol, a top-level class))
  • content/docs/deployment/validating-metadata.mdx (via publishMetaItem (symbol, a method of class ObjectStackProtocolImplementation), saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/kernel/cluster.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/kernel/contracts/metadata-service.mdx (via getPublished (sdk, the bare tail of client method meta.getPublished, bound to GET /api/v1/meta/:type/:name/published; the bare tail of client method meta.getPublished, bound to GET /meta/:type/:name/published), getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it), /meta/:type/:name/publish (route, a path literal in a comment in publishMetaItem))
  • content/docs/kernel/services-checklist.mdx (via deleteMetaItem (symbol, a method of class ObjectStackProtocolImplementation), getDiscovery (symbol, a method of class ObjectStackProtocolImplementation), getMetaItem (symbol, a method of class ObjectStackProtocolImplementation), getUiView (symbol, a method of class ObjectStackProtocolImplementation), saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation), getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), /view/:object/:type (route, bridged from symbol getUiView — its route source's handler names it))
  • content/docs/permissions/authorization.mdx (via saveMetaItem (symbol, a method of class ObjectStackProtocolImplementation))
  • content/docs/protocol/kernel/error-handling.mdx (via /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it))
  • content/docs/protocol/objectql/state-machine.mdx (via SeedLoaderService (symbol, a top-level class))
  • content/docs/protocol/objectui/concept.mdx (via /view/:object/:type (route, bridged from symbol getUiView — its route source's handler names it))
  • content/docs/ui/react-pages.mdx (via /:type/:name/references (route, bridged from symbol findReferencesToMeta — its route source's handler names it))

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

  • content/docs/releases/v16.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class), /api/v1/notifications (route, a path literal in a comment on a changed line))
  • content/docs/releases/v17/17-0.mdx (via ObjectStackProtocolImplementation (symbol, a top-level class), getView (sdk, the bare tail of client method meta.getView, bound to GET /api/v1/ui/view/:object/:type), meta.getView (sdk, the route ledger binds it to GET /api/v1/ui/view/:object/:type, selected by route anchor /view/:object/:type), /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it), /api/v1/notifications (route, a path literal in a comment on a changed line))
  • content/docs/releases/v17/17-1.mdx (via SeedLoaderService (symbol, a top-level class), auditMetaItem (symbol, a method of class ObjectStackProtocolImplementation), diffMetaItem (symbol, a method of class ObjectStackProtocolImplementation), findReferencesToMeta (symbol, a method of class ObjectStackProtocolImplementation), /:type/:name/diff (route, bridged from symbol diffMetaItem — its route source's handler names it), /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it), /meta/:type/:name/publish (route, a path literal in a comment in publishMetaItem))
  • content/docs/releases/v17/17-2.mdx (via /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it), /meta/:type/:name/publish (route, a path literal in a comment in publishMetaItem))
  • content/docs/releases/v17/17-3.mdx (via getUiView (symbol, a method of class ObjectStackProtocolImplementation), /:type/:name/published (route, bridged from symbol getMetaItemLayered — its route source's handler names it))
  • content/docs/releases/v17/17-4.mdx (via /:type/:name/publish (route, bridged from symbol publishMetaItem — its route source's handler names it))
  • content/docs/releases/v17/17-5.mdx (via getAudit (sdk, the bare tail of client method meta.getAudit, bound to GET /api/v1/meta/:type/:name/audit), meta.getAudit (sdk, the route ledger binds it to GET /api/v1/meta/:type/:name/audit, selected by route anchor /:type/:name/audit), /:type/:name/diff (route, bridged from symbol diffMetaItem — its route source's handler names it))

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
  • 3 changed file(s) yielded no anchor (packages/metadata-protocol/src/discovery-version.ts, packages/metadata-protocol/src/migrations/live-mysql-database.testkit.ts, packages/metadata-protocol/tsup.config.ts) — pages documenting those are invisible to this run
  • 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 — 11 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 3ddd3d0c4a355b58d074245ae6732ac91489c2a7 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 3ddd3d0c4a355b58d074245ae6732ac91489c2a7

⚠️ 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 3ddd3d0c4a355b58d074245ae6732ac91489c2a7 → 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: 3265b142f09d14f46901728b80b5cff445c97f61
Local-runs: none

Inputs read, and nothing else: card #20595 (body and all five comments: the two lane pointers, the post-landing census 5923084795, the claim 5938223120, the os-dev-report 5939504521), ruling C+D (5749154545 on #19123), PR #21233 (body, 55-file list, the diff against its merge base), the anchors and files at the head through git show / git log / git grep in a full (not shallow) clone, the GitHub issues endpoint for every number the diff removes or keeps, and the head's check-runs last. The dispatch order and the seat's own conclusions were not inputs.

① Derived judgments

Diff of record. GitHub's diff for #21233 (55 files, +295/-280, 236 hunks) is byte-equal, index lines aside, to git diff e47355be5...3265b142f in the local clone; the merge base of the head with origin/main (95e24b0096 at read) and with the PR's base.sha 3ddd3d0c4a is e47355be5 in both cases. Four commits, each ending in the model-free trailer pair. Head repo equals base repo (not a fork). Judged RIGHT.

Accept set and public surface: nothing moves. Judged RIGHT, on this reading of the diff text: every -/+ line was paired in order within its hunk, 280 pairs across the 54 .ts files, the 15 unpaired + lines being the changeset. In all 280 pairs the first differing character lies after a // on that line, or on a line whose first non-blank is * or /*; no pair has a */ before its change point or code after a */; no // sits inside a string literal or a URL scheme. The nine pairs whose quote-character counts move are apostrophes or backticks inside comment prose (for example protocol.ts:18462, seed-loader-pointer-pair.test.ts:871, sys-metadata-repository.contract.test.ts:187). Every .ts file keeps its line count: numstat additions equal deletions for all 54, and wc -l at base and head agree for each. So no code token, string literal or numeric literal changes on this reading. The dev's parser-based guard (0 of 54 files on two readings, with a comment control at 0 and identifier, string and numeric controls at DIFFER) is consistent with it; I did not re-run the guard.

Sampled hunks, 18 pairs across 12 files, each read as its -/+ pair. protocol.ts:3000 (pre-#10888 clause to the clause before commit d806081dd), :5697 (#9798 to commit c7655d472 restored a declared-but-unenforced refusal), :8590 (#14683 to the resurrection commit 96326040f closed), :8618 (#13185 / PR #13186 dropped beside commit 9e0ba21a1, the same line keeping recorded as a correction inside principle 3), :16640 (#12176 census to the census commit 311433f6b records), :18462 (#10350 to Commit 490879ad0 added the pins), :18478 (#9741 to recorded 2026-08-18, landed as commit 2a29caa53), :22792 (#8671 to commit 75e66fc8e); migrations/seed-tenancy-backfill.ts:964 (#10159 to refused since commit 1ec36b730); discovery-version.ts:26 (#11235 triage to when the derivation landed (commit 376c70f98)); migrations/partial-index-probe.ts:395 (the slash-joined #16019/#16657 to #16019/commit 5a95b0e93, the live number kept); tsup.config.ts:15 ([#11235] to [commit 376c70f98], #10993 kept, live); seed-loader.ts:1947 (the card's control site, pre-#11674 to before commit 9a884c6e4) with :365, :441, :616, :891, :993, :1479, :1866; seed-loader-pointer-pair.test.ts:871 (#11674's to commit 1cba33f16, the B half); seed-loader-deferred-dropped.test.ts:9, :19, :371; protocol-publish-drafts-package-scope.test.ts:399 and :400; sys-metadata-repository.contract.test.ts:187 (the quoted deleted line elided to an ellipsis, not re-spelled); protocol.item-name-grammar.test.ts:4, :6, :12 (:4 takes Commit 311433f6b, :6 and :12 drop the number with no substitute); get-meta-item-org-read-gate.test.ts:10 and :40. Every one is a comment-only rewrite in ruling C's form. Judged RIGHT.

Anchors. All 56 shas in the per-number table (54 distinct: ee58392e1 serves #6483 and #6608, 311433f6b serves #12176 and #12194, 9e0ba21a1 serves #13186 and the ADR anchor's execution) resolve with git rev-parse --disambiguate to exactly one commit each, and git merge-base --is-ancestor exits 0 against both origin/main and the base e47355be5 for all 56. Each one's message or diff names the number it replaces: 37 in the message body and the diff, 12 in the diff alone (#8818, #10159, #10382, #13259, #13324, #14403, #14723, #14770, #14907, #15068, #17167 and 9a884c6e4 for #11674), and 7 only in the subject's squash suffix (#6478, #6608, #8600, #9817, #10788, #10895, #14409), where the dead number was that pull request's own and the commit is its squash. The + lines introduce exactly 54 new nine-hex spans, the 54 anchors, and no other. PR numbers appear beside a sha only as a convenience (get-meta-item-org-read-gate.test.ts:10: Commit 96326040f (PR #14767)), never as the citation. Judged RIGHT.

The ADR anchor (#13185 to ADR-0005). docs/adr/0005-metadata-customization-overlay.md at origin/main, design principle 3, carries a dated Correction (2026-08-29) recording that MetadataOverlaySchema and its module were retired and deleted whole under ADR-0049, executed by PR #13186, which is 9e0ba21a1. Ruling C's first rung applies, and both sites take it: get-meta-item-org-read-gate.test.ts:40 now reads ADR-0005 principle 3's correction, commit 9e0ba21a1; protocol.ts:8618 drops the number beside the commit on a line that already names the correction. For the other 55 numbers I read no ADR or anchor that records the decision a site describes, and the body's statement that ADR-0094 D5-R and ADR-0086 only point at the #6483 rollback matches the ruling living in ee58392e1's message. Judged RIGHT.

The #11674 split. On the two commits' own diffs: 9a884c6e4 adds internalIdByRecordIndex (7 added lines) and no early-signal line; 1cba33f16 adds the EARLY SIGNAL for a required-column deferral (23 added lines) and no internalIdByRecordIndex line. The 25 sites citing 9a884c6e4 describe write-back by the captured internal id; the 7 citing 1cba33f16 describe the deferral signal (seed-loader.ts:441, :616, :835, :1080, :1354, :1419; seed-loader-pointer-pair.test.ts:871). Each read. Judged RIGHT.

No new tracker number; live numbers stayed. For every pair, the numbers on the + line are a subset of the numbers on its - line: 0 added. 293 number occurrences leave (328 on - lines, 35 on +; one line, sys-metadata-repository.contract.test.ts:187, cited #10842 twice), the dev's 293 exactly, over 56 distinct numbers. Probed over the issues endpoint at my read: all 56 removed numbers answer 404; of the 23 distinct numbers kept on + lines, 20 answer as issues and 2 as pull requests (#11099, #8390), and #14767 answers 404, the one the body declares; controls #5286, #12624, #20595 and #21233 answer 200. Judged RIGHT, as declared.

Rewritten sentences stay true. Each sentence that credits a commit with a ruling, a measurement or a note was matched against that commit's message: c74aefe63 carries Maintainer ruling 2026-08-22, option A (protocol.ts:6274, :6319; package-scope test :399-:400); 65846bc46 carries Maintainer ruling A (2026-09-03) (protocol.ts:2432); ee58392e1 carries the 2026-08-08 three-part ruling in Chinese (protocol.adr0005-org-override-rollback.test.ts:27); 75e66fc8e carries Implements the maintainer ruling (issue comment 5299845282, Option B) (protocol.ts:22792, redaction test :4, dead-history test :628); 96326040f carries Includes the idempotence proof the direction-A ruling was conditional on (protocol.ts:8590, :9193; layered test :38); 8744de9e9 carries Measured, not reasoned: an ablation neutering the second rung (#15068); 82cb6e849 names the two unrepaired faces (protocol.ts:4284); 376c70f98 records both the measured shims: true consequence and a package-local resolver because the dependency direction forbids importing runtime's (tsup.config.ts:15, discovery-version.ts:26); d806081dd records that both channels showed each finding twice and renders the clause per face, so the duplication commit d806081dd removed is true (protocol.ts:3000, 409-inventory test :654); 490879ad0 adds 90 lines to protocol-publish-drafts-package-scope.test.ts, so added the pins is true (protocol.ts:18462); 311433f6b is the commit that added protocol.item-name-grammar.test.ts, whose header carries the census (protocol.ts:16640; grammar test :4, :6, :12; unrecognised-meta-type test :307); 1ec36b730 is refuse a settings write issued before the engine is bound (seed-tenancy-backfill.ts:964); c7655d472 restores a refusal that could not fire through ObjectQL.delete on exactly the shape it refuses (protocol.ts:5697); 2a29caa53 records the environmentId decision and not the 2026-08-18 ruling, and the site keeps its own date (protocol.ts:18478). Nothing overclaimed. Two softenings, named, neither an overclaim: discovery-version.ts:26 says the hoist was considered and declined when the derivation landed, and the commit records the choice and its reason rather than a triage discussion (the body says so); protocol.org-scoped-write-refused.test.ts:320 moves deliberately left open from PR #6478 to commit 474f131cf, which is that pull request's squash, so the subject is the same actor, though the commit's message does not itself narrate the open tier.

Reach into dist, which decides the changeset. Not rebuilt here. From the manifest at the head: files is dist, README.md, CHANGELOG.md; the package is not private; tsup.config.ts sets dts unless OS_SKIP_DTS and no minify, so the .d.ts emission keeps the JSDoc on exported declarations, and the sampled protocol.ts, sys-metadata-repository.ts and seed-loader.ts sites are docblocks on exported members. The dev's three-leg measurement (52 of 154 rewritten non-test lines verbatim in dist; base-text rebuild differs in index.d.ts, index.d.cts, index.js, index.cjs and the chunk names; restore byte-identical, 24 of 24) is consistent with that and is read, not reproduced. A changeset is owed. Judged RIGHT.

② Semver level

.changeset/20595-metadata-protocol-provenance-anchors.md at the head: frontmatter '@objectstack/metadata-protocol': patch; a summary line; Clause-②: no bare at the start of its own line (line 7); prose stating comment-only text, its reach into index.d.ts / index.d.cts and a few esbuild-kept JavaScript comments, and no export, type, error code, status, message text or runtime behaviour changes. patch is the right level: nothing authorable, exported or on a payload moves, so no breaking marker and no ADR-0087 disposition is owed, and Clause-②: no is the right arm. The readers in scripts/check-changeset-no-major.mjs and scripts/check-adr-0087-registration.mjs accept exactly this bare line-start shape. The PR body's first line is Part of #20595 with no closing keyword, and its second is Clause-②: no. Check Changeset and Part-of PR must not also close its card read success at the check-run read below. Judged RIGHT.

③ Boundary flags

  • Deviation (1), unmerged main. At read origin/main is 95e24b0096, seven commits past e47355be5. None of the seven touches a file in this diff (two other metadata-protocol test files moved on main, neither in the PR); scripts/check-issue-citations.mjs and scripts/pm/dispatch-gates.mjs did not move; scripts/doc-authoring-prose-id.baseline.json shrank. An in-memory git merge-tree --write-tree origin/main head reports no conflict, and no merge=os-regen path is in the diff, so the local reading and GitHub's would not diverge here (mergeable: true at the PR read). The queue rebuilds onto main. Accepted.
  • Deviation (2), the one no-number line, protocol-publish-drafts-package-scope.test.ts:400: its antecedent was the number removed on :399, and c74aefe63's message carries the ruling it now points at. Accepted.
  • Deviation (3), the void first guard reading: disclosed, replaced, and the replacement's 0 of 54 agrees with my text reading. Accepted.
  • Deviation (4), the before-census not bracketed by newest-number reads (frontier driver-sql on MySQL: create() answers the insert id (0) instead of the inserted record, so sign-up answers 400 FAILED_TO_CREATE_USER, the dev admin seed fails and no user can sign in #21227, newest fix(mcp)!: the MCP stdio engine-only reader joins the stored-metadata-body family (exit one) #21228 read before the after-run): the one number in the gap is a pull request this package never cites; immaterial to a 163-to-0 reading of this package. Accepted.
  • The reconciliation by NUMBER. The census instrument reads comment prose under src/** with strings blanked and defers test files; the claim's file surface is comment and docblock prose in packages/metadata-protocol/**. Rewriting test-file comments whose numbers the census itself reads as dead stays inside the claimed surface and inside ruling C's form, adds no number and moves no token, and the interpretation is stated in the report rather than chosen silently. Accepted. Its complement, below, is what the rule leaves behind.
  • The 22 unread test-comment sites (13 numbers). All 13 answer 404 at my read; 21 lines (one carries two numbers) in 15 test files, matching the body's list. They are comment prose inside the claimed file surface and the same rot class, so they belong to THIS PACKAGE's surface and must be swept before metadata-protocol is called clean; they were counted, not edited, under a stated rule, and the body's Sites left bounds the claim correctly (In src comments and tsup.config.ts: none). Not a FAIL. Escalated to the seat: carry them into the next metadata-protocol sub-stage of dead tracker citations in the domain:engine packages (645 sites, 160 numbers, 104 files): the ruling C+D stage for this lane (from #20556) #20595 (same form, same anchors where the numbers overlap) rather than a gate-widening card, and note that #14767 on get-meta-item-org-read-gate.test.ts:10 stands beside 96326040f as a convenience PR number that itself answers 404.
  • The 63 test-string sites. describe / it titles and assertion arguments. Outside this stage by the claim's own words (no string literal), and not runtime strings in check-doc-authoring's sense, so [finding] runtime warnings outside the migration ledger print tracker numbers to authors and operators: the AutomationEngine resumeAuthority boot warning (#3801 / #5561 / #3823) and two objectql data-event warnings (#4639 / #4626) #20513 is not obviously their carrier either. Not this stage. Escalated to the seat as a carrier question: a test-title lane, or a later stage of dead tracker citations in the domain:engine packages (645 sites, 160 numbers, 104 files): the ruling C+D stage for this lane (from #20556) #20595 with the claim widened to test strings.
  • The CHANGELOG.md sites (56, on 41 lines). Release-owned under the Documentation Guardrails; never edited in a code PR, and no CHANGELOG.md is in the file list. Not this stage and not any code PR. Correctly left.
  • The dead comment-id citation (comment 5299845282 at protocol.ts:22793 and protocol.diff-credential-redaction.test.ts:19): answers 404 at my read; outside the #N grammar, so neither instrument reads it. Both lines now sit beside commit 75e66fc8e, whose message carries the ruling and its Option B, so a reader lands on an in-repo record. It is the same rot class in the claimed surface and belongs with the 22 above in the next metadata-protocol sub-stage; not this stage's population. Noted, not a FAIL.
  • Governance and size. The file list touches no governed surface (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md, docs/NORTH-STAR.md); Governed Surface Queue Guard reads success; 575 changed lines, under the 5,000 line class. This record is the dispatch's adversarial review, not a Prime Directive 14 tier record.
  • Check-runs on the head, read last, at 2026-10-01T20:14:46Z. 32 runs: 26 completed/success, 3 completed/skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)), 3 in_progress with no conclusion: Lint & Repo Gates, Test Core (5/6), Type Check · workspace. Of the seven required contexts by name: Build Core, Dogfood Regression Gate, Temporal Conformance (live PG + MySQL) and Governed Surface Queue Guard read success; Lint & Repo Gates is in progress and not yet a verdict; Test Core shards 1, 2, 3, 4 and 6 read success and shard 5 is in progress; no run named exactly TypeScript Type Check or Test Core was present at read, the type-check family reading as Type Check · source gates success, Type Check · debt ledger success, Type Check · consumer gates success and Type Check · workspace in progress. Also success: Check Changeset, Check PR Size, Part-of PR must not also close its card, The card this PR closes must claim this branch, No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Dogfood Verify CLI. An in-progress required job is not a verdict and does not by itself make this record FAIL; the seat confirms the green bar from the merge bar, not from these names, before enqueue.
  • Local-runs: none, spelled out. Reads only: GitHub GETs; git show, git log, git grep, git rev-parse, git merge-base, git diff and one in-memory git merge-tree in the main clone (no worktree, no checkout); a text pairing pass over the downloaded diff in the scratch directory. No build, test, gate or ablation was run or re-run; every guard, census, dist and gate figure above is the dev's, read and judged for consistency, not reproduced.

Implemented-by: claude/issue-20595-metadata-protocol-citations
Reviewed-by: session_017xfMoEjKUuSh2xYB8sCozp

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 20:21
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 20:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit a7d9768 Oct 1, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-20595-metadata-protocol-citations branch October 1, 2026 20:49
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ment-id citations in test comments (stage 2 of objectstack-ai#20595) (objectstack-ai#21246)

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

## What changed

Stage 2 of the `domain:engine` lane of the dead-citation sweep: the
`packages/metadata-protocol` remainder that stage 1 (PR objectstack-ai#21233, landed
as `a7d9768ec`) left, per the claim (`5940298570`). Comment and docblock
prose only. objectstack-ai#20595 stays open for the next stages (`objectql` next).

The population is what stage 1's contract review (`5939794396`)
escalated to this sub-stage:

- the **22 test-comment sites carrying 13 numbers that answer 404**,
which the census never reads (it defers test files);
- the **dead comment-id citation** `comment 5299845282`, on two lines;
- the **dead convenience number** `PR objectstack-ai#14767` beside its anchor
`96326040f`.

Each is rewritten in ruling C+D's form C (record `5749154545` on
objectstack-ai#19123): the commit in this repository's history that made the decision
the sentence describes. **23 lines in 16 files** (21 lines for the 22
numbered sites, 2 for the comment id). **Anchors: 13 numbers and the
comment id, all by commit sha; 0 by ADR or ruling record (none exists
for any of them); 0 by words alone.** Seven numbers reuse the commit
another lane already chose for them, `objectstack-ai#14767` and the comment id keep
stage 1's commit already on their line, and five were measured here.

Every file keeps its line count (+23 / -23). No code token moves (guard
below). No citation number is added: the only numbers left on changed
lines, `objectstack-ai#9612` and `objectstack-ai#13331`, stood there before and answer as issues.

**No changeset, and `skip-changeset` is the declaration:** 22 of the 23
lines are in test files, which the `tsup` entry graph never reaches, and
the one `protocol.ts` line is a `//` comment inside a method body that
the build drops. Measured, not assumed (see Changeset).

## Population, before and after

**H1, re-enumerated on `origin/main` (`a7d9768ec`).** The instrument is
the gate's own code, read-only. `extractCitations` from
`scripts/check-issue-citations.mjs` runs over every tracked file in the
package (239), in both projections (whole file and comment prose),
keeping the citations `namesThisRepository` assigns to this board.
`classifyCitation` judges each one against a board read by the gate's
own `enumerateBoard`: 191 pages, frontier objectstack-ai#21241, 19,062 records, read
20:54:38Z to 20:58:19Z on 2026-10-01. Then one REST read per number: all
13 answer **404**, and the lit controls `objectstack-ai#5286` and `objectstack-ai#12624` answer 200.

| reading | tree | citations | dead | src comment | test comment | test
string | changelog |
|---|---|---|---|---|---|---|---|
| before | base `a7d9768ec` | 6,092 | **141** | 0 | **22** | 63 | 56 |
| after | head `16b5bf6c1` | 6,070 | **119** | 0 | **0** | 63 | 56 |

- The before reading matches stage 1's own after reading (141: 0 / 22 /
63 / 56).
- The drop of 22 citations is exactly the rewritten numbered sites. No
live count moved: test comments resolve 1,586 and
resolve-as-pull-request 111 at both.
- **The count against the dispatch's 22 / 13: 22 sites, 13 numbers, on
21 lines.** One line carries two numbers. The lines are in **14** test
files; the review counted 15.
- Comment ids (10-digit, outside the citation grammar): 9 sites, 5
distinct ids at the base. Read one by one through the comments endpoint,
`5299845282` answers **404** and the other four answer 200: `5302933802`
(on objectstack-ai#8833), `5528370129` (objectstack-ai#13609), `5865708652` (objectstack-ai#20378) and
`5875579209` (objectstack-ai#20451). The control `5940298570` (on objectstack-ai#20595) answers 200.
After: 7 sites, 0 dead.

## Per-number table

Every sha below resolves to exactly one commit (`git rev-parse
--disambiguate`). Each is an ancestor of the base: `git merge-base
--is-ancestor` exits 0 for all 14, on a full clone
(`--is-shallow-repository` is false). Each one's message or diff names
the number it replaces. `ADR`: `git grep` over `docs/adr` and
`scripts/adr-anchors` names none of the 13 numbers or the comment id, so
the ADR rung is empty for all of them.

| number | sites | anchor | kind | named in | provenance | what it
decided |
|---|---|---|---|---|---|---|
| `objectstack-ai#6287` | 1 | `84c86fb45` | commit | message (squash suffix) + diff |
reused: runtime and spec lanes | `preview` / `trial` discovery folds
declared, the fold table exhaustive over `EnvironmentType` |
| `objectstack-ai#10058` | 1 | `6f5a44976` | commit | subject (the squash of that PR)
| measured here | judge a package write against its own closure |
| `objectstack-ai#10064` | 2 | `def0d3e63` | commit | message + diff | reused: lint
lane | key publish-gate finding paths by name, not the private snapshot
index |
| `objectstack-ai#10420` | 1 | `05bc692e0` | commit | message + diff | measured here |
run `SysMetadataRepository` through the shared repository contract suite
(it adds this file) |
| `objectstack-ai#10978` | 2 | `4c9780c7a` | commit | message + diff | reused: runtime
lane | ObjectQL doubles enforce `limit` after the filter and by presence
|
| `objectstack-ai#11017` | 1 | `d806081dd` | commit | diff (it adds this file) |
measured here | render the 422 findings clause per write face |
| `objectstack-ai#13214` | 1 | `cc837dbfe` | commit | diff | reused: rest lane |
require the resolved environment to belong to the caller at `GET
/ui/view/:object/:type` |
| `objectstack-ai#13244` | 1 | `889ec5b42` | commit | subject (squash) | reused: rest
lane | measure identity resolution at `GET /ui/view/:object/:type` |
| `objectstack-ai#13258` | 1 | `3d10755f0` | commit | subject (squash) | reused: rest
lane | drive the tenancy axis of `GET /ui/view/:object/:type` (it adds
the sibling harness) |
| `objectstack-ai#14389` | 1 | `10220a7bf` | commit | diff | reused: rest lane | the
409 arm gated on the engine's envelope (name and code) |
| `objectstack-ai#14431` | 5 | `a98b61b3e` | commit | subject (squash) | measured here
| re-measure the deleted-datasource prolongation across replicas (it
adds this file) |
| `objectstack-ai#14767` | 1 | `96326040f` | commit | subject (squash) | stage 1's
anchor, already on the line | the dead convenience number is dropped;
the sha stays |
| `objectstack-ai#17621` | 4 | `7e74af3df` | commit | message + diff | measured here |
execute the read-probe PostgreSQL arm on a live server (it adds the
live-postgres file) |
| `comment 5299845282` | 2 | `75e66fc8e` | commit | message | stage 1's
anchor, already beside both lines | its message records the ruling: diff
raw, then redact (Option B) |

Sentences that credit a commit with a finding, a ruling or a record were
checked against that commit's own message:

- `a98b61b3e` carries the finding all five `objectstack-ai#14431` sites describe: the
bridge heals the registry, a read inside the residue window re-hydrates
the deleted row, and the window is bounded only when no read lands in
it.
- `7e74af3df` records the no-coverage state: the PostgreSQL arm was
pinned and run nowhere.
- `6f5a44976` records the package-closure ruling.
- `75e66fc8e` carries the Option B ruling.
- `10220a7bf` records the name-and-code gate.
- `4c9780c7a` records the after-the-filter, by-presence bound.
- `d806081dd` records the per-face decision (silence keeps the full
prose on the duplicate face) but not the ruling itself. So that site
keeps the ruling's own date and now reads 「landed as」. This is stage 1's
precedent for `objectstack-ai#9741` and `2a29caa53`.

## Wordings to check

Most rewrites swap the tag in place, as the landed stages do: `(#N)`
becomes `(commit SHA)`, `[#N]` becomes `[commit SHA]`, and `PR #N`
becomes `commit SHA`. These say more than the tag:

- `protocol.invalid-metadata-422-face-inventory.test.ts:22`: 「The
maintainer ruling on objectstack-ai#11017 (2026-08-22, option D) resolved it by」
became 「The maintainer ruling of 2026-08-22 (option D, landed as commit
d806081) resolved it by」.
- `protocol.ui-view-hidden-columns.test.ts:46`: 「(objectstack-ai#13214 / PR objectstack-ai#13258)
drives the same defect」 became 「(commit 3d10755, for the card commit
cc837db fixed) drives the same defect」. Line `:47` says 「It belongs to
that card」, and this wording keeps an antecedent for it.
- `protocol.batch-row-http-status.test.ts:546`: 「(objectstack-ai#14389 §5)」 became
「(commit 10220a7)」. The `§5` named a section of the deleted card's
body, so it goes with the number.
- `get-meta-item-org-read-gate.test.ts:10`: 「Commit 9632604 (PR
objectstack-ai#14767) moved」 became 「Commit 9632604 moved」. Ruling C allows a PR
number only as a convenience link, and a link that answers 404 is not
one.
- `protocol.ts:22793`: 「maintainer ruling (comment 5299845282), Option
B.」 became 「maintainer ruling (recorded in that commit's message),
Option B.」. The line above opens with `[commit 75e66fc]`.
- `protocol.diff-credential-redaction.test.ts:19`: 「(maintainer ruling,
issue comment 5299845282 — Option B)」 became 「(maintainer ruling,
recorded in commit 75e66fc's message — Option B)」.
- `migrations/seed-tenancy-backfill.live-postgres.test.ts:55` and
`:256`: 「the no-coverage state objectstack-ai#17621 records」 became 「the no-coverage
state commit 7e74af3 records」, and 「objectstack-ai#17621 exists because it was」
became 「commit 7e74af3 exists because it was」.
- No line was reflowed, so some lines now run past their block's wrap.
`eslint.config.mjs` declares no line-length rule, and a reflow would
move neighbouring lines and every line citation into the file.

## Sites left in this package

- **Dead tracker numbers in comment prose (src, tests and
`tsup.config.ts`): none.** Dead comment ids: none.
- **63 test-string sites** (`describe` / `it` titles and assertion
arguments) are not edited. 56 carry census-dead numbers and 7 carry
`objectstack-ai#17621`. They are recorded on objectstack-ai#20595, and the claim excludes string
literals.
- **`CHANGELOG.md`, 56 sites**, is release-owned and not edited.

## Mechanical guard: no code token moves

The guard compares the base blob with the working tree for all 16
touched files, with TypeScript 6.0.3, on two readings:

- **Reading 1:** the parser's leaf nodes, from a `forEachChild` walk.
Comments are trivia there, and JSDoc is never visited.
- **Reading 2:** the full token stream in parser context, from a
`getChildren` walk, with JSDoc nodes skipped.

String, template and numeric literals are compared in full on both
readings.

- **Real run at head `16b5bf6c1`:** 104,684 base tokens, **0 files with
a token change** (exit 0).
- **Controls,** each through `scripts/ablation-replace.mjs` in wrap
mode, under a shell trap that restores by absolute path from `HEAD`:
- comment (`bodies untouched` to `bodies UNTOUCHED`, `protocol.ts`): 0
files changed, exit 0;
- identifier (`const issues` to `const issuesX`,
`runtime-authoring-gate.dataset-writes.test.ts`): DIFFER on both
readings, exit 1;
- string (`'organization probe'` to `'organization probeX'`,
live-postgres test): DIFFER on both, exit 1;
- numeric (`WINDOWS_PROBED = 10` to `11`, prolongation test): DIFFER on
both, exit 1.
- **Each control landed:** anchor count went from 1 to 0 and the blob
changed. Each restore equals its `HEAD` blob (`766d14add061`,
`9c4bcfec727d`, `3457d6e431ed`, `9931944568bb`). `git diff HEAD` is
empty and the tree is clean afterwards.

## Changeset: none, with `skip-changeset` (`dist` measured)

`files[]` is `dist`, `README.md` and `CHANGELOG.md`. The `tsup` entry is
`src/index.ts`, and no test file is in its graph. For the one non-test
line (H3), the dependency closure was built first (`turbo run build
--filter='@objectstack/metadata-protocol^...'`, 12 of 12 tasks). Then
the package's own `build` ran four times under the shared verify lock,
and each leg hashed all 24 `dist` files:

| leg | state of `protocol.ts` | `dist` against leg 1 |
|---|---|---|
| 1 | head | (reference) |
| 2 | the base text put back on `:22793`, blob `5be50ab59075` (stage 1's
recorded head blob for this file) | **0 of 24 files differ**; the marker
`comment 5299845282` is in no built file |
| positive control | a docblock line on an exported member changed
(`refusal is being` to `refusal is BEING`) | `index.d.ts` and
`index.d.cts` differ; `ablation-dist-preflight` finds the marker in 2
built files |
| 3 | head again | 0 of 24 differ (deterministic);
`ablation-dist-preflight --absent` passes |

So a comment change that ships is visible to this instrument, and this
one does not ship. Restore proof: the `protocol.ts` blob equals `HEAD`
(`766d14add061`), `git diff HEAD` is empty and the tree is clean.

## Gates (head `16b5bf6c1`)

- **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --commands` derives 55 commands for 16 paths
against merge base `a7d9768ec`. All 55 ran, each exit captured before
any pipe, and **all 55 exit 0**. `--ran`, given `COMMAND :: exit CODE`
lines, reports 「55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN」, a derived
zero, and exits 0. A full `turbo run build` over `./packages/*` and
`./packages/*/*` ran first under the verify lock (71 of 71 tasks), so no
gate read an unbuilt workspace.
- **Named readings among them:**
- `node scripts/check-issue-citations.mjs` exits 0: 「no issue citations
added against a7d9768」.
- `pnpm check:issue-citations` exits 0: self-test, 173 cases, 9
batteries.
- `pnpm check:doc-authoring` exits 0, and the sibling-package prose-id
baseline holds with no growth.
- `pnpm check:nul-bytes` exits 0 (9,902 files). A raw control-byte scan
of the 16 changed files finds none (grep exit 1).
- **Tests and typecheck, under the verify lock:**
- `pnpm --filter @objectstack/metadata-protocol test`: 200 test files
pass and 3 skip (203); 2,973 tests pass and 19 skip. These are the same
totals as stage 1's.
- `pnpm --filter @objectstack/metadata-protocol typecheck` exits 0, and
`tsc --noEmit --listFiles` puts all 203 tracked test files and all 16
changed files in the program.
- **Lint, as a proven narrowing:** eslint runs with inline config off
(`--format json`) over the 16 touched files plus `dist/index.js` as the
control. It gives 17 results, 0 errors and 1 warning, the control's
ignore notice, and none of the 16 is reported ignored.
`eslint.config.mjs:327-328` states that no type-aware linting is
enabled, so a comment edit cannot move the verdict on an untouched file.
The repo-wide `pnpm lint` is CI's run.

## Acceptance notes

- **Base.** The branch is on `main` at `a7d9768ec`, the stage 1 squash.
`main` has moved two commits since (`1a4c7f826`, `ef96c9ede`). Neither
touches this package, `scripts/check-issue-citations.mjs` or
`scripts/pm/dispatch-gates.mjs`. No merge was taken; the merge queue
rebuilds on the merged generation.
- **File count.** The review recorded the 22 sites as 「21 lines in 15
test files」. Measured here: 21 lines in 14 files. The site and number
counts agree.
- **The same family in other packages, not edited.** Two other packages
carry similar test comments: `packages/rest` (`objectstack-ai#14389 §5` in
`error-response-sandbox-arm-message.test.ts` and
`rest-duplicate-record-arm.test.ts`) and `packages/lint` (`(objectstack-ai#10064)` in
`runtime-gate.object-writes.test.ts` and
`validate-object-field-refs.test.ts`). They are independent sites with
the same rot, outside this claim's file surface. Their lanes' anchors
are `10220a7bf` and `def0d3e63`.
- **The census tool was not re-run.** Its surface defers test files, so
this population is outside it by construction. The same extractor and
classifier, over the census's comment-prose projection of the package's
non-test files, read 0 dead both before and after.
- **Wording only:** 「the card」 / 「this card」 stands on many comment
lines in this package. It carries no number, and neither instrument sees
it. This diff removes one antecedent of it, and the rewrite repairs that
one in place (`protocol.ui-view-hidden-columns.test.ts:46`).

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

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants