Files
msd-core/docs/how-to/version-a-capability.md
Tom Boucher 2421cf1b4a feat(#1430): versioned capability manifest + native stamping (ADR-1244 Phase 1) (#1436)
* feat(#1430): versioned capability manifest + native stamping (ADR-1244 Phase 1)

Make the capability manifest versioned — the data substrate the Capability
Ecosystem (ADR-1244) keys off:

- capability.json gains a REQUIRED semver `version` plus the optional
  ecosystem envelope (`engines.gsd`, `compatVersions`, `integrity`,
  `provenance`); the build-time conformance validator enforces them via a new
  `validateVersionEnvelope()` (exported for the Phase 2 runtime overlay).
- All 32 native capabilities stamped with `version` (= package version,
  lockstep) + `engines.gsd`; `sync-manifest-versions.cjs` gains a glob sweep
  that keeps them in sync, and the issue-844 regression guard is extended.
- Strict SemVer 2.0.0 grammar blocks metacharacter/space/unicode smuggling in
  version strings; range/integrity fields are shape-validated (satisfaction
  and the load-time gate are deferred to Phase 2/4).
- Capability rel-paths emitted forward-slash for cross-platform git correctness.

Closes #1430

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#1430): add changeset for versioned capability manifest

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 10:45:53 -04:00

7.0 KiB

How to version and upgrade a capability

This guide covers two separate journeys: how a capability author keeps their manifest correctly versioned as GSD evolves, and how a capability consumer safely applies updates. Read only the section that matches your role; each stands alone.


If you author a capability

Choose a version number

Every capability.json must carry a version field expressed as a semver string. GSD rejects a capability manifest that omits it.

Use the standard semver conventions:

Kind of change Version bump Examples
Backwards-compatible bug fixes or minor prompt improvements patch (0.0.x) Fix a typo in an agent instruction; tighten a hook condition.
New loop-extension hook, new skill, or new config key — existing consumers unaffected minor (0.x.0) Add a verify:post gate; add a new optional config key.
Breaking change to the hook contract, removal of a skill or config key, change of id major (x.0.0) Rename a hook extension point; remove a skill consumers depend on.

Set the version in your manifest before every release:

{
  "id": "my-deploy-gate",
  "version": "1.2.0",
  "engines": { "gsd": ">=1.6.0 <3.0.0" }
}

First-party capabilities are versioned automatically. The native capabilities shipped inside GSD (capabilities/<id>/capability.json) are stamped in lockstep with the GSD package version at release time by scripts/sync-manifest-versions.cjs — their version always equals the GSD version, so per-capability semver and compatVersions only carry independent signal for third-party capabilities. As an author of a third-party capability, you own your own version line; the lockstep rule does not apply to you.

Decide when to raise engines.gsd

The engines.gsd range expresses which GSD host versions your capability is compatible with. GSD enforces this as a hard gate at install time and again at load time.

Raise the lower bound when you start using a GSD feature introduced in a specific release — for example, a loop extension point added in 1.7.0, a new manifest field, or a config federation key that does not exist in older GSD builds. Do not raise it pre-emptively; only raise it when the capability genuinely requires the newer behaviour.

When you do raise the lower bound:

  1. Bump version (at minimum a minor bump, or a major bump if the change is otherwise breaking).
  2. Update engines.gsd to reflect the new minimum.
  3. Add a compatVersions entry (see below).

Maintain compatVersions

compatVersions is a capability-version → minimum-GSD-version table that lets GSD offer older consumers a downgrade instead of a hard block:

{
  "version": "2.0.0",
  "engines": { "gsd": ">=1.7.0 <3.0.0" },
  "compatVersions": {
    "1.2.0": "1.6.0"
  }
}

This entry tells GSD: "version 1.2.0 of this capability requires at least GSD 1.6.0." When a consumer's GSD is older than 1.7.0, GSD uses compatVersions to offer them version 1.2.0 instead of failing outright.

Add a new entry only when you change engines.gsd — that is the only moment an older GSD version and a specific capability version become correlated. A compatVersions entry is not meaningful for a capability distributed as a bare tarball URL (a tarball exposes a single version and cannot be auto-selected from a table); it is only actionable for sources that enumerate versions: git tags, a registry, or npm.

Publish a new version

How consumers receive the update depends on your distribution channel.

Git tag. Commit the updated capability.json (with the new version field), then push a tag whose name matches the version:

git tag v1.2.0
git push origin v1.2.0

GSD's git adapter fetches tags to determine what is available. Without a matching tag, the new version is invisible to gsd capability outdated.

npm. Publish normally. GSD uses dist-tags to check for updates, so the standard npm publish flow is sufficient:

npm version 1.2.0
npm publish

New tarball. Upload the new archive at a URL and communicate the URL to consumers. GSD cannot auto-detect updates for tarball sources — consumers must run gsd capability update <id> <new-url> manually after you announce the new URL. If you anticipate frequent updates, consider switching to a git or npm source.


If you consume a capability

Check for available updates

Run:

gsd capability outdated

GSD contacts the source of each installed capability and reports which ones have a newer version available. Whether an update is detectable depends on the source:

Source Auto-detectable?
Git (tags / manifest) Yes — GSD fetches available tags.
Registry Yes — GSD queries the catalogue.
npm Yes — GSD checks dist-tags.
Tarball URL No — a tarball exposes one version; updates must be applied manually when the author announces a new URL.

If a capability is installed from a tarball and the author publishes a new version at a different URL, you will need to run gsd capability update <id> <new-url> yourself once the author communicates the new address.

Apply an update

To update a specific capability:

gsd capability update <id>

To update all installed capabilities at once:

gsd capability update --all

Updates are atomic: GSD fully fetches and validates the new version before swapping it in. The ledger write is the commit point. If GSD stops mid-update (for example, due to a network failure), the next command run will detect the orphaned state via a reconciliation sweep and restore a consistent install — you will never be left with a half-updated capability.

If the new version adds or removes hooks, MCP server entries, or command modules compared to the version you have installed, GSD will pause and present a summary of the changes before proceeding. You must confirm explicitly; declining leaves the current version in place.

This re-prompt applies even if you previously consented to auto-update. The consent mechanism is scoped to the declared executable surface of a specific version, so a changed surface is always a fresh decision.

Auto-update is off by default for third-party capabilities. If you enable it, the re-prompt on executable-surface change still applies.

When engines.gsd no longer matches

If the new version of a capability requires a GSD version newer than what you have installed, GSD will tell you clearly and — where the source enumerates versions — offer the newest compatVersions-compatible version instead. If no compatible version is available, or the source is a bare tarball, you will need to either upgrade GSD or stay on your current capability version.