Repository navigation
Commit 54ace18
fix(cli): os migrate meta --out writes its snapshot on a run with nothing to migrate (#22121)
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 `--json` wrote FILE. `printMigrationReport` returns early
when the chain applied no edit and listed no manual change, and that
return came before the `--out` write.
- `packages/cli/src/commands/migrate/meta.ts`: the `--out` write and its
line move into one helper, `writeStackSnapshot(out, stack)`, called from
both exits of `printMigrationReport`. 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.
- Why a helper rather than moving the write above the branch: above the
branch, the snapshot line would print before group ② on the main path
and change that path's order. The helper keeps one writer and leaves
every main-path line where it was.
- Untouched, per the ruling: the `--json` path (it writes FILE on its
own branch), `--write`, `--stored` / `--apply` and the chain.
- `.changeset/22116-migrate-meta-out-snapshot.md`: `@objectstack/cli`
patch.
## Measured before the change (base `a959493c`)
Source CLI (`packages/cli/bin/run-dev.js`) over a one-object fixture
project:
| run | exit | snapshot line | FILE |
|---|---|---|---|
| `--from 18 --to 18 --out h.json` | 0 | none | absent (`ls`: No such
file) |
| `--from 18 --to 18 --out j.json --json` | 0 | n/a | written, 663 bytes
|
| `--from 17 --to 18 --out c.json` (control) | 0 | printed after `306
manual change(s)` | written, byte-identical to `j.json` |
### Which 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 on
`a959493c`, 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:
- 16 → 16: 0 / 0 / 0; 17 → 17: 0 / 0 / 0; 18 → 18: 0 / 0 / 0
- 16 → 17: 1 / 0 / 77; 17 → 18: 1 / 0 / 306; 16 → 18: 2 / 0 / 383
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.todos` whole, 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-process `MigrateMeta.run` over a temp project that links the real
`@objectstack/spec`; nothing is spawned and no kernel is booted):
1. Every empty range the command accepts (`--from N --to N` for 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 `--json`
mode writes FILE too. The two FILEs are byte-identical, and FILE is the
stack.
2. A stale FILE left by an earlier run is overwritten.
3. With `--write`, the snapshot line prints before `--write`'s outcome.
4. Control: `--from 17 --to 18` writes FILE and names it after the
manual-change header, and its bytes equal the `--json` mode's.
5. `printMigrationReport` on 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:
- Red on `76439ebd` (the pins, before the change): 7 failed, 1 passed
(the control). The failures were FILE absent (`ENOENT`) or the stale
bytes kept.
- Green on `0bd4955e`: the new pin plus `meta.report-order.test.ts` and
`migrate-meta-write.test.ts`, 3 files, 47 tests passed.
- Ablation on `0bd4955e`, through `scripts/ablation-replace.mjs` in wrap
mode (trap restore). It deleted only the early-return call `if
(report.out) writeStackSnapshot(report.out, result.stack);` (anchor 1 →
0, blob `20cadb91c148` → `2542a9eca0f5`). Result: 7 failed, 1 passed,
with the control green. Restored: blob equal to HEAD `20cadb91c148`,
`git diff HEAD` empty.
## Verification (all on `0bd4955e`)
- `@objectstack/cli` unit tier (`vitest run --project unit
--maxWorkers=2`): 263 of 263 files, 3869 tests passed. The first full
run reported 2 files failed with `packages/cli is not built
(./dist/index.js is absent)`. That is a prerequisite, not a measurement,
and both files passed (29 tests) after `pnpm 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`, then
`check:test-typecheck`): exit 0. `tsc --listFiles` shows the new test in
`tsconfig.test.json`'s program.
- Gates: each of the 65 commands `node scripts/pm/dispatch-gates.mjs
--commands` derives for this diff exited 0. Four first exited 3
(`PREREQUISITE NOT MET`, an unbuilt workspace) and passed when re-run
after `pnpm build`: `check:dual-build-cjs-loads`, `check:i18n`,
`check:i18n-coverage`, `check:i18n-walk-parity`. The `--ran`
reconciliation, 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
- Neighbour: the draft PR #22115 edits `printMigrationReport` next to
this region, inserting a group ④ call directly above `if (report.out)
{`. This diff leaves that `if` line byte-identical and changes only the
two lines under it, so an unchanged line separates the two diffs.
Whichever lands later merges `main`.
- The snapshot line's text is unchanged. On the early return it follows
the blank line after the range answer, just as it follows the blank line
after group ③ on the main path.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent db4c45b commit 54ace18
3 files changed
Lines changed: 359 additions & 3 deletions
File tree
- .changeset
- packages/cli
- src/commands/migrate
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
251 | 251 | | |
252 | 252 | | |
253 | 253 | | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
254 | 267 | | |
255 | 268 | | |
256 | 269 | | |
| |||
526 | 539 | | |
527 | 540 | | |
528 | 541 | | |
529 | | - | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
530 | 546 | | |
| 547 | + | |
531 | 548 | | |
532 | 549 | | |
533 | 550 | | |
| |||
563 | 580 | | |
564 | 581 | | |
565 | 582 | | |
566 | | - | |
567 | | - | |
| 583 | + | |
568 | 584 | | |
569 | 585 | | |
570 | 586 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
0 commit comments