Files
msd-core/VERSIONING.md
Tom Boucher 1adf6d2245 fix(#3620): point the docs at files that actually exist (#3658)
* fix(3620): point the docs at files that actually exist

docproof found 34 stale references; the reporter hand-read all 34 and reported the 8 that
are real, explaining why the other 26 are deliberate (files the documents themselves label
legacy or "superseded by", and one pre-Diataxis link label whose target still resolves).
Those 26 are left alone — re-touching them would contradict the issue's own analysis.

Every claim was re-verified against git ls-files at HEAD before editing.

docs/INVENTORY.md said its roster is anchored by six drift-control tests. Five are gone
(commands-doc-parity, agents-doc-parity, cli-modules-doc-parity, hooks-doc-parity in
5d8a8c4d; command-count-sync in fbf30792), so the sentence now names the one that exists.
Whether one test is sufficient coverage is a maintainer question the issue explicitly
declined to answer, so no new drift tests are proposed here.

The four translations were a revision further behind, each naming a seventh test deleted in
ae8bb707 that the English file had already dropped. All four now match.

Renamed targets corrected in CONTEXT.md, VERSIONING.md, docs/CONFIGURATION.md and the
update workflow. The new test names carry no issue-NNN- prefix, which is what
lint-regression-test-names requires, so they are the correct targets.

docs/TESTING-SUITES.md is the one that could cost somebody time: it INSTRUCTED contributors
to add an acknowledgment to the legacy drift-ack file, which CONTRIBUTING.md says to never
use. Rewritten from the real workflow — per-PR fragments under the drift-acks directory,
and a spent base-side ack is re-armed by rewording that fragment's reason in place, never
by adding a duplicate, since two sources naming one path is a hard error.

docs/skills/discovery-contract.md's heading named a query module deleted in 11918dcc. The
section was REMOVED rather than retargeted: its documented behavior (skip the deprecated
root) is not what the surviving code does — skill-manifest includes that root marked
deprecated:true — so retargeting would have documented something false.

Found and fixed inline, same class: VERSIONING.md described an SDK bundling step the
release workflow does not have (zero such mentions in that file); CONFIGURATION.md and four
translations named a dead model-catalog triple collapsed by ADR-457.

Dead config removed: the changeset lint's user-facing prefix list still carried two retired
sdk entries. git ls-files -- 'sdk/*' returns nothing. No test pins that array.

Left deliberately: the comment explaining the retired catalog path, the install regression
test that reconstructs the old broken layout to prove it fails, and the generated
test-timings cache. Each is a legitimate mention of a dead path, not drift.

Note lint-removed-but-needed cannot catch this class: it diffs baseRef...HEAD, so it only
sees files deleted in the change under review. These were orphaned by PRs that predate the
lint. A repo-wide existence audit would need a suppression mechanism for the 26 deliberate
mentions above; that is a feature, not part of this fix.

Fixes #3620

* chore(3620): backfill changeset PR number (#3658)

---------

Co-authored-by: sim <sim@local>
2026-08-19 01:54:01 -04:00

7.5 KiB

Versioning & Release Strategy

GSD follows Semantic Versioning 2.0.0 with three release tiers mapped to npm dist-tags.

Release Tiers

Tier What ships Version format npm tag Branch Install
Patch Bug fixes only 1.27.1 latest hotfix/1.27.1 npx @opengsd/gsd-core@latest
Minor Fixes + enhancements 1.28.0 latest (after RC) release/1.28.0 npx @opengsd/gsd-core@next (RC)
Major Fixes + enhancements + features 2.0.0 latest (after beta) release/2.0.0 npx @opengsd/gsd-core@next (beta)

npm Dist-Tags

Only two tags, following Angular/Next.js convention:

Tag Meaning Installed by
latest Stable production release npm install @opengsd/gsd-core (default)
next Pre-release (RC or beta) npm install @opengsd/gsd-core@next (opt-in)

The version string (-rc.1 vs -beta.1) communicates stability level. Users never get pre-releases unless they explicitly opt in.

Semver Rules

Increment When Examples
PATCH (1.27.x) Bug fixes, typo corrections, test additions Hook filter fix, config corruption fix
MINOR (1.x.0) Non-breaking enhancements, new commands, new runtime support New workflow command, discuss-mode feature
MAJOR (x.0.0) Breaking changes to config format, CLI flags, or runtime API; new features that alter existing behavior Removing a command, changing config schema

Pre-Release Version Progression

Major and minor releases use different pre-release types:

Minor: 1.28.0-rc.1  →  1.28.0-rc.2  →  1.28.0
Major: 2.0.0-beta.1 →  2.0.0-beta.2 →  2.0.0
  • beta (major releases only): Feature-complete but not fully tested. API mostly stable. Used for major releases to signal a longer testing cycle.
  • rc (minor releases only): Production-ready candidate. Only critical fixes expected.
  • Each version uses one pre-release type throughout its cycle. The rc action in the release workflow automatically selects the correct type based on the version.

Branch Structure

main                              ← stable, always deployable
  │
  ├── hotfix/1.27.1               ← patch: cherry-pick fix from main, publish to latest
  │
  ├── release/1.28.0              ← minor: accumulate fixes + enhancements, RC cycle
  │     ├── v1.28.0-rc.1          ← tag: published to next
  │     └── v1.28.0               ← tag: promoted to latest
  │
  ├── release/2.0.0               ← major: features + breaking changes, beta cycle
  │     ├── v2.0.0-beta.1         ← tag: published to next
  │     ├── v2.0.0-beta.2         ← tag: published to next
  │     └── v2.0.0                ← tag: promoted to latest
  │
  ├── fix/1200-bug-description    ← bug fix branch (merges to main)
  ├── feat/925-feature-name       ← feature branch (merges to main)
  └── chore/1206-maintenance      ← maintenance branch (merges to main)

Release Workflows

Patch Release (Hotfix)

For fixes that need to ship without waiting for the next minor.

A hotfix vX.YY.Z cumulatively includes everything in vX.YY.{Z-1} plus every fix:/chore: commit landed on main since that base. The base tag is the anchor — git cherry $BASE_TAG main reveals exactly which commits are still unshipped, and the new vX.YY.Z tag becomes the next hotfix's base, so the cycle is self-documenting.

How to dispatch a hotfix

Hotfixes are dispatched via the Release workflow (release.yml) with a patch version (X.Y.Z). There is no separate hotfix workflow.

  1. Trigger release.yml with action=create, version=1.27.1, auto_cherry_pick=true (default).
    • Workflow detects BASE_TAG = highest v1.27.* < v1.27.1 (so 1.27.1 branches from v1.27.0; 1.27.2 would branch from v1.27.1).
    • Branches hotfix/1.27.1 from BASE_TAG.
    • Auto-cherry-picks every fix:/chore: commit on origin/main not already in the base, oldest-first. Patch-equivalents are skipped via git cherry. feat:/refactor: are never auto-included.
    • On conflict the workflow halts with the offending SHA. Resolve manually on the branch, then re-run finalize with auto_cherry_pick=false.
    • Bumps package.json, pushes the branch, and lists every included SHA in the run summary.
  2. (Optional) push additional manual commits to hotfix/1.27.1.
  3. Trigger release.yml with action=finalize. The workflow:
    • Runs install-smoke cross-platform gate.
    • Runs full test suite + coverage.
    • Promotes CHANGELOG from merged fragments.
    • Tags v1.27.1, publishes to @latest, re-points @next → v1.27.1.
    • Opens merge-back PR against main.

Minor Release (Standard Cycle)

For accumulated fixes and enhancements.

  1. Trigger release.yml with action create and version (e.g., 1.28.0)
  2. Workflow creates release/1.28.0 branch from main, bumps package.json
  3. Trigger release.yml with action rc to publish 1.28.0-rc.1 to next
  4. Test the RC: npx @opengsd/gsd-core@next
  5. If issues found: fix on release branch, publish rc.2, rc.3, etc.
  6. Trigger release.yml with action finalize — publishes 1.28.0 to latest
  7. Merge release branch to main

Major Release

Same as minor but uses -beta.N instead of -rc.N, signaling a longer testing cycle.

  1. Trigger release.yml with action create and version (e.g., 2.0.0)
  2. Trigger release.yml with action rc to publish 2.0.0-beta.1 to next
  3. If issues found: fix on release branch, publish beta.2, beta.3, etc.
  4. Trigger release.yml with action finalize -- publishes 2.0.0 to latest
  5. Merge release branch to main

Conventional Commits

Branch names map to commit types:

Branch prefix Commit type Version bump
fix/ fix: PATCH
feat/ feat: MINOR
hotfix/ fix: PATCH (immediate)
chore/ chore: none
docs/ docs: none
refactor/ refactor: none

Manifest Version Sync

Certain runtime-integration manifests carry a version field that must always match package.json:

  • .claude-plugin/plugin.json — Claude Code plugin manifest (issue #766)
  • gemini-extension.json — Gemini CLI extension manifest (issue #775)
  • .claude-plugin/marketplace.json — Claude plugin marketplace manifest; its version lives at plugins[0].version and is stamped via a nested versionKey descriptor (issue #1855)

The version npm lifecycle script (scripts/sync-manifest-versions.cjs --stage) stamps these files automatically on every npm version call, and stages them so they are included in the release commit alongside package.json.

To add a new manifest that must track the package version, register its path (and, if its version field is not top-level, its dotted versionKey) in the VERSIONED_MANIFESTS array in scripts/sync-manifest-versions.cjs. A regression test (tests/manifest-version-sync.test.cjs) enforces this: it scans all committed JSON files for a matching version field and fails if any are missing from the registry.

Publishing Commands (Reference)

# Stable release (sets latest tag automatically)
npm publish

# Pre-release (must use --tag to avoid overwriting latest)
npm publish --tag next

# Verify what latest and next point to
npm dist-tag ls @opengsd/gsd-core