Repository navigation
fix(cli): os migrate meta --write rewrites the declared protocol range the load still refuses - #22252
Conversation
…he load refuses Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU
…grate-meta-write-range Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU
📓 Docs Drift CheckThis PR changes 1 package(s): 19 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 8 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. 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 9da99fe0cde7b100a2f38c6ecea7ee2563d2e9de && git checkout 9da99fe0cde7b100a2f38c6ecea7ee2563d2e9de
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 73a0a6bf1db3216a0c82bee1503011040c6a1394 9578a2af27c67222a3e543a6de7341e0bec25385 && git checkout -B drift-repro 73a0a6bf1db3216a0c82bee1503011040c6a1394 && git merge --no-ff 9578a2af27c67222a3e543a6de7341e0bec25385
node scripts/docs-audit/affected-docs.mjs --json 73a0a6bf1db3216a0c82bee1503011040c6a1394
|
Fixes #22219
Clause-②: no
What changed
os migrate meta --from N --writenow rewrites the manifest's declared protocol range when the load would still refuse the migrated source under it. Before, it wrote the chain's mechanical edits and leftengines: { protocol: '^16' }as it was, so the load refusal that names this command as its remedy came straight back.--to, capped at the protocol this runtime implements, in the scaffold's spelling ('^17'on this build). See H5 for why the cap is needed.engines.protocol, elseengines.platform, else the legacyengine.objectstack). See H2.'>=16'), an absent or unparsable range, and a range at or above the target (a range is never lowered). A range that--fromstarts above is also left, with a line naming the--fromthat would move it.--writewould make.--jsonreports the edit aswrite.range, a NEW key (see H6).Measured on
main7b926f76(H1, premise holds)The CLI was built from that tree and each load went through
os serve, with--from 16:^16plus a tombstonedviews[0].list.striped.✗ views.0.list.striped: … Run os migrate meta --from 16 …--writereportedWrote 1 of 1 mechanical change(s) into 1 file(s). The reload was then refused:✗ package 'com.example.h1' targets protocol ^16 (engines.protocol) but this runtime is protocol 17.0.0. This is a major-version break. Run: objectstack migrate meta --from 16^16with nothing to convert.--writereported--write: the chain made no mechanical change here, so no file was written.The reload returned the same refusal.The PM's readings, measured
enginesorengine).engines.platform: '^16'andengine.objectstack: '^16.0.0'both parse).engines.protocolis absent. Both are refused withmigrateCommand: objectstack migrate meta --from 16.engine.objectstack: '^17'is refused at parse (invalid_format, because the schema wants a full version). That home is therefore written'^17.0.0'.RangeRewriteinput toplanAuthoredSourceWrite. It joins as the last entry and is reported apart (plan.range).verifyAuthoredSourceWritetakes the range the written sources still owe:The restore case is pinned. Its error reads
still converted: manifest.engines.protocol (declared protocol range).no file was written). The range edit is now written alone, and the report says so.os init, thecreate-objectstackblank template andos lint's fix all write'^' + PROTOCOL_MAJOR, and that form is used here. A range that already admits the target ('>=16') is pinned as a no-op.--to: on this build--todefaults to 18 (the chain's terminus) while the runtime implements 17.'^18'is refused by this runtime. Measured throughos serve:targets protocol ^18 … Run: objectstack migrate meta --from 18. So the written major ismin(--to, PROTOCOL_MAJOR):--toabove the runtime writes the runtime's major, and--tobelow it writes--to.--json. The edit does not fit the existingwrite.written/write.manuallists. Those list exactly the chain'sappliedentries, an invariant already pinned (writes the applied set and nothing else), and the range is not one of them. It is reported as a new key,write.range:status,path,from,to, plusfile/linewhen written orkind/reasonwhen left. Per the claim, the seat amends the Clause-② line if this key needs it; line 2 above isnoas dispatched. A dry run's--jsoncarries no range, because that would be a new top-level key.Pins (
packages/cli/test/migrate-meta-protocol-range.test.ts, unit tier)The pins run in-process over
MigrateMeta.run, and each reload goes through the load's own door: the CLI'sloadConfig(whosedefineStackruns the schema), thenAppPlugin.init(whose handshake runs before the manifest registers).--from 16with a converted key:STACK_SCHEMA_INVALID, namingmigrate meta --from 16).--writerewrites the range and the key, and only those bytes change.--from 16with nothing to convert:OS_PROTOCOL_INCOMPATIBLE, status 422,migrateCommand: objectstack migrate meta --from 16).--writewrites the range alone and says so.'>=16'is left byte for byte, andwrite.rangeis absent.engines.platformandengine.objectstackare rewritten in place, and each reload is not refused.--tois capped at the runtime's major. A--fromabove the range is left, and the report names--from 16. The planner never lowers a range, and never touches an absent, unparsable, admitted or non-string range.shared) while the chain's edit beside it is written.SHAPESrow:Ablation (one-off,
meta.tsrestored to a blob equal to HEADecbcd924,git diff HEADempty)The test imports
meta.tsrelatively, so it resolves to source and no rebuild applies. Each mutation went throughscripts/ablation-replace.mjs, which reportedanchor 1 → 0and a changed blob. Both legs ran with a trap that restores from HEAD.rangeargument toplanAuthoredSourceWritewas deleted.ProtocolIncompatibleError … targets protocol ^16 (engines.protocol) but this runtime is protocol 17.0.0 … Run: objectstack migrate meta --from 16.Verification (at
9578a2af,mainf4bed583merged in)vitest list --project unit).@objectstack/cliunit project: 264 files, 3888 tests passed.migrate metaand codemod files (7 files): 75 passed, plus 1 pre-existingskipIfskip.pnpm --filter @objectstack/cli typecheck: exit 0. Thetsc --noEmitandcheck:test-typechecksteps both pass, and the new test is intsconfig.test.json's program.pnpm lint(fulleslint . --no-inline-config): exit 0.dispatch-gates --ran: 65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN. The list is identical to the dispatched one.check:dual-build-cjs-loadsandcheck:i18n-coveragefirst exited 3 (PREREQUISITE NOT MET: dist absent for packages outside the CLI closure). Re-run once those dist trees existed, both exit 0.Acceptance notes
content/docs/upgrading.mdx(the--writerow and callout) andskills/objectstack-data(--write applies the proven sites, landed in docs(skills): objectstack-data says whatos migrate meta --from 16does: lists the edits,--writeapplies the proven sites #22199) do not mention the range rewrite. Both are outside this card's file surface (content/docs) or a governed surface (skills/**).--outsnapshot. The--outsnapshot (result.stack) keeps the authored range. The docs already call it an oracle to diff against, not a file to ship.handshakeSliceinmeta.tsrepeats the type narrowing thattoHandshakeManifestinutils/protocol-version-gap.tsdoes. That file is not on this card's surface, so it was not exported from there.retiredFromLoadPath: false, e.g.driver: 'mongo') in a stack the schema otherwise accepts is converted bydefineStackduring the authored-source load. The chain therefore never sees it, and--writenever writes it. Measured:--from 16 --write --jsongivesapplied: []while the source keeps'mongo'and the load warns. Reported to the PM; not changed here.Generated by Claude Code