---
description: Git tag, CHANGELOG heading, package.json, and npm publish must share one semver. Use when releasing, shipping, deploying, tagging, or publishing npm.
alwaysApply: true
---

# Release version lockstep

**One version.** The git tag, `package.json` `"version"`, CHANGELOG heading, and npm publish are the same semver. Never tag or publish a version that is not already committed in `package.json`.

## Identity

| Surface | Form | Example |
|---------|------|---------|
| `package.json` `"version"` | no leading `v` | `1.14.0` or `1.15.0-beta.1` |
| git tag | `v` + that string | `v1.14.0` or `v1.15.0-beta.1` |
| CHANGELOG heading | `## [vX.Y.Z]` (or without `v`) | `## [v1.14.0]` |
| npm | that same `package.json` version | `balakit@1.14.0` (`latest`) or dist-tag `beta` |

Generated version fields (plugin or marketplace manifests, lockfiles) follow `package.json`. Bump `package.json` only, then run the project's generate or sync step. Never hand-edit generated versions.

## Order

1. Cut CHANGELOG `[Unreleased]` into `## [vX.Y.Z]`.
2. Set `package.json` `"version"` to `X.Y.Z` (no `v`).
3. Run the project's generate or sync step (if any), then its tests and any version-consistency check.
4. Commit. Tag that commit: `git tag -a vX.Y.Z -m "vX.Y.Z"`.
5. Push the tag only after the commit is on the release branch. The `v*` workflow creates the GitHub Release and runs `npm publish`. Do **not** also `gh release create` or `npm publish` locally — that duplicates the tag and can publish a different version than the tag.

If the tag workflow fails after the GitHub Release exists, do not publish a different version locally to "catch up." Fix the secret/token, then re-run the same tag job (make `gh release create` a no-op if the release already exists).

## npm auth

GitHub Actions `NPM_TOKEN` must be an npm **Automation** token (bypasses 2FA). Publish/classic tokens fail CI with `EOTP`.

Procedure: `release-deploy` skill + `skills/release-deploy/projects/<owner>-<repo>.md`.
