Skip to content

test(cli): drive migrate audit-metadata-bodies in the --json stdout-purity family - #21394

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-21347-json-stdout-family
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-21347-json-stdout-family

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21347
Clause-②: no

What

packages/cli/test/json-stdout-purity.e2e.test.ts discovers its family from the source tree (every command whose comment-masked source calls bootSchemaStack( and declares a json: Flags.boolean( flag) and reconciles that set against FAMILY. os migrate audit-metadata-bodies (added by 336e191, PR #21144) joined the discovered set without joining FAMILY, so the nightly e2e tier went red on main on the reconciliation case.

This PR lists it in FAMILY with its bare argv ([]). The command's default is a read-only dry run, so the drive boots the stack with plugin-audit's objects registered, reads sys_audit_log / sys_activity, and writes nothing. The three per-member purity assertions now run against it: one JSON document on stdout through a bare JSON.parse, no kernel-logger record on stdout, and every boot diagnostic on stderr. discoverFamily is untouched, and the failing case keeps its name and body.

No command change was needed. Driven under --json, its stdout is exactly one document (measured below), so this PR does not touch packages/cli/src/commands/migrate/audit-metadata-bodies.ts, which has another change in flight.

Measurements

All readings are at this branch's single commit 105d266829, unless the table names another tree.

reading tree result
repro: OS_TEST_TIERS=nightly pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/json-stdout-purity.e2e.test.ts 43e928dd4c (origin/main, unmodified) 1 failed, 40 passed. AssertionError: expected [ 'meta resync', …(13) ] to deeply equal [ 'meta resync', …(12) ]; the extra member is migrate audit-metadata-bodies
the same command 105d266829 44 of 44 passed (41 plus this member's 3)
sibling test/config-miss-stdout-purity.e2e.test.ts, nightly tier 105d266829 174 of 174 passed
pnpm --filter @objectstack/cli typecheck (src plus the test layer) 105d266829 exit 0
pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 105d266829 244 files passed. Two published-subpath-*.pin files refused because packages/cli/dist was absent, which is a prerequisite refusal and not a measurement. After a cached full build, both re-ran green (29 of 29)

Direct drive of the member (a dry run against the uncompiled fixture and a fresh SQLite file): exit 0. Stdout is one JSON document: database, apply: false, a report with sys_audit_log and sys_activity each at 0 scanned / 0 rewritten, failures: 0, and duration. Stderr carries [StandaloneStack] no compiled artifact, Bootstrap complete, Graceful shutdown complete and the runner's own [stored-metadata-body-migration] would rewrite 0 of 0 … line.

Ablation

The fix was committed first (HEAD 105d266829). The mutation ran through node scripts/ablation-replace.mjs --anchor " 'migrate audit-metadata-bodies': []," --delete, wrapping the nightly command above:

  • Mutation: it landed on disk. The anchor count went from 1 to 0, and the blob changed from 2b249b4b8c54 to 4389d5638a10. A grep in the child counted 0 before the suite started.
  • Suite: 1 failed, 40 passed, with the original signature: expected [ 'meta resync', …(13) ] to deeply equal [ 'meta resync', …(12) ].
  • Restore: the blob after restore, 2b249b4b8c54, equals the HEAD blob. git diff HEAD and git status --porcelain are both empty.

Gates

I ran node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands with no paths. Its change set is this one file against merge base 43e928dd4. It derived 50 commands, which ran one after another at 105d266829, with each exit code recorded before any pipe.

  • Exit 0: 49 commands.
  • Exit 3: pnpm check:dual-build-cjs-loads (PREREQUISITE NOT MET, because 9 packages had no dist/). After a cached full build at the same HEAD, it re-ran with exit 0: 105 require entry points across 66 packages load.
  • --ran reconciliation over the first sweep: 50 derived, 49 run, 1 NOT-MEASURED (the line above), 0 UNRUN.

The derivation also printed a stale-tree note. Local origin/main had moved past the base and changed .github/workflows/release.yml, scripts/release-pending-publish.mjs and scripts/release-verify-npm.mjs. The workflow change adds one invocation of a release script inside a release job. No PR gate places it on this path.

Lint was narrowed to the one changed file. eslint --no-inline-config --format json reported 1 file, 0 errors and 0 warnings. That file is the population eslint's own --print-config matches (5 rules). The config enables no type-aware linting (no parserOptions.project, no projectService), so this diff cannot move any untouched file's verdict. The repo-wide pnpm lint belongs to CI.

Changeset

None. The diff is test-only, and @objectstack/cli publishes only dist, README.md and CHANGELOG.md.

Acceptance notes

  • The reconciliation case is pure source analysis, with no boot behind it. It still lives in a nightly-tier file, so the PR that added this member could not see it go red, and main found out only from the nightly. The pre-boot sibling (config-miss-stdout-purity.e2e.test.ts) has the same shape. Observation only, nothing filed. Carrier: none.

Generated by Claude Code

…-purity family

`os migrate audit-metadata-bodies` calls `bootSchemaStack(` and declares
`--json`, so the purity suite's source discovery already counts it, but
FAMILY did not list it and the nightly reconciliation case went red on
main. List it with its bare argv: the dry run is the command's default, so
the drive boots, reads sys_audit_log / sys_activity and writes nothing,
and the three per-member purity assertions now run against it.

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

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see

Coarse fallback — 0 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 51550933dbc5c7cbd89349a6beb5145363fc881d → packageMentionDocs.

@github-actions github-actions Bot added the tests label Oct 2, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 2, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 2, 2026 10:37
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 2, 2026 10:37
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 2, 2026
Merged via the queue into main with commit 56238d8 Oct 2, 2026
39 of 40 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21347-json-stdout-family branch October 2, 2026 10:55
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…hat decided them (stage 6 of objectstack-ai#20595) (objectstack-ai#21396)

Part of objectstack-ai#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`; objectstack-ai#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 objectstack-ai#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 `objectstack-ai#5108/objectstack-ai#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` (`objectstack-ai#14399/objectstack-ai#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
(objectstack-ai#12914, in `metadata-core`), and 2 it never judges on this tree because
they stand only in test files (objectstack-ai#13072, objectstack-ai#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.** `objectstack-ai#6111` never meant this repository's
issue: both sites read `objectui#6110 + objectstack-ai#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: objectstack-ai#15041 finds
ADR-0104). `ADR-0049`, already beside objectstack-ai#10485 and objectstack-ai#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 `objectstack-ai#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 objectstack-ai#21388, 19,209 records (newest number read before
and after the run: objectstack-ai#21388) | 271 | **41** | 41 | 12 | 15 |
| after | `7f9836c0e`, run 10:17:35Z to 10:20:58Z | enumerated, 193
pages, frontier objectstack-ai#21394, 19,215 records (newest before: objectstack-ai#21392, after:
objectstack-ai#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
objectstack-ai#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 `objectstack-ai#5286`
and `objectstack-ai#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 objectstack-ai#13074, whose two commits each name objectstack-ai#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 objectstack-ai#14486 and objectstack-ai#14423, `efc5447ee` citing objectstack-ai#14486 and
objectstack-ai#14341, `4b4d5a331` citing objectstack-ai#14341 and objectstack-ai#14627, `3e7ef9c23` citing
objectstack-ai#14409), or are the two `objectstack-ai#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 |
|---|---|---|---|---|---|---|
| `objectstack-ai#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 |
| `objectstack-ai#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) |
| `objectstack-ai#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 |
| `objectstack-ai#12914` | 0 | 1 | `f887e5249` | commit | measured | a form SECTION
`visibleWhen` binds `current_user` too: re-measure the stale contract
text |
| `objectstack-ai#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) |
| `objectstack-ai#13135` | 1 | 2 | `9e0ba21a1` | commit | reused (the spec lane) |
retire the paper metadata-customization protocol, the manager's overlay
limb included (ADR-0049) |
| `objectstack-ai#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 |
| `objectstack-ai#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 |
| `objectstack-ai#14341` | 3 | 5 | `2e471e362` | commit | measured |
`FilesystemLoader` keys loader-held items by the name it can resolve
(the squash of PR objectstack-ai#14497) |
| `objectstack-ai#14399` | 3 | 0 | `3c1bbd2a8` | commit | reused (stage 3) | derive a
view container's object through the shared `deriveViewContainerObject` |
| `objectstack-ai#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) |
| `objectstack-ai#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 |
| `objectstack-ai#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 objectstack-ai#14486」 |
| `objectstack-ai#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 |
| `objectstack-ai#14680` | 3 | 0 | `3bd9b3498` | commit | reused (stage 3) | the leaf
`/view-container` subpath, measured on the built artifacts |
| `objectstack-ai#14921` | 8 | 2 | `c1d274de7` | commit | reused (the runtime lane) |
refuse an ambiguous metadata stem with both paths named |
| `objectstack-ai#15037` | 1 | 1 | `efc5447ee` | commit | measured |
`RemoteLoader.list()` no longer reports a nameless body as a literal
`undefined` |
| `objectstack-ai#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:

- **`objectstack-ai#6111`** (`plugin.ts:916`,
`plugin-unbound-form-predicate-roots.test.ts:94`): 「objectui#6110 +
objectstack-ai#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`」.
- **`objectstack-ai#14409`** (`database-loader.ts:82`,
`database-loader-14078-invalid-date-total-arm.test.ts:19`): 「(objectstack-ai#14409,
landed `3ecb7dc1a`)」 became 「(commit `3ecb7dc1a`)」 and 「PR objectstack-ai#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.
- **`objectstack-ai#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 objectstack-ai#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 `objectstack-ai#9741` precedent): `objectstack-ai#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`);
`objectstack-ai#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**: `objectstack-ai#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 「objectstack-ai#14486 ruling (…)」 became 「ruling in
commit 4b4d5a3 (…)」 (`filesystem-loader.ts:203`); `objectstack-ai#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 objectstack-ai#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
「objectstack-ai#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
objectstack-ai#14423」 became 「the whole of the defect commit a56baa2 fixed」
(`metadata-manager.ts:2841`).
- **Item references**: 「objectstack-ai#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 objectstack-ai#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 objectstack-ai#14341 pin」 became 「landed pin of commit
2e471e3」, and 「three landed objectstack-ai#14341 pins」 / 「the three landed objectstack-ai#14341
pins」 became 「three pins commit 2e471e3 landed」 / 「the three pins
commit 2e471e3 landed」; 「(objectstack-ai#14486 RECORD)」 became 「(commit 4b4d5a3's
RECORD)」, the RECORD cases that commit pinned.
- **Slash pairs**: 「(objectstack-ai#5108/objectstack-ai#14423)」 became 「(objectstack-ai#5108 and commit
a56baa2)」; 「(objectstack-ai#14341/objectstack-ai#14205 fixed items going MISSING」 became 「(commit
2e471e3 and objectstack-ai#14205 fixed …」; 「(objectstack-ai#14399/objectstack-ai#14680)」 became 「(commits
3c1bbd2 and 3bd9b34)」 (`tsup.config.ts:17`). objectstack-ai#5108 and objectstack-ai#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): `objectstack-ai#14921` 5, `objectstack-ai#14486`
4, `objectstack-ai#12140` 3, `objectstack-ai#15037` 3, `objectstack-ai#16657` 3, `objectstack-ai#14341` 2, `objectstack-ai#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 (`objectstack-ai#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 「[objectstack-ai#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`**: `objectstack-ai#6111`
on 3 census sites and `objectstack-ai#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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nightly-tiers: red on main

2 participants