Skip to content

fix(release): make MARKETING_VERSION the only version, and gate it in both pipelines - #267

Merged
sarthakagrawal927 merged 1 commit into
mainfrom
fix/version-single-source
Sep 6, 2026
Merged

sarthakagrawal927 merged 1 commit into
mainfrom
fix/version-single-source

Conversation

@sarthakagrawal927

Copy link
Copy Markdown
Member

Fixes the red CI on main and closes the recurring release-pipeline bug behind it. Follow-up to #253 / SAR-52.

The immediate break

#266 bumped only apps/macos/Config/Shared.xcconfig to 1.13.4. The three Rust crates, the =<version> pins between them, and Cargo.lock stayed at 1.13.3, so CI on main fails closed:

CodeVetter CLI qualification failed: CLI version mismatch:
  expected "codevetter 1.13.4", received "codevetter 1.13.3"

Why a script instead of another hand-bump

This is the third occurrence. 72b6b60b fixed the identical drift at 1.13.0 by hand-bumping and cutting forward as 1.13.1 — it patched the instance, not the cause. The version lived in four hand-edited places, so the next cut that touched one of them brought it straight back.

Red CI is the lucky symptom. 72b6b60b recorded the dangerous one in its own commit message and left it open: core:qualify-cli runs only in ci.yml, never in the release qualification path. A drifted cut could sign, notarize, staple, and publish an app whose bundled codevetter and codevetter-mcp reported the previous version — with every release gate green.

The change

scripts/sync-native-version.mjs makes MARKETING_VERSION the single source of truth and derives the rest:

  • CURRENT_PROJECT_VERSION — major*10000 + minor*100 + patch, the encoding every release since 1.11.1 has used
  • the three crate [package] versions
  • the sibling =<version> pins
  • the Cargo.lock entries

It rewrites only what it owns: the [package] version rather than every dependency's version key, and pins keyed on a path = "../codevetter-*" sibling so third-party exact pins like rmcp's "=2.2.0" are untouched. A crate missing from the lockfile fails closed rather than being silently skipped.

pnpm version:check is wired into both pipelines:

Where Why
ci.yml, before lint Reports the bump that caused it in ~1s, instead of the CLI symptom ~3 min in
native-production-qualification.yml, before signing Closes the gap above — drift costs seconds, not a notarization round trip

Verification

  • pnpm test:version-sync — 12 tests: encoding, selective rewrites, third-party pins left alone, lockfile fail-closed, drift detection, idempotence
  • pnpm lint, pnpm knip:strict — clean
  • pnpm version:check — reports drift on origin/main (exit 1), clean after sync (exit 0)

The crate bump here is the script's own output, byte-identical to what 72b6b60b did by hand.

Note on the version

Deliberately stays at 1.13.4 rather than cutting forward. The v1.13.4 release is still a draft with zero assets, so the tag is not yet spent — unlike the 1.13.0 case, where the release was already public and 1.13.1 was the only way out.

🤖 Generated with Claude Code

… both pipelines

PR #266 bumped only apps/macos/Config/Shared.xcconfig to 1.13.4, so the three
Rust crates, the "=<version>" pins between them, and Cargo.lock all stayed at
1.13.3. CI on main fails closed with:

  CodeVetter CLI qualification failed: CLI version mismatch:
    expected "codevetter 1.13.4", received "codevetter 1.13.3"

This is the third occurrence. 72b6b60 fixed the identical drift at 1.13.0 by
hand-bumping and cutting forward as 1.13.1. That patched the instance, not the
cause: the version still lived in four hand-edited places, so the next cut that
touched one of them reintroduced it.

Red CI is the lucky symptom. 72b6b60 explicitly recorded the dangerous one and
left it open: core:qualify-cli, which compares the built CLI's --version against
MARKETING_VERSION, runs only in ci.yml and never in the release qualification
path. A drifted cut could therefore sign, notarize, staple, and publish an app
whose bundled codevetter and codevetter-mcp companions reported the previous
version, with every release gate green.

scripts/sync-native-version.mjs makes MARKETING_VERSION the single source of
truth and derives everything else: CURRENT_PROJECT_VERSION (major*10000 +
minor*100 + patch, the encoding every release since 1.11.1 has used), the three
crate versions, the sibling "=<version>" pins, and the Cargo.lock entries. It
rewrites only what it owns -- the [package] version rather than every
dependency's version key, and pins keyed on a path = "../codevetter-*" sibling
so third-party exact pins like rmcp's "=2.2.0" are untouched. It fails closed on
a crate missing from the lockfile instead of silently skipping it.

pnpm version:check is wired into ci.yml, before lint, where it reports the bump
that caused the problem in about a second instead of the CLI symptom three
minutes in; and into native-production-qualification.yml before signing, which
closes the gap above so drift costs seconds rather than a notarization round
trip. 12 tests cover the encoding, the selective rewrites, drift detection, and
idempotence.

The crate bump in this commit is the script's own output, and is byte-identical
to what 72b6b60 did by hand.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sarthakagrawal927
sarthakagrawal927 force-pushed the fix/version-single-source branch from 717531f to 9a144d6 Compare September 6, 2026 12:53
@sarthakagrawal927
sarthakagrawal927 merged commit d804776 into main Sep 6, 2026
24 checks passed
@sarthakagrawal927
sarthakagrawal927 deleted the fix/version-single-source branch October 8, 2026 19:59
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.

1 participant