Repository navigation
fix(create-objectstack, cli): scaffolded projects pin every @objectstack/* dependency to the exact scaffolder version - #25539
Conversation
…he exact scaffolder version create-objectstack, os init and os create (standalone) wrote ^<version> for every @objectstack/* dependency of a new project, the range the v18 docs say can move a project onto a breaking release. They now write the scaffolder's own version exactly, prereleases verbatim; os create --in-repo keeps workspace:*. Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…he exact @objectstack/spec version Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…at collection time Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 7 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 36 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 bc8b13bbdf1d4e24225afe8d03ed2b1c6c44cef3 && git checkout bc8b13bbdf1d4e24225afe8d03ed2b1c6c44cef3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 74496c64b0637103f87cceec3f37932a48813e65 3a678c53a7c71a4c4e13e183f0ba3fbafa64f7d8 && git checkout -B drift-repro 74496c64b0637103f87cceec3f37932a48813e65 && git merge --no-ff 3a678c53a7c71a4c4e13e183f0ba3fbafa64f7d8
node scripts/docs-audit/affected-docs.mjs --json 74496c64b0637103f87cceec3f37932a48813e65
|
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Sales · project thread
Part of #25510 (the producer half). The card stays open for two halves this PR does not carry, named under "What is left" below.
Clause-②: no
Metadata-protocol: none
What changed
Every project scaffolded by
npm create objectstack,os initoros create(default standalone placement) now lists each@objectstack/*dependency at the EXACT version of the tool that wrote it. Before, it got^VERSION. A prerelease is written verbatim (18.1.0-rc.2). The v18 docs say a^18.0.0range "can therefore move you to a release that breaks you. Pin exact versions" (content/docs/releases/v18/index.mdx:129-130).packages/create-objectstack/src/pkg-utils.tssyncObjectStackDeps^VERSIONVERSIONpackages/cli/src/commands/init.tspkgVersion()^VERSIONVERSIONpackages/cli/src/commands/create.tsobjectstackDependencySpec('standalone')^VERSIONVERSIONos create --in-repoworkspace:*workspace:*(unchanged)Third-party ranges (
typescript,vitest,zod,@types/node) are not touched. Every scaffolded@objectstack/*name is in.changeset/config.json'sfixedgroup, so the scaffolder's own version is published for each one. The changeset ispatchoncreate-objectstackand@objectstack/cli, and it carries the hand fix for existing projects.What is left on #25510
packages/create-objectstack/src/templates/blank/package.json) stays^18.0.0on purpose. The release rewrite that stamps it isscripts/sync-template-versions.mjs, which is outside this card's file surface. It runs in the rootversionscript, afterchangeset version, and incut-rc.yml. It sets every@objectstack/*entry of every template'spackage.jsontorange=`^${major}.0.0`(loadScaffolderVersion(), about line 270). The samerangealso feeds the manifest'sspecVersion. If this PR wrote an exact baseline, the next version pass would put^MAJOR.0.0back, and an exact-form ratchet intemplate-consistency.test.tswould then fail on the post-version tree. So the baseline, its ratchet (template-consistency.test.ts:122-139, "must be ^MAJOR.x") and the rewriter have to move together, in a change whose surface includesscripts/sync-template-versions.mjsand its--self-testfixtures. That change has to split the deps value (the exactversion, prerelease included) fromspecVersion, which is a protocol range and should stay one. Users are not exposed in the meantime:syncObjectStackDepsoverwrites the baseline at scaffold time whenever the scaffolder's version is readable. The testleaves no @objectstack/* range in the scaffolded blank templatepins that.content/docs/protocol/kernel/plugin-spec.mdx:719,723(a pluginpackage.jsonexample with^17.0.0) and:741-746("the scaffolders caret-pin each dependency"), pluscontent/docs/upgrading.mdx:81-82("a project on the scaffold's default ranges (^17.0.0)").Tests
Measured on this branch (head
3a678c53a7for the final union; package runs cited per commit):pnpm --filter create-objectstack test: Test Files 18 passed (18), Tests 268 passed (268) (at95c59d7dbe; the create-objectstack tree is unchanged since).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2: Test Files 327 passed (327), Tests 4811 passed (4811) (at4f417d70e9).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 test/scaffold-exact-objectstack-pin.test.ts test/init.test.ts test/create.test.ts: 3 passed (3), 73 passed (73) (final test content).pnpm --filter create-objectstack typecheckexit 0;pnpm --filter @objectstack/cli typecheckexit 0 (tsc --noEmit && pnpm check:test-typecheck).What is pinned:
18.1.0-rc.2), and the real blank template through the rewrite (template-consistency.test.ts).runtime-image.test.tsnow uses EQUALITY between the Dockerfile tag and the emitted pin, plus a prerelease scaffold.satisfiesCaretand its battery are gone, because no producer writes a caret any more.scaffold-skills-single-copy.test.tsreads the exact form.os initandos create(standalone): exact form at this checkout's version (init.test.ts,create.test.ts). The newtest/scaffold-exact-objectstack-pin.test.tsimports the commands under a planted18.1.0-rc.2manifest and checks every template of both commands, with--in-repoworkspace:*as the control.Ablation, one per producer, through
scripts/ablation-replace.mjsin wrap mode. Each mutation put the caret back. Each restore was proven by blob == HEAD and an emptygit diff HEAD. These tests importsrc/, so no build or dist leg applies.init.tspkgVersion(): 4 failed / 53 passed (init.test.ts×3 templates and the prerelease file).create.tsstandalone: 2 failed / 18 passed.pkg-utils.ts: 6 failed / 53 passed (template-consistency.test.ts×3,runtime-image.test.ts×3).Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 70 commands. The same 70 were re-derived at3a678c53a7and run there, together withpnpm lint. Results are in the report comment on #25510. CI owns every other family.Acceptance notes
packages/create-objectstack/src/index.ts:37and:586("the template carries a caret range"), andpackages/create-objectstack/src/runtime-image.ts:20-27(the generatedpackage.json"carries a caret RANGE"). The resolve-after-install logic is still correct with exact pins: the resolved version now equals the pin.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX