docs(metadata): re-anchor the dead tracker citations to the commits that decided them (stage 6 of #20595) - #21396
Conversation
…hat decided them (stage 6 of #20595) Every comment or docblock site in packages/metadata that cited a tracker number answering 404 now cites the commit in this repository that decided it (ruling C+D, form C), or, for objectui's #6111, spells that repository's qualifier: 75 sites on 74 lines in 24 files, 18 numbers. Comment prose only; every file keeps its line count. Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
…docblocks reach dist Clause-②: no Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 17 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1a26c3f08e3b33b1480977521d8417c40bf9c7e1 && git checkout 1a26c3f08e3b33b1480977521d8417c40bf9c7e1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 56238d890d3a6d2fe65a20ecb53fc4f15e9d316d 61a589d48036aeb129c4f23137495ecfae7a0377 && git checkout -B drift-repro 56238d890d3a6d2fe65a20ecb53fc4f15e9d316d && git merge --no-ff 61a589d48036aeb129c4f23137495ecfae7a0377
node scripts/docs-audit/affected-docs.mjs --json 56238d890d3a6d2fe65a20ecb53fc4f15e9d316d
|
Part of #20595
Clause-②: no
What changed
Stage 6 of the
domain:enginelane of the dead-citation sweep:packages/metadata/**, comment and docblock prose only, per the claim (5949772544). Stages 1 to 5 landed asa7d9768ec,d150c3039,4bf4e7e70,13a24ece2anddb0cf2231; #20595 stays open for the next stage.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
5749154545on #19123): the ADR when one records the decision, otherwise the commit in this repository's history that made it. That is 75 sites on 74 lines in 24 files, covering 18 numbers:src/): the wholeallocated-but-absentpopulation of the gate's own census in this package at the base, the slash-joined#5108/#14423atambiguous-metadata-stem.ts:40from the post-landing census (5923084795) included;tsup.config.ts:17(#14399/#14680in a//comment), stage 1'stsup.config.tsprecedent;src, 1 it reads as dead elsewhere in the repository (Four morecurrent_userbinding-text sites are stale — including the form SECTION slot, whose verdict flipped when objectui#6110 / #6111 landed #12914, inmetadata-core), and 2 it never judges on this tree because they stand only in test files (Three more consumers of the "nocurrent_userat section level" claim go stale the moment #12914 lands — one of them is a live runtime detector, not prose #13072, Raw-exec consumers that surfaceerror.messageas an operator-facingdetailnow read the composed DATABASE_ERROR sentence — readcausethere (follow-up to #16019) #16657), which the board and a single read each settle.No comment-id citation stands in the package (see Census).
Anchors: 17 numbers by commit, 0 by ADR, 0 by words alone, and 1 respelled with its repository.
#6111never meant this repository's issue: both sites readobjectui#6110 + #6111, a pair of objectui issues whose second number the same test file spellsobjectui#6111at:229, so they now readobjectui#6110 + objectui#6111, the exact respelling the spec lane gave the same pair (2123fcca3). 11 numbers reuse the anchor (or respelling) another lane or stage already used for them; 7 were measured here. 17 distinct shas. No ADR or ruling record names any of the 18 (git grepoverdocs/adrandscripts/adr-anchorsfinds none of them; control: #15041 finds ADR-0104).ADR-0049, already beside #10485 and #13135, stays.Only comments changed. Every file keeps its line count (74 lines out, 74 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: on every changed line, the numbers on the new text are a subset of those on the old (for
#6111the same number, now qualified), and the diff-scoped gate judged the citations left on changed lines (see Gates).A
patchchangeset: 23 of the 42 rewritten non-test lines are in the publisheddist(the.d.tskeeps JSDoc on exported members, and esbuild keeps some comments in the JS), anddistis not byte-identical with the base text (see Changeset).H0: the largest remaining
domain:enginepackageThe gate's own
node scripts/check-issue-citations.mjs --census --jsonat baseee75aae1a(the before run below),allocated-but-absentper package over everydomain:enginefamily in.claude/skills/pm-dispatch/SKILL.md's lane table:metadatacoreplatform-objectsmetadata-coredrivers/driver-tursodrivers/driver-mongodbformulametadata-fsmetadata-protocol,objectql,plugins/plugin-pinyin-search,drivers/driver-memory,drivers/driver-sql,drivers/driver-sqlite-wasmmetadatais still the largest (lane total 156), so this stage went ahead. The margin is one site:corereads 40.Census:
metadata, before and afterInstrument (A1). The gate's own
node scripts/check-issue-citations.mjs --census --json, read-only and unchanged. The count is itsallocated-but-absentfindings underpackages/metadata/.allocated-but-absentee75aae1a, run 10:00:40Z to 10:04:24Z7f9836c0e, run 10:17:35Z to 10:20:58ZThe whole-repo drop is 41, and the two finding sets differ by exactly the 41 rows of this package, removed; none was added.
resolves(34,962) andresolves-as-pull-request(2,380) did not move;cross-repo-unjudgedwent 1,169 to 1,170, which isplugin.ts:916'sobjectui#6111, now read as objectui's. The card's 41-site figure formetadatawas stage 5's re-measure at5fd4855a9; the base here reads the same 41. The head's later commits are the changeset (outside the census surface) and one merge ofmainthat touches no file underpackages/metadata.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 exportedextractCitations(whole-file and comment-prose projections) over every tracked file in the package (100: 91.ts,package.json,tsconfig.json, three__fixtures__artifact.jsonfiles,README.md,ROADMAP.md,CHANGELOG.mdandLICENSE), and classifies each citation with the gate'sclassifyCitationagainst one board enumerated by the gate'senumerateBoard(193 pages, frontier #21390, 19,211 records, 10:05:02Z to 10:08:30Z). Every one of the 18 numbers in the population was then read on its own over the issues endpoint (10:09Z): all 18 answer 404, and the lit controls#5286and#12624answer 200.ROADMAP.mdee75aae1a7f9836c0esrc commenthere is the 41 census sites plus the 2 intsup.config.ts. The citation count drops by 73: the 75 rewritten sites less the 2objectui#6111respellings, which are still citations and now classify as cross-repo (src comment 3 to 4, test comment 7 to 8). The live counts did not move (src comment: 277 resolve, 7 as pull requests; test comment: 275 and 13). A third, raw reading (every#followed by 2 to 6 digits, whatever surrounds it) counts 1,111 before and 1,038 after: a drop of 73, for the same reason.Comment ids. No comment-id citation stands in the package:
git grep -E '[0-9]{10}'overpackages/metadata(itsCHANGELOG.mdaside) finds no line (exit 1); the same grep overpackages/metadata-protocolfinds 13 lines, 5 of themcomment 5…citations (control). So no comment id is in the population.Per-number table
srccounts the census sites plus the two intsup.config.ts;testcounts test-comment sites. Every sha below matches exactly one commit (git rev-parse --disambiguate, count 1) and is an ancestor of the baseee75aae1a(git merge-base --is-ancestor, exit 0 for all 17 shas; the clone is not shallow,--is-shallow-repositoryfalse). The+lines carry exactly these 17 nine-hex spans and no other: 16 are new, and3ecb7dc1awas already on both of its lines. The message or the diff of each one names the number it replaces: 9 in the message and the diff (35ad101bc,6a180e42d,2e471e362,3c1bbd2a8,a56baa2bd,4b4d5a331,3bd9b3498,c1d274de7,5a95b0e93), 5 in the diff alone (f4e7ae5c711 added lines,200d255e72,9e0ba21a180,4cda78c9b11,efc5447ee5), 1 in the message alone (f887e5249, the squash of PR #13074, whose two commits each name #12914), and 2 only in the subject's squash suffix (3ecb7dc1aandac8ed476f, where the dead number was that pull request's own).git blameat the base puts 49 of the 75 sites on their anchor. The other 26 were written by a later commit that cites the number as an earlier change (for examplec1d274de7citing #14486 and #14423,efc5447eeciting #14486 and #14341,4b4d5a331citing #14341 and #14627,3e7ef9c23citing #14409), or are the two#6111sites200d255e7wrote. In each case the anchor is the commit that made the change the sentence credits to the number.sourcesays whether another lane or stage already used this anchor for this number (reused) or it was measured here (measured).#6111objectui#61112123fcca3, which read objectui's issues endpoint for 6111 at 200)visibleWhenwith the host predicate scope bound (200d255e7's message), paired withobjectui#6110in the same sentence#1048535ad101bcthemescarrier key andThemeSchema(ADR-0049)#12140f4e7ae5c7NODE_ENV=developmentposture; the route ledger's row moves with it#12914f887e5249visibleWhenbindscurrent_usertoo: re-measure the stale contract text#13072200d255e7current_userpredicate (it inverted this very case in place)#131359e0ba21a1#132796a180e42disMissingTableError/isSchemaAlreadyExistsErrorto@objectstack/types; its message records the maintainer's 2026-08-30 ruling#133244cda78c9b#143412e471e362FilesystemLoaderkeys loader-held items by the name it can resolve (the squash of PR #14497)#143993c1bbd2a8deriveViewContainerObject#144093ecb7dc1a#14423a56baa2bdlistNamesgainsloadMany's per-loader fault parity, a keyed plural read lands besideloadMany, and the action audit reads the store key#144864b4d5a331FilesystemLoader.list()reports only namesfindFile()can resolve; its message records the maintainer's 2026-09-02 ruling (option A) and is 「Part of #14486」#14627ac8ed476ftypecheck), the concurrent claim the sentences name#146803bd9b3498/view-containersubpath, measured on the built artifacts#14921c1d274de7#15037efc5447eeRemoteLoader.list()no longer reports a nameless body as a literalundefined#166575a95b0e93causefor operator-facing recordsWordings to check
Most rewrites swap a tag in place (
[#N]to[commit SHA],(#N)to(commit SHA),#N's Xtocommit SHA's X), the form the landed stages use. These say more than the tag:#6111(plugin.ts:916,plugin-unbound-form-predicate-roots.test.ts:94): 「objectui#6110 + v17 GA 交付物:AI 一键升级客户元数据项目(升级 skill,骑在 D2 conversions 之上) #6111」 became 「objectui#6110 + objectui#6111」.200d255e7's message, which wrote both sites, says 「objectui#6110 threads the host shell's predicate scope intoisSectionVisible, and objectui#6111 evaluates the authored sectionvisibleWhen」.#14409(database-loader.ts:82,database-loader-14078-invalid-date-total-arm.test.ts:19): 「(test(driver-sql): measure what each dialect materialises for a datetime JS cannot hold (#14078) #14409, landed3ecb7dc1a)」 became 「(commit3ecb7dc1a)」 and 「PR test(driver-sql): measure what each dialect materialises for a datetime JS cannot hold (#14078) #14409 (landed3ecb7dc1a):」 became 「Commit3ecb7dc1a:」, stage 1's spelling for the same sentence: the dead pull-request number goes and the squash already beside it stays.#14486where it was written while that card was open (filesystem-loader-keyed-items.test.ts:42,:180):2e471e362wrote them a day before4b4d5a331, which is 「Part of finding(metadata): FilesystemLoader.list() reports names that findFile()/load()/exists() cannot resolve — nested paths and extension-less files are listed but unreachable, and .js/.ts files are listed but never loaded #14486」 and left this file's RECORD cases standing (the file was under a concurrent claim). A bare swap would credit4b4d5a331with inverting them, so they read 「the card commit 4b4d5a3 took in part」.#9741precedent):#14341(2e471e362's message states the rule, not the 2026-09-02 PM ruling) became 「PM ruling of 2026-09-02 — option D, landed as commit 2e471e3」 (filesystem-loader-keyed-items.test.ts:18) and 「PM ruling of 2026-09-02, landed as commit 2e471e3」 (filesystem-loader.ts:181);#14921(c1d274de7's message states the refusal, not the 2026-09-05 ruling) became 「(maintainer, via the director seat, 2026-09-05; landed as commit c1d274d)」 (ambiguous-metadata-stem.ts:24) and 「(maintainer ruling, landed as commit c1d274d, via the director seat,」 (filesystem-loader-ambiguous-stem.test.ts:28), whose next line keeps the date and the verbatim 「同意」.#14486's (4b4d5a331: 「Ruled by the maintainer via the director seat (2026-09-02) as option A over the reverse-unify」) reads 「recorded in commit 4b4d5a3」 on four sites (filesystem-loader-list-reachability.test.ts:26,filesystem-loader.ts:368,remote-loader-list-nameless.test.ts:42,remote-loader.ts:130), and 「finding(metadata): FilesystemLoader.list() reports names that findFile()/load()/exists() cannot resolve — nested paths and extension-less files are listed but unreachable, and .js/.ts files are listed but never loaded #14486 ruling (…)」 became 「ruling in commit 4b4d5a3 (…)」 (filesystem-loader.ts:203);#13279's (6a180e42d: 「Maintainer ruling 2026-08-30, verbatim: 第一批其余同意」) reads 「the maintainer's 2026-08-30 ruling (commit 6a180e4)」 (errors.ts:27).filesystem-loader.ts:207,filesystem-loader-list-reachability.test.ts:51,:238): 「(PR fix(metadata): repair the 89 hidden test-type errors and wire atypecheckscript #14627)」 became 「(squashed as commit ac8ed47)」.ac8ed476flanded at 16:18Z on 2026-09-03, after4b4d5a331(14:30Z the same day), so the claim was open when this landed. Its final diff does not touchfilesystem-loader-keyed-items.test.ts; the sentence says only that the file was under its claim.RemoteLoader.list()declaresPromise<string[]>but maps a nameless body straight through, solistNames()can return a literalundefinedas a name #15037 —RemoteLoader.list()declares … and maps a nameless body straight through」 became 「The defect commit efc5447 fixed — …」 (remote-loader-list-nameless.test.ts:4); 「that axis is the whole of [finding] Two residual audit ↔ router asymmetries after #14123: the audit's third source isloadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423」 became 「the whole of the defect commit a56baa2 fixed」 (metadata-manager.ts:2841).loadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 items 1 and 2」 became 「Items 1 and 2 of the card commit a56baa2 answered」 (metadata-manager-keyed-plural-read.test.ts:4), and 「the [finding] Two residual audit ↔ router asymmetries after #14123: the audit's third source isloadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423 cost question (the ruling's item 6)」 became 「the cost question commit a56baa2 answered (the ruling's item 6)」 (:356);a56baa2bd's commits include 「pin the by-name rung's per-name cost — one findOne per probe」.DatabaseLoader把存储读故障吞成空结果 —— ADR-0110 D3 的 miss/outage 之分在复数读路径上不成立 #5108/[finding] Two residual audit ↔ router asymmetries after #14123: the audit's third source isloadManywhile the router's isloadby name, andunboundDeclarationsstill reads two sources where the undeclared-handler half now reads three #14423)」 became 「(DatabaseLoader把存储读故障吞成空结果 —— ADR-0110 D3 的 miss/outage 之分在复数读路径上不成立 #5108 and commit a56baa2)」; 「(metadata: FilesystemLoader and RemoteLoader still discard the store key in loadMany(), so a nameless body stays invisible to list() through those two loaders #14341/metadata: readListUncached() drops every loader-held item whose stored body has no top-level name — an aggregated view container is invisible to list() after a restart #14205 fixed items going MISSING」 became 「(commit 2e471e3 and metadata: readListUncached() drops every loader-held item whose stored body has no top-level name — an aggregated view container is invisible to list() after a restart #14205 fixed …」; 「(The two SOURCE view-container registrars disagree on where the row's ownnamesits in the object-derivation chain #14399/[finding]engine.tsreachesderiveViewContainerObjectthrough@objectstack/metadata's ROOT entry, so objectql's lean ADR-0076core.tsclosure now pulls MetadataPlugin, chokidar, glob and js-yaml for a six-line pure function #14680)」 became 「(commits 3c1bbd2 and 3bd9b34)」 (tsup.config.ts:17).DatabaseLoader把存储读故障吞成空结果 —— ADR-0110 D3 的 miss/outage 之分在复数读路径上不成立 #5108 and metadata: readListUncached() drops every loader-held item whose stored body has no top-level name — an aggregated view container is invisible to list() after a restart #14205 are live and stay.eslint.config.mjsdeclares no line-length rule, and a reflow would move neighbouring lines and every line citation into the file).Sites left
tsup.config.ts): none.describeandittitles, assertion arguments):#149215,#144864,#121403,#150373,#166573,#143412,#144232. Every one of the 7 is in this stage's population, so the table above holds an anchor for each. Non-test strings carry none. Strings are outside this stage's surface.ROADMAP.md: 2 sites (#13135, at:32and:173). Markdown prose, not a comment or docblock, and not in the package'sfiles(dist,README.md,CHANGELOG.md), so outside this stage's surface; counted, not edited. Its anchor is in the table (9e0ba21a1).src: the release-ownedCHANGELOG.mdnames dead numbers on 20 sites (16 numbers); left.package.json,tsconfig.json, the three fixture artifacts,README.mdandLICENSEcite no dead number.Mechanical guard: no code token moves
The guard (stages 2 to 5's) compares base
ee75aae1aagainst the working tree at7f9836c0eover all 24 touched files, with TypeScript 6.0.3:forEachChildwalk. Comments are trivia there, and JSDoc is never visited. A leaf that is not itself a token is re-scanned with trivia skipped.getChildrenwalk, JSDoc nodes skipped. String, template and numeric literals are compared in full on both readings.Results:
filesystem-loader.ts): 0 files changed (exit 0).deriveViewContainerObjecttoderiveViewContainerObjectX,view-container.ts): DIFFER on both readings (exit 1).'AMBIGUOUS_METADATA_STEM'to…X,ambiguous-metadata-stem.ts): DIFFER on both readings (exit 1).err.status = 409to410,metadata-manager.ts): DIFFER on both readings (exit 1).Each mutation went through
scripts/ablation-replace.mjs(wrap mode, anchor hit 1 to 0, replacement 0 to 1) under a shell trap that restores by absolute path fromHEAD. Each restore was proven equal to itsHEADblob (9cb56cd6daac,1479bc09334e,7f0d7600dadb,8cf73463b771), withgit diff HEADempty and a clean tree afterwards.Changeset:
patch(distmeasured)files[]isdist,README.mdandCHANGELOG.md, and the package is not private. In one script under the shared verify lock (VERDICT command-exit 0), at7f9836c0e: the dependency closure was built first (turbo run build --filter='@objectstack/metadata^...', 9 of 9 tasks), then the package's ownbuild(tsup pluscheck-dts-emitted) ran three times:distfiles hashed. Of the 42 rewritten non-test lines, 23 appear verbatim indist: 10 fromfilesystem-loader.ts, 5 frommetadata-manager.ts, 3 fromambiguous-metadata-stem.ts, 2 each fromremote-loader.tsandview-container.ts, 1 fromplugin.ts; inindex/node.d.ts/.d.ctsand.js/.cjs, andview-container.d.ts/.d.cts.index.js,index.cjs,index.d.ts,index.d.cts,node.js,node.cjs,node.d.ts,node.d.cts,view-container.d.ts,view-container.d.cts; no sourcemap moves), andscripts/ablation-dist-preflight.mjsfinds the base marker 「[finding(metadata): FilesystemLoader.list() reports one name TWICE when two files share a stem across registered extensions, and only the first-precedence file is ever reachable #14921] The refusal a metadata source tree earns by naming one item twice.」 in 2 built files (exit 0).HEADblob,git diff HEADempty, porcelain empty): all 30 files are byte-identical to leg 1, and the preflight's--absentreading exits 0, so the build is deterministic and the difference is the rewrite.So the rewrite ships, and
.changeset/20595-metadata-provenance-anchors.mddeclares apatchfor@objectstack/metadata, comment text only, with the claim'sClause-②: noline. The later changeset commit and the merge ofmaintouch no file underpackages/metadata.Gates (head
61a589d48)node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat61a589d48(25 paths against merge base51550933d, 165 changed lines) derived 65 commands. All 65 ran, each exit code captured before any pipe: 65 exit 0.--ranreports 「65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived zero) and exits 0.node scripts/check-changeset-fixed.mjs,pnpm check:authz-resolver,pnpm check:error-code-casing,pnpm check:filter-alias-parity.node scripts/check-issue-citations.mjsexits 0 (「every citation this change adds resolves (or is a declared cross-repo reference)」: 3 judged across 12 files, 1 resolves, 2 cross-repo, theobjectui#6110/objectui#6111pair onplugin.ts:916);pnpm check:issue-citationsexits 0 (self-test, 173 cases, 9 batteries);pnpm check:doc-authoringexits 0 (the sibling-package prose-id baseline holds, no growth);pnpm check:nul-bytesexits 0 (9,684 files, no raw control bytes), and a control-byte grep over the 25 changed files finds none (exit 1).turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2at61a589d48, 71 of 71 tasks, VERDICT command-exit 0.61a589d48(one command joined by&&, VERDICT command-exit 0):pnpm --filter @objectstack/metadata test: 56 test files pass (56), 836 tests pass (836);pnpm --filter @objectstack/metadata typecheck(tsc --noEmit) exits 0.tsc --listFilesOnlyputs all 56 tracked test files and all 23 changedsrcfiles in that program;tsup.config.tsis outside it, and the token guard covers it..tsfiles plusdist/index.jsas the control: 25 results, 0 errors and 1 warning, the control's ignore notice; none of the 24 is reported ignored.eslint.config.mjsnever enables type-aware linting (its lines 327 and 328 say so), so a comment edit cannot move the verdict on an untouched file. The repo-widepnpm lintis CI's run.Acceptance notes
ee75aae1aand merged withmainonce, at51550933d, before the gates (merge61a589d48, no conflict;mainbrought onepackages/clicommit and its changeset, and neithercheck-issue-citations.mjsnordispatch-gates.mjs).617f25f8a(the last change underpackages/metadatabefore this stage) is in the base (merge-base --is-ancestorexit 0; controldb0cf2231, exit 0). The net diff againstmainis the 24 rewritten files (+74/−74) and the changeset (+17).objectui#6111was not read in this session. This session's GitHub access does not reachobjectstack-ai/objectui(its proxy answers 403 for the issues and pulls endpoints there, and the web page answers 403 for a control number too, so neither reading means anything). The 200 reading is the spec lane's, recorded in2123fcca3's message for the same pair;git ls-remoteon objectui shows norefs/pull/6111/head(control:refs/pull/6112/headis there), which fits an issue rather than a pull request.coreis one site (41 against 40 at the base).packages/metadata-core:#6111on 3 census sites and#12914on 1, in its own stage of this card; this stage'sobjectui#6111respelling andf887e5249are there to reuse where those sentences say the same thing.Generated by Claude Code