Files
gstack/ship
Garry Tan e3c961d00f fix(ship): detect + repair VERSION/package.json drift in Step 12 (v1.1.1.0) (#1063)
* fix(ship): detect + repair VERSION/package.json drift in Step 12

/ship Step 12's idempotency check read only VERSION and its bump
action wrote only VERSION. package.json's version field was never
updated, so the first bump silently drifted and re-runs couldn't
see it (they keyed on VERSION alone). Any consumer reading
package.json (bun pm, npm publish, registry UIs) saw a stale semver.

Rewrites Step 12 as a four-state dispatch:

  FRESH            → normal bump, writes VERSION + package.json in sync
  ALREADY_BUMPED   → skip, reuse current VERSION
  DRIFT_STALE_PKG  → sync-only repair path, no re-bump (prevents
                     double-bump on re-run)
  DRIFT_UNEXPECTED → halt and ask user (pkg edited manually,
                     ambiguous which value is authoritative)

Hardening: NEW_VERSION validated against MAJOR.MINOR.PATCH.MICRO
pattern before any write; node-or-bun required for JSON parsing
(no sed fallback — unsafe on nested "version" fields); invalid
JSON fails hard instead of silently corrupting.

Adds test/ship-version-sync.test.ts with 12 cases covering every
state transition, including the critical drift-repair regression
that verifies sync does not double-bump (the bug Codex caught in
the plan review of my own original fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(ship): regenerate SKILL.md + refresh golden fixtures

Mechanical follow-on from the Step 12 template edit. `bun run
gen:skill-docs --host all` regenerates ship/SKILL.md; host-config
golden-file regression tests then need fresh baselines copied
from the regenerated claude/codex/factory host variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ship): harden Step 12 against whitespace + invalid REPAIR_VERSION

Claude adversarial subagent surfaced three correctness risks in the
Step 12 state machine:

- CURRENT_VERSION and BASE_VERSION were not stripped of CR/whitespace
  on read. A CRLF VERSION file would mismatch the clean package.json
  version, falsely classify as DRIFT_STALE_PKG, then propagate the
  carriage return into package.json via the repair path.

- REPAIR_VERSION was unvalidated. The bump path validates NEW_VERSION
  against the 4-digit semver pattern, but the drift-repair path wrote
  whatever cat VERSION returned directly into package.json. A
  manually-corrupted VERSION file would silently poison the repair.

- Empty-string CURRENT_VERSION (0-byte VERSION, directory-at-VERSION)
  fell through to "not equal to base" and misclassified as
  ALREADY_BUMPED.

Template fix strips \r/newlines/whitespace on every VERSION read,
guards against empty-string results, and applies the same semver
regex gate in the repair path that already protects the bump path.

Adds two regression tests (trailing-CR idempotency + invalid-semver
repair rejection). Total Step 12 coverage: 14 tests, 14/14 pass.

Opens two follow-up TODOs flagged but not fixed in this branch:
test/template drift risk (the tests still reimplement template bash)
and BASE_VERSION silent fallback on git-show failure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(ship): regenerate SKILL.md + refresh goldens after hardening

Mechanical follow-on from the whitespace + REPAIR_VERSION validation
edits to ship/SKILL.md.tmpl. bun run gen:skill-docs --host all
regenerates ship/SKILL.md; host-config golden-file regression tests
need fresh baselines copied from the regenerated claude/codex/factory
host variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump version and changelog (v1.0.1.0)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:58:59 +08:00
..