Repository navigation
fix(cli): os migrate meta --out writes its snapshot on a run with nothing to migrate - #22121
Conversation
Red on the unfixed report: the human path's early return skips the --out write, while the --json path writes FILE. Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU Co-authored-by: Claude <noreply@anthropic.com>
…hing to migrate The human report's early return (no applied edit, no manual change) returned before the --out write, so --from 18 --to 18 --out FILE exited 0 with no snapshot line and no FILE, while --json wrote FILE. The write and its line move into one helper, called from both exits of the report, in the main path's order: snapshot, then --write's outcome, then the data migrations. Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 8 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 28 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 1fec15794505a37080e4593644c8ebb8e9b96fa2 && git checkout 1fec15794505a37080e4593644c8ebb8e9b96fa2
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f2a45db2ad6302cb669fe646df34941146917abb 0bd4955e8509c49094d868a0a0641ea57ee800af && git checkout -B drift-repro f2a45db2ad6302cb669fe646df34941146917abb && git merge --no-ff 0bd4955e8509c49094d868a0a0641ea57ee800af
node scripts/docs-audit/affected-docs.mjs --json f2a45db2ad6302cb669fe646df34941146917abb
|
Fixes #22116
Clause-②: no
What this changes
os migrate meta --from 18 --to 18 --out FILE(a range that crosses no step) exited 0, printed no snapshot line and wrote no FILE. The same command with--jsonwrote FILE.printMigrationReportreturns early when the chain applied no edit and listed no manual change, and that return came before the--outwrite.packages/cli/src/commands/migrate/meta.ts: the--outwrite and its line move into one helper,writeStackSnapshot(out, stack), called from both exits ofprintMigrationReport. On the early return it runs after the range answer and before--write's outcome and the data migrations. That is the main path's order: the snapshot after group ③, then--write, then the data migrations last.--jsonpath (it writes FILE on its own branch),--write,--stored/--applyand the chain..changeset/22116-migrate-meta-out-snapshot.md:@objectstack/clipatch.Measured before the change (base
a959493c)Source CLI (
packages/cli/bin/run-dev.js) over a one-object fixture project:--from 18 --to 18 --out h.jsonls: No such file)--from 18 --to 18 --out j.json --json--from 17 --to 18 --out c.json(control)306 manual change(s)j.jsonWhich ranges reach the early return
The early return is keyed on "nothing applied and nothing listed", not on "no step". So a range WITH steps over canonical metadata could in principle take its other arm (
Nothing to migrate). Measured ona959493c, no range does. Every major in the registry carries semantic entries (protocol 17: 77, protocol 18: 306), and the chain lists every entry of every hop it crosses, whatever the stack holds. Over a canonical stack, as hops / applied / todos:So today only an empty range reaches the early return. The change sits in the branch both arms share, so it covers both, and the second arm is pinned in-process over a real chain result with its notices taken away. The draft PR #22115 keeps
result.todoswhole, filters only what it lists, and does not touch the early-return condition, so that arm stays unreachable after it lands too.Pins
packages/cli/test/migrate-meta-out-snapshot.test.ts, unit tier (in-processMigrateMeta.runover a temp project that links the real@objectstack/spec; nothing is spawned and no kernel is booted):--from N --to Nfor N = 16, 17, 18, derived from the registry): the human mode exits 0, writes FILE and names it on exactly one line, after the range answer. The--jsonmode writes FILE too. The two FILEs are byte-identical, and FILE is the stack.--write, the snapshot line prints before--write's outcome.--from 17 --to 18writes FILE and names it after the manual-change header, and its bytes equal the--jsonmode's.printMigrationReporton both arms of the early return (an empty range, and a range with steps whose notices are taken away): FILE is written, and the snapshot line,--write's outcome and the data migrations print in that order.Red before, green after, and one ablation:
76439ebd(the pins, before the change): 7 failed, 1 passed (the control). The failures were FILE absent (ENOENT) or the stale bytes kept.0bd4955e: the new pin plusmeta.report-order.test.tsandmigrate-meta-write.test.ts, 3 files, 47 tests passed.0bd4955e, throughscripts/ablation-replace.mjsin wrap mode (trap restore). It deleted only the early-return callif (report.out) writeStackSnapshot(report.out, result.stack);(anchor 1 → 0, blob20cadb91c148→2542a9eca0f5). Result: 7 failed, 1 passed, with the control green. Restored: blob equal to HEAD20cadb91c148,git diff HEADempty.Verification (all on
0bd4955e)@objectstack/cliunit tier (vitest run --project unit --maxWorkers=2): 263 of 263 files, 3869 tests passed. The first full run reported 2 files failed withpackages/cli is not built (./dist/index.js is absent). That is a prerequisite, not a measurement, and both files passed (29 tests) afterpnpm build. The integration tier is declared to CI: the diff touches no integration-tier file and no spawn entry.pnpm --filter @objectstack/cli typecheck(tsc --noEmit, thencheck:test-typecheck): exit 0.tsc --listFilesshows the new test intsconfig.test.json's program.node scripts/pm/dispatch-gates.mjs --commandsderives for this diff exited 0. Four first exited 3 (PREREQUISITE NOT MET, an unbuilt workspace) and passed when re-run afterpnpm build:check:dual-build-cjs-loads,check:i18n,check:i18n-coverage,check:i18n-walk-parity. The--ranreconciliation, over recorded exit codes, reads 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN. The four artifact-roster gates whose roster sits under a changed path (check-changeset-fixed,check:authz-resolver,check:error-code-casing,check:filter-alias-parity) also exited 0.pnpm lint(full repository,eslint . --no-inline-config): exit 0, no findings.Acceptance notes
printMigrationReportnext to this region, inserting a group ④ call directly aboveif (report.out) {. This diff leaves thatifline byte-identical and changes only the two lines under it, so an unchanged line separates the two diffs. Whichever lands later mergesmain.Generated by Claude Code