Skip to content

Commit 8598614

Browse files
docs(metadata): re-anchor the dead tracker citations to the commits that decided them (stage 6 of #20595) (#21396)
Part of #20595 Clause-②: no ## What changed Stage 6 of the `domain:engine` lane of the dead-citation sweep: `packages/metadata/**`, comment and docblock prose only, per the claim (`5949772544`). Stages 1 to 5 landed as `a7d9768ec`, `d150c3039`, `4bf4e7e70`, `13a24ece2` and `db0cf2231`; #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 `5749154545` on #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**: - **41 census sites** (41 lines, 12 files under `src/`): the whole `allocated-but-absent` population of the gate's own census in this package at the base, the slash-joined `#5108/#14423` at `ambiguous-metadata-stem.ts:40` from the post-landing census (`5923084795`) included; - **2 sites on 1 line outside the census glob, inside the claimed surface**: `tsup.config.ts:17` (`#14399/#14680` in a `//` comment), stage 1's `tsup.config.ts` precedent; - **32 test-comment sites** (32 lines, 11 test files), which the census defers. They carry 13 numbers: 10 the census itself reads as dead in this package's `src`, 1 it reads as dead elsewhere in the repository (#12914, in `metadata-core`), and 2 it never judges on this tree because they stand only in test files (#13072, #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.** `#6111` never meant this repository's issue: both sites read `objectui#6110 + #6111`, a pair of objectui issues whose second number the same test file spells `objectui#6111` at `:229`, so they now read `objectui#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 grep` over `docs/adr` and `scripts/adr-anchors` finds 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 `#6111` the same number, now qualified), and the diff-scoped gate judged the citations left on changed lines (see Gates). **A `patch` changeset**: 23 of the 42 rewritten non-test lines are in the published `dist` (the `.d.ts` keeps JSDoc on exported members, and esbuild keeps some comments in the JS), and `dist` is not byte-identical with the base text (see Changeset). ## H0: the largest remaining `domain:engine` package The gate's own `node scripts/check-issue-citations.mjs --census --json` at base `ee75aae1a` (the before run below), `allocated-but-absent` per package over every `domain:engine` family in `.claude/skills/pm-dispatch/SKILL.md`'s lane table: | package | sites | |---|---| | `metadata` | **41** | | `core` | 40 | | `platform-objects` | 27 | | `metadata-core` | 19 | | `drivers/driver-turso` | 14 | | `drivers/driver-mongodb` | 9 | | `formula` | 4 | | `metadata-fs` | 2 | | `metadata-protocol`, `objectql`, `plugins/plugin-pinyin-search`, `drivers/driver-memory`, `drivers/driver-sql`, `drivers/driver-sqlite-wasm` | 0 each | `metadata` is still the largest (lane total 156), so this stage went ahead. The margin is one site: `core` reads 40. ## Census: `metadata`, before and after **Instrument (A1).** The gate's own `node scripts/check-issue-citations.mjs --census --json`, read-only and unchanged. The count is its `allocated-but-absent` findings under `packages/metadata/`. | reading | tree | board | whole-repo `allocated-but-absent` | sites | lines | files | numbers | |---|---|---|---|---|---|---|---| | before | base `ee75aae1a`, run 10:00:40Z to 10:04:24Z | enumerated, 193 pages, frontier #21388, 19,209 records (newest number read before and after the run: #21388) | 271 | **41** | 41 | 12 | 15 | | after | `7f9836c0e`, run 10:17:35Z to 10:20:58Z | enumerated, 193 pages, frontier #21394, 19,215 records (newest before: #21392, after: #21394) | 230 | **0** | 0 | 0 | 0 | The 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) and `resolves-as-pull-request` (2,380) did not move; `cross-repo-unjudged` went 1,169 to 1,170, which is `plugin.ts:916`'s `objectui#6111`, now read as objectui's. The card's 41-site figure for `metadata` was stage 5's re-measure at `5fd4855a9`; the base here reads the same 41. The head's later commits are the changeset (outside the census surface) and one merge of `main` that touches no file under `packages/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 exported `extractCitations` (whole-file and comment-prose projections) over every tracked file in the package (100: 91 `.ts`, `package.json`, `tsconfig.json`, three `__fixtures__` artifact `.json` files, `README.md`, `ROADMAP.md`, `CHANGELOG.md` and `LICENSE`), and classifies each citation with the gate's `classifyCitation` against one board enumerated by the gate's `enumerateBoard` (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 `#5286` and `#12624` answer 200. | reading | citations | dead | src comment | test comment | test string | `ROADMAP.md` | changelog | |---|---|---|---|---|---|---|---| | before, `ee75aae1a` | 1,102 | **119** | 43 | 32 | 22 | 2 | 20 | | after, `7f9836c0e` | 1,029 | **44** | 0 | 0 | 22 | 2 | 20 | `src comment` here is the 41 census sites plus the 2 in `tsup.config.ts`. The citation count drops by 73: the 75 rewritten sites less the 2 `objectui#6111` respellings, 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}'` over `packages/metadata` (its `CHANGELOG.md` aside) finds no line (exit 1); the same grep over `packages/metadata-protocol` finds 13 lines, 5 of them `comment 5…` citations (control). So no comment id is in the population. ## Per-number table `src` counts the census sites plus the two in `tsup.config.ts`; `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 `ee75aae1a` (`git merge-base --is-ancestor`, exit 0 for all 17 shas; the clone is not shallow, `--is-shallow-repository` false). The `+` lines carry exactly these 17 nine-hex spans and no other: 16 are new, and `3ecb7dc1a` was 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 (`f4e7ae5c7` 11 added lines, `200d255e7` 2, `9e0ba21a1` 80, `4cda78c9b` 11, `efc5447ee` 5), 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 (`3ecb7dc1a` and `ac8ed476f`, where the dead number was that pull request's own). `git blame` at 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 example `c1d274de7` citing #14486 and #14423, `efc5447ee` citing #14486 and #14341, `4b4d5a331` citing #14341 and #14627, `3e7ef9c23` citing #14409), or are the two `#6111` sites `200d255e7` wrote. In each case the anchor is the commit that made the change the sentence credits to the number. `source` says whether another lane or stage already used this anchor for this number (`reused`) or it was measured here (`measured`). | number | src | test | anchor | kind | source | what it decided | |---|---|---|---|---|---|---| | `#6111` | 1 | 1 | `objectui#6111` | repository qualifier | reused (spec lane stage 5, `2123fcca3`, which read objectui's issues endpoint for 6111 at 200) | objectui's own issue: evaluate a form section's `visibleWhen` with the host predicate scope bound (`200d255e7`'s message), paired with `objectui#6110` in the same sentence | | `#10485` | 1 | 0 | `35ad101bc` | commit | reused (stages 1 and 3; the spec, qa, cli, rest and runtime lanes) | retire the `themes` carrier key and `ThemeSchema` (ADR-0049) | | `#12140` | 4 | 1 | `f4e7ae5c7` | commit | measured | gate the metadata HMR door on an explicit `NODE_ENV=development` posture; the route ledger's row moves with it | | `#12914` | 0 | 1 | `f887e5249` | commit | measured | a form SECTION `visibleWhen` binds `current_user` too: re-measure the stale contract text | | `#13072` | 0 | 1 | `200d255e7` | commit | measured | the unbound-root notice stops flagging a section-level `current_user` predicate (it inverted this very case in place) | | `#13135` | 1 | 2 | `9e0ba21a1` | commit | reused (the spec lane) | retire the paper metadata-customization protocol, the manager's overlay limb included (ADR-0049) | | `#13279` | 3 | 0 | `6a180e42d` | commit | reused (stage 4; the types, rest, runtime and services lanes) | move `isMissingTableError` / `isSchemaAlreadyExistsError` to `@objectstack/types`; its message records the maintainer's 2026-08-30 ruling | | `#13324` | 1 | 0 | `4cda78c9b` | commit | reused (stages 1 and 4; the types lane) | require a missing-table error to name the table that was read | | `#14341` | 3 | 5 | `2e471e362` | commit | measured | `FilesystemLoader` keys loader-held items by the name it can resolve (the squash of PR #14497) | | `#14399` | 3 | 0 | `3c1bbd2a8` | commit | reused (stage 3) | derive a view container's object through the shared `deriveViewContainerObject` | | `#14409` | 1 | 1 | `3ecb7dc1a` | commit | reused (stage 1; the rest lane), already on both lines | measure what each dialect materialises for a datetime JS cannot hold (that pull request's squash) | | `#14423` | 6 | 3 | `a56baa2bd` | commit | reused (stage 3; the runtime lane) | `listNames` gains `loadMany`'s per-loader fault parity, a keyed plural read lands beside `loadMany`, and the action audit reads the store key | | `#14486` | 6 | 11 | `4b4d5a331` | commit | measured | `FilesystemLoader.list()` reports only names `findFile()` can resolve; its message records the maintainer's 2026-09-02 ruling (option A) and is 「Part of #14486」 | | `#14627` | 1 | 2 | `ac8ed476f` | commit | measured | that pull request's squash (repair the package's hidden test-type errors and wire `typecheck`), the concurrent claim the sentences name | | `#14680` | 3 | 0 | `3bd9b3498` | commit | reused (stage 3) | the leaf `/view-container` subpath, measured on the built artifacts | | `#14921` | 8 | 2 | `c1d274de7` | commit | reused (the runtime lane) | refuse an ambiguous metadata stem with both paths named | | `#15037` | 1 | 1 | `efc5447ee` | commit | measured | `RemoteLoader.list()` no longer reports a nameless body as a literal `undefined` | | `#16657` | 0 | 1 | `5a95b0e93` | commit | reused (stages 1 and 4; the types lane) | read the dialect text out of `cause` for operator-facing records | ## 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 say more than the tag: - **`#6111`** (`plugin.ts:916`, `plugin-unbound-form-predicate-roots.test.ts:94`): 「objectui#6110 + #6111」 became 「objectui#6110 + objectui#6111」. `200d255e7`'s message, which wrote both sites, says 「objectui#6110 threads the host shell's predicate scope into `isSectionVisible`, and objectui#6111 evaluates the authored section `visibleWhen`」. - **`#14409`** (`database-loader.ts:82`, `database-loader-14078-invalid-date-total-arm.test.ts:19`): 「(#14409, landed `3ecb7dc1a`)」 became 「(commit `3ecb7dc1a`)」 and 「PR #14409 (landed `3ecb7dc1a`):」 became 「Commit `3ecb7dc1a`:」, stage 1's spelling for the same sentence: the dead pull-request number goes and the squash already beside it stays. - **`#14486` where it was written while that card was open** (`filesystem-loader-keyed-items.test.ts:42`, `:180`): `2e471e362` wrote them a day before `4b4d5a331`, which is 「Part of #14486」 and left this file's RECORD cases standing (the file was under a concurrent claim). A bare swap would credit `4b4d5a331` with inverting them, so they read 「the card commit 4b4d5a3 took in part」. - **Rulings the anchor's message does not carry, so the date stays and the site reads 「landed as」** (stage 1's `#9741` precedent): `#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 「同意」. - **Rulings the anchor's message does carry**: `#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 「#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`). - **The concurrent claim** (`filesystem-loader.ts:207`, `filesystem-loader-list-reachability.test.ts:51`, `:238`): 「(PR #14627)」 became 「(squashed as commit ac8ed47)」. `ac8ed476f` landed at 16:18Z on 2026-09-03, after `4b4d5a331` (14:30Z the same day), so the claim was open when this landed. Its final diff does not touch `filesystem-loader-keyed-items.test.ts`; the sentence says only that the file was under its claim. - **Where the number named the defect, not the change**: the title 「#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 #14423」 became 「the whole of the defect commit a56baa2 fixed」 (`metadata-manager.ts:2841`). - **Item references**: 「#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 #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」. - **Pins and RECORDs**: 「landed #14341 pin」 became 「landed pin of commit 2e471e3」, and 「three landed #14341 pins」 / 「the three landed #14341 pins」 became 「three pins commit 2e471e3 landed」 / 「the three pins commit 2e471e3 landed」; 「(#14486 RECORD)」 became 「(commit 4b4d5a3's RECORD)」, the RECORD cases that commit pinned. - **Slash pairs**: 「(#5108/#14423)」 became 「(#5108 and commit a56baa2)」; 「(#14341/#14205 fixed items going MISSING」 became 「(commit 2e471e3 and #14205 fixed …」; 「(#14399/#14680)」 became 「(commits 3c1bbd2 and 3bd9b34)」 (`tsup.config.ts:17`). #5108 and #14205 are live and stay. - **Capitalisation**: where the number opened a sentence, the new text opens with 「Commit」 (five sites), or with 「Items」, 「The defect」 or 「The card」 (one each). - No line was reflowed, so some are longer than 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 comments (src, test, `tsup.config.ts`): none.** - **String literals: 22 test-string sites, 7 numbers, 7 files** (`describe` and `it` titles, assertion arguments): `#14921` 5, `#14486` 4, `#12140` 3, `#15037` 3, `#16657` 3, `#14341` 2, `#14423` 2. 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 `:32` and `:173`).** Markdown prose, not a comment or docblock, and not in the package's `files` (`dist`, `README.md`, `CHANGELOG.md`), so outside this stage's surface; counted, not edited. Its anchor is in the table (`9e0ba21a1`). - **Outside `src`:** the release-owned `CHANGELOG.md` names dead numbers on 20 sites (16 numbers); left. `package.json`, `tsconfig.json`, the three fixture artifacts, `README.md` and `LICENSE` cite no dead number. ## Mechanical guard: no code token moves The guard (stages 2 to 5's) compares base `ee75aae1a` against the working tree at `7f9836c0e` over all 24 touched files, with TypeScript 6.0.3: - **Reading 1**: the parser's leaf nodes, from a `forEachChild` walk. Comments are trivia there, and JSDoc is never visited. A leaf that is not itself a token is re-scanned with trivia skipped. - **Reading 2**: the full token stream in parser context, from a `getChildren` walk, JSDoc nodes skipped. String, template and numeric literals are compared in full on both readings. Results: - Real run: 56,628 base tokens, **0 files with a token change** (exit 0). - Comment control (「Matched first, read second.」 to 「Matched FIRST」, `filesystem-loader.ts`): 0 files changed (exit 0). - Positive control, an identifier (`deriveViewContainerObject` to `deriveViewContainerObjectX`, `view-container.ts`): DIFFER on both readings (exit 1). - Positive control, a string literal (`'AMBIGUOUS_METADATA_STEM'` to `…X`, `ambiguous-metadata-stem.ts`): DIFFER on both readings (exit 1). - Positive control, a numeric literal (`err.status = 409` to `410`, `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 from `HEAD`. Each restore was proven equal to its `HEAD` blob (`9cb56cd6daac`, `1479bc09334e`, `7f0d7600dadb`, `8cf73463b771`), 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. In one script under the shared verify lock (VERDICT command-exit 0), at `7f9836c0e`: the dependency closure was built first (`turbo run build --filter='@objectstack/metadata^...'`, 9 of 9 tasks), then the package's own `build` (tsup plus `check-dts-emitted`) ran three times: - **Leg 1**, the head text: 30 `dist` files hashed. Of the 42 rewritten non-test lines, 23 appear verbatim in `dist`: 10 from `filesystem-loader.ts`, 5 from `metadata-manager.ts`, 3 from `ambiguous-metadata-stem.ts`, 2 each from `remote-loader.ts` and `view-container.ts`, 1 from `plugin.ts`; in `index` / `node` `.d.ts` / `.d.cts` and `.js` / `.cjs`, and `view-container.d.ts` / `.d.cts`. - **Leg 2**, the base text put back in the 13 non-test touched files (13 of 13 proven equal to their base blob): 10 of the 30 files differ from leg 1 (`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), and `scripts/ablation-dist-preflight.mjs` finds the base marker 「[#14921] The refusal a metadata source tree earns by naming one item twice.」 in 2 built files (exit 0). - **Leg 3**, after the proven restore (13 of 13 equal to their `HEAD` blob, `git diff HEAD` empty, porcelain empty): all 30 files are byte-identical to leg 1, and the preflight's `--absent` reading exits 0, so the build is deterministic and the difference is the rewrite. So the rewrite ships, and `.changeset/20595-metadata-provenance-anchors.md` declares a `patch` for `@objectstack/metadata`, comment text only, with the claim's `Clause-②: no` line. The later changeset commit and the merge of `main` touch no file under `packages/metadata`. ## Gates (head `61a589d48`) - **Derived gates:** `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` at `61a589d48` (25 paths against merge base `51550933d`, 165 changed lines) derived 65 commands. All 65 ran, each exit code captured before any pipe: 65 exit 0. `--ran` reports 「65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN」 (a derived zero) and exits 0. - **Roster families the derivation flags under a touched directory**, also run, each exit 0: `node scripts/check-changeset-fixed.mjs`, `pnpm check:authz-resolver`, `pnpm check:error-code-casing`, `pnpm check:filter-alias-parity`. - **Named readings:** `node scripts/check-issue-citations.mjs` exits 0 (「every citation this change adds resolves (or is a declared cross-repo reference)」: 3 judged across 12 files, 1 resolves, 2 cross-repo, the `objectui#6110` / `objectui#6111` pair on `plugin.ts:916`); `pnpm check:issue-citations` exits 0 (self-test, 173 cases, 9 batteries); `pnpm check:doc-authoring` exits 0 (the sibling-package prose-id baseline holds, no growth); `pnpm check:nul-bytes` exits 0 (9,684 files, no raw control bytes), and a control-byte grep over the 25 changed files finds none (exit 1). - **Build before the gates:** `turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2` at `61a589d48`, 71 of 71 tasks, VERDICT command-exit 0. - **Tests and typecheck, under the verify lock, at `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 --listFilesOnly` puts all 56 tracked test files and all 23 changed `src` files in that program; `tsup.config.ts` is outside it, and the token guard covers it. - **Lint, as a proven narrowing:** eslint with inline config disabled, over the 24 touched `.ts` files plus `dist/index.js` as the control: 25 results, 0 errors and 1 warning, the control's ignore notice; none of the 24 is reported ignored. `eslint.config.mjs` never 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-wide `pnpm lint` is CI's run. ## Acceptance notes - **Base.** The branch was cut at `ee75aae1a` and merged with `main` once, at `51550933d`, before the gates (merge `61a589d48`, no conflict; `main` brought one `packages/cli` commit and its changeset, and neither `check-issue-citations.mjs` nor `dispatch-gates.mjs`). `617f25f8a` (the last change under `packages/metadata` before this stage) is in the base (`merge-base --is-ancestor` exit 0; control `db0cf2231`, exit 0). The net diff against `main` is the 24 rewritten files (+74/−74) and the changeset (+17). - **`objectui#6111` was not read in this session.** This session's GitHub access does not reach `objectstack-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 in `2123fcca3`'s message for the same pair; `git ls-remote` on objectui shows no `refs/pull/6111/head` (control: `refs/pull/6112/head` is there), which fits an issue rather than a pull request. - **The margin over `core` is one site** (41 against 40 at the base). - **The same dead numbers stand in `packages/metadata-core`**: `#6111` on 3 census sites and `#12914` on 1, in its own stage of this card; this stage's `objectui#6111` respelling and `f887e5249` are there to reuse where those sentences say the same thing. - **Wording only:** no line without a number was changed. --- _Generated by [Claude Code](https://claude.ai/code/session_017xfMoEjKUuSh2xYB8sCozp)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ecb6ca0 commit 8598614

25 files changed

Lines changed: 91 additions & 74 deletions
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/metadata': patch
3+
---
4+
5+
Provenance comments in `@objectstack/metadata` cite the commits that decided them, not tracker numbers that no longer resolve
6+
7+
Clause-②: no
8+
9+
Docblocks and comments across the package cited issue-tracker numbers that now answer 404 on GitHub.
10+
Each now cites the commit in this repository's history that made the decision it describes, except two
11+
that meant an objectui issue and now spell `objectui#6111`. Some of these
12+
docblocks sit on exported members, so the reworded text appears in the published `index.d.ts` /
13+
`index.d.cts`, `node.d.ts` / `node.d.cts` and `view-container.d.ts` / `view-container.d.cts`, and
14+
comments that esbuild keeps appear in the JavaScript output (`index.js` / `index.cjs`, `node.js` /
15+
`node.cjs`).
16+
17+
Comment only: no export, type, error code, status, message text or runtime behaviour changes.

‎packages/metadata/src/errors.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* `@objectstack/spec/shared`). Architecturally attractive and explicitly
2525
* *not* precluded by this module — but out of scope on the round that
2626
* needed it (spec was frozen; types was under concurrent change).
27-
* ⇒ **TAKEN, by the maintainer's 2026-08-30 ruling on #13279.** The
27+
* ⇒ **TAKEN, by the maintainer's 2026-08-30 ruling (commit 6a180e42d).** The
2828
* predicate now lives in `@objectstack/types`
2929
* (`driver-error-classification.ts`); what forced it was a consumer this
3030
* file could never serve — `resolveAuthzContext` in `@objectstack/core`,
@@ -37,7 +37,7 @@
3737
* Option 2 is taken, so this is the compatibility seam it always said it would
3838
* become — "a single, greppable seam to delete if the maintainer later takes
3939
* option 2". It is NOT deleted: `@objectstack/metadata/errors` is a published
40-
* subpath with out-of-repo consumers, and #13279 is a fix, not a removal. It
40+
* subpath with out-of-repo consumers, and commit 6a180e42d is a fix, not a removal. It
4141
* re-exports the one symbol it always exported, from the new home. Everything
4242
* below still describes why the subpath exists and why it stays narrow.
4343
*

‎packages/metadata/src/index.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export { MemoryLoader } from './loaders/memory-loader.js';
1919
export { RemoteLoader } from './loaders/remote-loader.js';
2020
export { DatabaseLoader, type DatabaseLoaderOptions } from './loaders/database-loader.js';
2121

22-
// [#14921] The ambiguous-stem refusal. Published from the ROOT entry, not only
22+
// [commit c1d274de7] The ambiguous-stem refusal. Published from the ROOT entry, not only
2323
// from `./node` beside `FilesystemLoader`: the error reaches consumers through
2424
// `MetadataManager.listNames()` / `list()`, which live here, and a caller that
2525
// wants to tell "this deployment's metadata tree names one item twice" apart
@@ -54,7 +54,7 @@ export { TypeScriptSerializer } from './serializers/typescript-serializer.js';
5454

5555
// View container binding
5656
//
57-
// [#14399] `deriveViewContainerObject` is this package's ONE spelling of "which
57+
// [commit 3c1bbd2a8] `deriveViewContainerObject` is this package's ONE spelling of "which
5858
// object does an aggregated `defineView` container bind to" — the container's
5959
// own top-level `object` first, then `list.data.object`, `form.data.object`,
6060
// and the row's own `name` last (its own docblock carries the ruling). It is

‎packages/metadata/src/loaders/ambiguous-metadata-stem.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* [#14921] The refusal a metadata source tree earns by naming one item twice.
4+
* [commit c1d274de7] The refusal a metadata source tree earns by naming one item twice.
55
*
66
* ## The invariant this restores
77
*
@@ -21,7 +21,7 @@
2121
* diagnostic anywhere, and `MetadataManager.admitLoaderItems()`'s documented
2222
* "keep the first and say nothing" absorbs the collision a second time.
2323
*
24-
* ## The ruling (maintainer, via the director seat on #14921, 2026-09-05)
24+
* ## The ruling (maintainer, via the director seat, 2026-09-05; landed as commit c1d274de7)
2525
*
2626
* Option 1 of three: **refuse the ambiguous stem loudly at list time.** Two
2727
* files sharing a stem across the registered extensions is an AUTHORING ERROR,
@@ -37,7 +37,7 @@
3737
*
3838
* ## Why a brand and a predicate rather than bare `instanceof`
3939
*
40-
* `MetadataManager`'s plural reads catch per loader on purpose (#5108/#14423):
40+
* `MetadataManager`'s plural reads catch per loader on purpose (#5108 and commit a56baa2bd):
4141
* a storage outage must degrade to a short-but-served list rather than take the
4242
* whole enumeration down. This refusal is the opposite kind of fact — an
4343
* author's tree is malformed and no retry fixes it — so those seams have to

‎packages/metadata/src/loaders/database-loader-14078-invalid-date-total-arm.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*
1717
* ## Reachability is measured, not argued
1818
*
19-
* PR #14409 (landed `3ecb7dc1a`): mysql2 3.23.1 returns a module constant
19+
* Commit `3ecb7dc1a`: mysql2 3.23.1 returns a module constant
2020
* literally named `INVALID_DATE` for a zero `DATETIME`; postgres-date 1.0.7
2121
* builds `new Date(NaN)` for every year in 275760..294276, which Postgres
2222
* itself stores. Ruled option B by the maintainer on 2026-09-02, on all five

‎packages/metadata/src/loaders/database-loader.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import type { IDataDriver, IDataEngine, DriverQuery } from '@objectstack/spec/co
2727
import type { MetadataLoader, MetadataKeyedItem } from './loader-interface.js';
2828
import { calculateChecksum } from '../utils/metadata-history-utils.js';
2929
import { LRUCache } from '../utils/lru-cache.js';
30-
// [#13279] Both predicates moved to `@objectstack/types` — see its
30+
// [commit 6a180e42d] Both predicates moved to `@objectstack/types` — see its
3131
// `driver-error-classification.ts` `## Home` section. The verdicts are
3232
// byte-identical; only the import path changed.
3333
import { isMissingTableError, isSchemaAlreadyExistsError } from '@objectstack/types';
@@ -79,7 +79,7 @@ import { migrateProjectIdToEnvironmentId } from '../migrations/migrate-project-i
7979
* because a guard on some arms and not others re-opens the drift the single
8080
* spelling closed.
8181
*
82-
* Reachability is MEASURED, not assumed (#14409, landed `3ecb7dc1a`): mysql2
82+
* Reachability is MEASURED, not assumed (commit `3ecb7dc1a`): mysql2
8383
* 3.23.1 returns a module constant literally named `INVALID_DATE` for a zero
8484
* `DATETIME`, and postgres-date 1.0.7 builds `new Date(NaN)` for every year in
8585
* 275760..294276 — years Postgres itself stores. Unguarded,
@@ -940,7 +940,7 @@ export class DatabaseLoader implements MetadataLoader {
940940
* with its empty value.
941941
*/
942942
private rethrowUnlessTableUnprovisioned(error: unknown): void {
943-
// [#13324] Every caller of this helper reads `this.tableName`, so that is
943+
// [commit 4cda78c9b] Every caller of this helper reads `this.tableName`, so that is
944944
// the relation whose emptiness they are about to trust — a failure naming
945945
// any OTHER relation (a view over a dropped base table) is not evidence
946946
// about it and stays loud.

‎packages/metadata/src/loaders/filesystem-loader-ambiguous-stem.test.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* #14921 — two files sharing one stem are REFUSED, never resolved by
4+
* Commit c1d274de7 — two files sharing one stem are REFUSED, never resolved by
55
* extension precedence.
66
*
77
* ---------------------------------------------------------------------------
@@ -25,7 +25,7 @@
2525
* set.
2626
*
2727
* ---------------------------------------------------------------------------
28-
* The rule this pins (maintainer ruling on #14921, via the director seat,
28+
* The rule this pins (maintainer ruling, landed as commit c1d274de7, via the director seat,
2929
* 2026-09-05 — option 1 of three, verbatim 「同意」)
3030
* ---------------------------------------------------------------------------
3131
* Two files sharing a stem across the REGISTERED extensions is an authoring
@@ -123,7 +123,7 @@ beforeAll(async () => {
123123
await fs.writeFile(path.join(cleanDir, rel), body, 'utf-8');
124124
}
125125
// Nested twin of a FLAT name. Not a collision: a nested file is neither
126-
// listed nor resolvable (#14486), so it derives no name to collide with.
126+
// listed nor resolvable (commit 4b4d5a331), so it derives no name to collide with.
127127
await fs.writeFile(
128128
path.join(cleanDir, 'crm', 'solo.json'),
129129
JSON.stringify({ name: 'nested-solo' }),
@@ -242,7 +242,7 @@ describe('#14921 PIN 2 — one file per stem is still listed AND loadable', () =
242242

243243
it('CONTROL: a NESTED file sharing a flat name is not a collision', async () => {
244244
// `crm/solo.json` sits beside a flat `solo.json`. A nested file derives no
245-
// resolvable name at all (#14486), so there is nothing for it to collide
245+
// resolvable name at all (commit 4b4d5a331), so there is nothing for it to collide
246246
// with — and treating basenames as the unit would refuse this good tree.
247247
const loader = loaderFor(cleanRoot);
248248

@@ -280,15 +280,15 @@ describe('#14921 PIN 3 — the loadMany() two-body symptom is gone', () => {
280280
const bodies = await loaderFor(cleanRoot).loadMany<{ name?: string }>(TYPE);
281281

282282
// Flat AND nested, exactly as before: this walk was never narrowed to the
283-
// listed set (#14486 RECORD), and this card does not narrow it either.
283+
// listed set (commit 4b4d5a331's RECORD), and this card does not narrow it either.
284284
expect(bodies.map(b => b.name).sort()).toEqual(['alpha', 'beta', 'nested-solo', 'solo']);
285285
});
286286
});
287287

288288
describe('#14921 CONTROL — the refusal reads the REGISTERED extension set', () => {
289289
it('`dual.json` + `dual.js` is NOT ambiguous under the default set', async () => {
290290
// `.js` carries no registered serializer there, so it derives no name —
291-
// the same reason #14486 stopped listing it.
291+
// the same reason commit 4b4d5a331 stopped listing it.
292292
const loader = loaderFor(probeRoot);
293293

294294
expect(await loader.list(TYPE)).toEqual(['dual']);
@@ -349,7 +349,7 @@ describe('#14921 CONTROL — a storage OUTAGE still degrades, it is not swept up
349349
const manager = new MetadataManager({ formats: ['json'], loaders: [] });
350350
manager.registerLoader(new OutageLoader());
351351

352-
// Degraded, exactly as #14423 left it: an array, short and served.
352+
// Degraded, exactly as commit a56baa2bd left it: an array, short and served.
353353
expect(await manager.listNames(TYPE)).toEqual([]);
354354
});
355355

‎packages/metadata/src/loaders/filesystem-loader-keyed-items.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* #14341 — `FilesystemLoader.loadManyKeyed()`: a file-held item is keyed by the
4+
* Commit 2e471e362 — `FilesystemLoader.loadManyKeyed()`: a file-held item is keyed by the
55
* name this loader can actually RESOLVE for it, and by nothing else.
66
*
77
* ---------------------------------------------------------------------------
@@ -15,7 +15,7 @@
1515
* while `listDiagnosed()` called the short answer complete.
1616
*
1717
* ---------------------------------------------------------------------------
18-
* The rule this pins (PM ruling on #14341, 2026-09-02 — option D)
18+
* The rule this pins (PM ruling of 2026-09-02 — option D, landed as commit 2e471e362)
1919
* ---------------------------------------------------------------------------
2020
* An item is keyed by this loader's own name-to-path derivation (the basename
2121
* minus extension, the same derivation `list()` reports) ONLY where that
@@ -39,7 +39,7 @@
3939
* ---------------------------------------------------------------------------
4040
* `RECORD:` cases pin behaviour this ruling deliberately LEAVES ALONE — the
4141
* nested nameless file is still dropped. They exist so the derivation repair
42-
* (#14486: one shared name-to-path function for `list()`, `findFile()` and
42+
* (the card commit 4b4d5a331 took in part: one shared name-to-path function for `list()`, `findFile()` and
4343
* `loadManyKeyed()`) inverts them deliberately, with the change visible in a
4444
* diff, instead of silently.
4545
*
@@ -177,7 +177,7 @@ describe('#14341 FilesystemLoader.loadManyKeyed() keys by the resolvable name',
177177

178178
it('RECORD: a nested NAMELESS file is still dropped — the honest drop, #14486', async () => {
179179
// Not a repair this ruling makes: there is no name for it that any other
180-
// door reports. #14486 (one shared name-to-path derivation) is where this
180+
// door reports. The card commit 4b4d5a331 took in part (one shared name-to-path derivation) is where this
181181
// inverts, deliberately.
182182
const keyed = await loader().loadManyKeyed(TYPE);
183183

‎packages/metadata/src/loaders/filesystem-loader-list-reachability.test.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22

33
/**
4-
* #14486 — `FilesystemLoader.list()` reports only names the resolve trio can
4+
* Commit 4b4d5a331 — `FilesystemLoader.list()` reports only names the resolve trio can
55
* open: one shared name-to-path derivation for `list()`, `findFile()` and
66
* `loadManyKeyed()`.
77
*
@@ -23,7 +23,7 @@
2323
* author (human or AI) reads it as their own typo and retries the same word.
2424
*
2525
* ---------------------------------------------------------------------------
26-
* The rule this pins (maintainer ruling on #14486, via the director seat,
26+
* The rule this pins (maintainer ruling recorded in commit 4b4d5a331, via the director seat,
2727
* 2026-09-02 — option A, narrow)
2828
* ---------------------------------------------------------------------------
2929
* `list()` reports a file only where this loader's derivation is a bijection
@@ -47,8 +47,8 @@
4747
* divergence that remains is visible in a diff rather than implicit. Both of
4848
* them are the same half of the ruling — "a file that does not fit is neither
4949
* listed NOR resolvable" — which could not be taken here: each would invert a
50-
* landed #14341 pin in `filesystem-loader-keyed-items.test.ts`, a file under a
51-
* concurrent claim (PR #14627) when this landed.
50+
* landed pin of commit 2e471e362 in `filesystem-loader-keyed-items.test.ts`, a file under a
51+
* concurrent claim (squashed as commit ac8ed476f) when this landed.
5252
*
5353
* `CONTROL:` cases pin the things that must NOT move, and one of them
5454
* (`javascript` registered) is the reverse verification that the narrowing
@@ -232,10 +232,10 @@ describe('#14486 the repair reaches MetadataManager', () => {
232232
describe('#14486 RECORD: the half of the ruling this PR could not take', () => {
233233
it('RECORD: loadMany() still returns bodies for files list() no longer names', async () => {
234234
// The ruling also pinned "nothing unlisted is returned by `loadMany()`
235-
// either". Filtering the shared walk would invert three landed #14341 pins
235+
// either". Filtering the shared walk would invert three pins commit 2e471e362 landed
236236
// — `filesystem-loader-keyed-items.test.ts:113`, `:167`, `:187` — and its
237237
// `loadMany()` CONTROL at `:196` ("with every file", length 7), in a file
238-
// held by a concurrent claim (PR #14627). Recorded, not repaired.
238+
// held by a concurrent claim (squashed as commit ac8ed476f). Recorded, not repaired.
239239
const bodies = await loader().loadMany<{ name?: string; label?: string }>(TYPE);
240240

241241
expect(bodies).toContainEqual({ name: 'account', label: 'Account' });

0 commit comments

Comments
 (0)