fix(cli): direction-aware version skew + release-train version lockstep#669
Conversation
🦋 Changeset detectedLatest commit: daa25b8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (10)
📝 WalkthroughWalkthroughChangesRelease train alignment
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant StashInit
participant InstallDepsStep
participant PackageManifests
User->>StashInit: run init
StashInit->>InstallDepsStep: inspect release-train packages
InstallDepsStep->>PackageManifests: read installed versions
PackageManifests-->>InstallDepsStep: package versions
InstallDepsStep-->>User: show alignment or CLI update guidance
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR hardens the stash init version-pinning workflow by (1) preventing “downgrade” guidance when a project already has newer runtime packages installed, and (2) ensuring the CLI’s embedded runtime-version map can’t go stale by locking the release-train packages to a single Changesets fixed version line.
Changes:
- Add a lightweight semver-precedence comparator to classify version skew as “behind” vs “ahead”.
- Update
stash initdependency installation step to only offer “align” actions for behind packages, and emit a distinct warning for ahead packages (updatestash, never downgrade runtime deps). - Couple the release-train packages into a Changesets
fixedgroup and add a changeset describing the behavior change; expand unit/E2E coverage for the new “ahead” path.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/cli/src/runtime-versions.ts | Adds compareVersions helper for prerelease-aware ordering used by skew direction logic. |
| packages/cli/src/commands/init/steps/install-deps.ts | Introduces direction-aware skew classification and warning text; only “behind” skew gets alignment handling. |
| packages/cli/src/commands/init/steps/tests/install-deps.test.ts | Updates mocks to retain real helpers; adds coverage asserting “ahead” never triggers downgrade/align commands. |
| packages/cli/src/tests/runtime-versions.test.ts | Adds a comparator precedence matrix for compareVersions. |
| .changeset/release-train-coupling.md | Documents the direction-aware skew behavior and the new lockstep release-train coupling. |
| .changeset/config.json | Defines a Changesets fixed group for the release-train packages to enforce lockstep versioning. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Closes the remaining #666 review finding: post-GA, a stack-only release would not republish stash (stack is only a devDep + in-range optional peer), leaving the published CLI's embedded version map stale — fresh installs would pin outdated versions and the skew warning would print downgrade commands to users on legitimately newer versions. Two-part fix: 1. Changesets `fixed` group over the release train (stash, stack, both adapters, wizard): a release of any republishes all at the same version, so the embed can never disagree with what's live. prisma-next stays on its own line by design. NOTE: wizard joins the group from 0.5.0-rc.1 and will land on the group version (1.0.0-rc.2) at the next `changeset version`. 2. Direction-aware skew: versionSkew classifies behind vs ahead using a small prerelease-aware comparator (compareVersions — deliberately not a full semver impl; no new dependency per the supply-chain policy). Behind (and unreadable) installs get the align offer as before; an install NEWER than this release expects gets "update stash to the matching release" and never a downgrade command. Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
…head" Copilot review catch on #669: compareVersions parsed core segments with Number.parseInt and NaN !== NaN made any malformed version (v1.0.0, 1.0.x, garbage from a corrupt manifest) deterministically compare as GREATER — classifying it 'ahead' and suppressing the align/reinstall guidance exactly where it's needed. Guard the parse: either side with a NaN core returns 0 (not comparable), which callers classify as 'behind' — the safe direction. Tests at both layers (comparator + versionSkew classification). Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
960efda to
83ec7ea
Compare
Seven findings from the code-review pass, all addressed:
1. compareVersions gets a strict shape gate (^\d+\.\d+\.\d+ with optional
well-formed prerelease; build metadata rejected): empty prerelease
('1.0.0-'), '+metadata', truncated/extra cores, and parseInt trailing
garbage ('9.9.10rc') all previously slipped the NaN-only guard and could
classify a malformed install as 'ahead', suppressing the reinstall
guidance. One gate now encodes the whole not-comparable policy; the NaN
check is subsumed. Test matrix covers every class.
2. Mixed ahead+missing no longer silently manufactures a cross-train
mismatch: when missing packages are about to be installed beside newer
ones, the warning says the pairing may not match and to update stash
first.
3. The ahead warning prints the exact update command (devInstallCommand of
stash@<highest installed>) — lockstep guarantees that release exists —
instead of an uncommanded "matching release".
4. @cipherstash/prisma-next JOINS the fixed group (per Dan: the prisma-next
package should line up to the train version too). The changeset's
"can never go stale" claim is now actually true — no residual exclusion.
5. release-train.test.ts gains the growth guard: the changesets fixed group
must equal RELEASE_TRAIN_MANIFESTS exactly, so a future train package
can't silently version outside the lockstep.
6. skills/stash-cli skew sentence is direction-aware (align when older;
update stash — never downgrade — when newer), fixing the now-wrong
unconditional "align versions before continuing".
7. Companion changesets for @cipherstash/wizard and @cipherstash/prisma-next
so their CHANGELOGs explain the version-line jump onto the train.
Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
|
Human and AI review (Copilot, Claude/Fable). |
Why
Follow-up closing the one #666 review finding deliberately left out of that PR: post-GA staleness of the version embed.
stashhas no publishable dependency edge to the runtime packages, so a runtime-only release would not republishstash— the live CLI tarball would keep embedding (andinitwould keep pinning/recommending) outdated versions, and the skew warning would tell users on correctly-newer versions to downgrade (versionSkewwas a bare!==with no direction).What
1. Changesets
fixedgroup — the whole release train:stash,@cipherstash/stack,@cipherstash/stack-drizzle,@cipherstash/stack-supabase,@cipherstash/prisma-next,@cipherstash/wizardversion in lockstep: any release republishes all, so the embed can never go stale against the packages it pins. prisma-next joins per Dan (its version lines up with the train too); a new test asserts fixed group == RELEASE_TRAIN_MANIFESTS exactly, so a future train package can't silently version outside the lockstep. Companion changesets give wizard (0.5.x→train) and prisma-next (0.4.x→train) CHANGELOG entries explaining the version-line jump.changeset status+ a dry-runchangeset versionvalidated (whole train converges; pre-mode safe — verified against @changesets/assemble-release-plan's fixed-group handling).2. Direction-aware skew —
versionSkewentries carrydirection: 'behind' | 'ahead'via a small prerelease-aware comparator (no new dependency, per the supply-chain policy), now guarded by a strict shape gate: anything not exactlydigits.digits.digits[-well-formed-prerelease](build metadata included) is not comparable → classified behind → safe reinstall guidance. This closes the whole malformed-version family (1.0.0-,+sha,1.0,1.0.0beta,9.9.10rc), not just NaN cores.npm install -D stash@<installed>(lockstep guarantees that release exists) and, when missing packages are about to be installed beside newer ones, says the pairing may not match and to updatestashfirst (no more silently-manufactured cross-train mixes).skills/stash-cliskew guidance is now direction-aware to match (rule 8).Tests
497 unit + 58 e2e green. Comparator matrix covers every malformed class; ahead/mixed paths asserted end-to-end; fixed-group growth guard in
release-train.test.ts.After merge
Rebase #655 onto this so CJ's prisma-next work rides the same train version.
https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
Summary by CodeRabbit
New Features
stash initnow distinguishes outdated and newer installed packages.stashCLI instead of a downgrade.Documentation