Skip to content

fix(cli): direction-aware version skew + release-train version lockstep#669

Merged
coderdan merged 3 commits into
mainfrom
fix/release-train-coupling
Jul 16, 2026
Merged

fix(cli): direction-aware version skew + release-train version lockstep#669
coderdan merged 3 commits into
mainfrom
fix/release-train-coupling

Conversation

@coderdan

@coderdan coderdan commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Why

Follow-up closing the one #666 review finding deliberately left out of that PR: post-GA staleness of the version embed. stash has no publishable dependency edge to the runtime packages, so a runtime-only release would not republish stash — the live CLI tarball would keep embedding (and init would keep pinning/recommending) outdated versions, and the skew warning would tell users on correctly-newer versions to downgrade (versionSkew was a bare !== with no direction).

What

1. Changesets fixed group — the whole release train: stash, @cipherstash/stack, @cipherstash/stack-drizzle, @cipherstash/stack-supabase, @cipherstash/prisma-next, @cipherstash/wizard version 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-run changeset version validated (whole train converges; pre-mode safe — verified against @changesets/assemble-release-plan's fixed-group handling).

2. Direction-aware skewversionSkew entries carry direction: '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 exactly digits.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.

  • behind (or unreadable/malformed manifest): align offer / pinned install command, as in fix(cli): pin init's installs to the release's own package versions (#661) #666.
  • ahead: never a downgrade — the warning prints the exact 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 update stash first (no more silently-manufactured cross-train mixes).
  • skills/stash-cli skew 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 init now distinguishes outdated and newer installed packages.
    • Outdated packages receive alignment and reinstall guidance.
    • Newer packages trigger an update recommendation for the stash CLI instead of a downgrade.
    • Missing packages are installed with the versions required by the current release, with mismatch warnings when necessary.
    • Related packages now release together at matching versions.
  • Documentation

    • Expanded guidance explains how to resolve version mismatches and when to update the CLI.

@coderdan
coderdan requested a review from a team as a code owner July 16, 2026 06:50
@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: daa25b8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 11 packages
Name Type
@cipherstash/prisma-next Patch
stash Patch
@cipherstash/wizard Patch
@cipherstash/prisma-next-example Patch
@cipherstash/basic-example Patch
@cipherstash/e2e Patch
@cipherstash/stack Patch
@cipherstash/stack-drizzle Patch
@cipherstash/stack-supabase Patch
@cipherstash/bench Patch
@cipherstash/test-kit Patch

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

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e64eaf86-0d13-4456-b4d9-507052ce49a0

📥 Commits

Reviewing files that changed from the base of the PR and between 7d1226a and daa25b8.

📒 Files selected for processing (10)
  • .changeset/config.json
  • .changeset/prisma-next-joins-release-train.md
  • .changeset/release-train-coupling.md
  • .changeset/wizard-joins-release-train.md
  • packages/cli/src/__tests__/release-train.test.ts
  • packages/cli/src/__tests__/runtime-versions.test.ts
  • packages/cli/src/commands/init/steps/__tests__/install-deps.test.ts
  • packages/cli/src/commands/init/steps/install-deps.ts
  • packages/cli/src/runtime-versions.ts
  • skills/stash-cli/SKILL.md

📝 Walkthrough

Walkthrough

Changes

Release train alignment

Layer / File(s) Summary
Release-train version comparison
packages/cli/src/runtime-versions.ts, packages/cli/src/__tests__/runtime-versions.test.ts
Adds strict release and prerelease version comparison, with coverage for precedence and malformed versions.
Direction-aware init dependency handling
packages/cli/src/commands/init/steps/install-deps.ts, packages/cli/src/commands/init/steps/__tests__/install-deps.test.ts, skills/stash-cli/SKILL.md, .changeset/release-train-coupling.md
Classifies installed packages as behind or ahead, warns against downgrading newer packages, and continues installing missing packages with release-pinned versions.
Fixed release-train configuration and validation
.changeset/config.json, .changeset/*release-train*.md, packages/cli/src/__tests__/release-train.test.ts
Defines the fixed package group, adds release metadata for Prisma Next and Wizard, and verifies the group matches the CLI release-train manifests.

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
Loading

Possibly related PRs

Suggested reviewers: calvinbrewer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main changes: direction-aware version skew handling and release-train lockstep versioning.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/release-train-coupling

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 init dependency installation step to only offer “align” actions for behind packages, and emit a distinct warning for ahead packages (update stash, never downgrade runtime deps).
  • Couple the release-train packages into a Changesets fixed group 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.

Comment thread packages/cli/src/runtime-versions.ts
@coderdan
coderdan marked this pull request as draft July 16, 2026 07:11
@coderdan
coderdan marked this pull request as ready for review July 16, 2026 07:18
coderdan added 2 commits July 16, 2026 17:19
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
@coderdan
coderdan force-pushed the fix/release-train-coupling branch from 960efda to 83ec7ea Compare July 16, 2026 07:19
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
@coderdan

Copy link
Copy Markdown
Contributor Author

Human and AI review (Copilot, Claude/Fable).

@coderdan
coderdan merged commit d307603 into main Jul 16, 2026
10 checks passed
@coderdan
coderdan deleted the fix/release-train-coupling branch July 16, 2026 07:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants