--- name: bump-version description: Bump a native addon package's version in package.json and write its CHANGELOG.md entry. Defaults to a minor bump. Use before /release. argument-hint: " [major|minor|patch]" disable-model-invocation: true --- # Bump Version Bump a native addon package's `version` in `package.json` and write the matching `CHANGELOG.md` entry, so `/release` can publish it. Defaults to a **minor** bump. `$ARGUMENTS` is ` [major|minor|patch]` — the package directory name under `packages/`, plus an optional explicit bump level. ## Usage ``` /bump-version llm-llamacpp # minor (default): 0.39.0 -> 0.40.0 /bump-version llm-llamacpp patch # 0.39.0 -> 0.39.1 /bump-version ocr-ggml minor /bump-version translation-nmtcpp major # never chosen automatically — must be explicit ``` ## When to use this skill Use it when work has merged to `main` for an addon package and the package still carries the previous version — i.e. the feature is on `main` but unreleasable because nothing bumped it. This is the routine gap that `/addon-changelog` and `/release` both refuse to fill: - `/addon-changelog` writes the changelog but **refuses to bump** — it stops with a `VERSION BUMP REQUIRED` banner if `package.json` is unchanged vs `main`. - `/release` **validates** the bump then stops and tells you to bump first. Do **not** use it for: | Case | Use instead | |---|---| | SDK/TS pods: `sdk`, `cli`, `rag`, `logging`, `error`, `ai-sdk-provider` | `/sdk-changelog` | | Back-porting a fix onto a shipped `release-*` line | `/release-patch` | | A `qvac-fabric` bump across all 7 consumers | `/rollout-phase-b` | | Publishing an existing bump | `/release` | ## Refusals — stop, do not edit anything 1. **SDK pods.** Their root `CHANGELOG.md` is machine-generated by `scripts/sdk/generate-changelog-sdk-pod.cjs` (`rebuildRootChangelog()`); a hand-edit is destroyed on the next generator run. Tell the user to run `/sdk-changelog` and stop. 2. **Version not higher than npm `latest`.** Report both numbers and stop. 3. **Working branch is a `release-*` branch.** That is `/release-patch` territory — a release branch must stay a byte-exact copy of its base. Stop. 4. **Package has no `CHANGELOG.md`.** Ask the user before creating one. **`fabric` is NOT a refusal.** An earlier version of this skill refused it, claiming its release read `packages/fabric/release-notes/v.md` via `create-github-release-fabric.yml`. That workflow does not exist and is referenced nowhere; `on-merge-nx.yml` passes `changelog-path: packages/fabric/CHANGELOG.md`, and that file is current and correctly bracketed. `packages/fabric/release-notes/` holds only a `v0.1.0.md` leftover and is not the release source. Treat `fabric` like any other addon. ## Step 1 — Read the real current version Never trust the local checkout; it is routinely days or thousands of commits behind. ```bash git -C fetch origin main git -C show origin/main:packages//package.json ``` Read `version` from that output. Cross-check the published version: ```bash npm view @qvac/ dist-tags.latest ``` Expect npm to equal or trail `origin/main`. If `origin/main` is already **ahead** of npm, a bump is already pending — say so and confirm with the user before adding another. ## Step 2 — Find what changed since the last bump Resolve the previous release tag (see Step 6 — do **not** guess it from the directory name), then: ```bash git -C log ..origin/main --oneline -- packages// ``` If the tag is missing, fall back to the last commit that touched `packages//package.json` and diff from there. Harvest PR numbers from the commit subjects (`(#1234)`) for the `### Pull Requests` list, and read the PR titles: ```bash gh pr view --repo tetherto/qvac --json title,body ``` Drop any PR whose diff does not touch `packages//`. Read the actual source diff for the user-visible behaviour — do not paraphrase commit messages, which go stale. (A real example: a merged commit advertised an opt-in env var that did not exist in the merged code.) ## Step 3 — Choose the bump level Honour an explicit `major|minor|patch` argument. Otherwise default to **minor**, and state the reasoning before editing. The de-facto rule from this repo's history: | Change | Level | |---|---| | New public API, new config key, new supported model, changed default | **minor** | | Bug fix only | patch | | `qvac-fabric` dependency bump | **minor** for `0.x`, **major** for `>=1.0` — see `/rollout-phase-b` | | Breaking change | **major — never selected automatically** | **Never auto-select major.** For a package already at major ≥ 1 still default to minor here; a major bump must be an explicit user decision, passed as the `major` argument. `translation-nmtcpp` is the only current post-1.0 addon (`9.0.0`). Its history used to be inconsistent — the same rollout landed as both minor and major — but `8.3.1` → `9.0.0` (#3567), a dependency-floor alignment with no breaking API change, settled it: post-1.0 packages take the major on a fabric rollout. `/rollout-phase-b` codifies that and passes `major` explicitly, which satisfies the never-auto-select rule. Note that for a 0.x package a minor bump is technically the SemVer breaking-change signal; this repo leans on that deliberately, so a minor default is correct rather than conservative. Print the decision before touching files: ``` llm-llamacpp: 0.39.0 -> 0.40.0 (minor) reason: adds new supported finetuning architectures (new accepted config values) ``` ## Step 4 — Bump `package.json` Edit `packages//package.json` with the Edit tool, changing **only** the `version` field. Nothing else in that file, and no other file — in particular never `vcpkg.json` (that is a dependency floor, not the package version) and never `vcpkg-configuration.json`'s `default-registry.baseline`. ## Step 5 — Write the CHANGELOG entry Insert at the top of `packages//CHANGELOG.md`, directly below the preamble and above the previous version heading. The dominant addon shape: ```markdown ## [] - ### Added - for transitions; explain why it matters, not just what changed> ### Pull Requests - [#3509](https://github.com/tetherto/qvac/pull/3509) - ``` Rules: - **The heading must be bracketed** — literally `## [0.40.0]`. `## 0.40.0`, `##[0.40.0]` and `### [0.40.0]` all fail the extractor at `.github/actions/verify-changelog-notes/action.yml`, whose awk is `$0 ~ "^## \\[" version "\\]"`. This is the single most common way this task goes wrong. - Use the date the bump lands, `YYYY-MM-DD`. - **Section headings are level-3**: `### Added`, `### Changed`, `### Fixed`. Pick by intent — new capability is `Added`, altered behaviour or a dependency bump is `Changed`, a defect fix is `Fixed`. Do not use the level-2 `## Features` / `## Bug Fixes` style; `/addon-changelog`'s own documentation still shows it, but that documentation is stale and no current addon entry uses it. - The body must be non-empty until the next `^## [`, or the extractor errors with `Changelog section for version X is empty`. - **`## [Unreleased]` blocks** exist in `ocr-ggml`, `tts-ggml` and `decoder-audio`. Insert the new dated entry **below** the Unreleased block. Do not fold Unreleased content into your version, and do not insert above it — that makes the extractor slice your body off at `## [Unreleased]`. Precedent: commit `dfd1fd7eb` on `ocr-ggml`. - Match the file's existing preamble. Some packages have a Keep-a-Changelog + SemVer preamble, some just `# Changelog`. Do not add one that was not there. - If the version documents work someone else merged, describe the **merged code**, and say so if a commit message contradicts it. ## Step 6 — Report what happens next Look up the git tag by reading `repoName` from `packages//project.json` (`targets.on-merge.options.ci`), which `on-merge-nx.yml` passes to `create-release-tag.yml`. **Never derive the tag from the directory name** — several are counter-intuitive: | Package | Tag | |---|---| | `llm-llamacpp` | `llamacpp-llm-v` (reversed) | | `embed-llamacpp` | `llamacpp-embed-v` (reversed) | | `vla-ggml` | `vla-v` (no `-ggml`) | | `translation-nmtcpp` | bare `v` | | `ocr-ggml` | `ocr-ggml-v` | | `classification-ggml` | `classification-ggml-v` | Addons get a **git tag only, no GitHub Release** — `on-merge-nx.yml` calls `create-release-tag.yml` since #2602. Do not promise a Releases-page entry; only the SDK publishes one. Then tell the user the next step is `/release `, which cuts `release--`, dispatches `on-merge-nx.yml` with `package=`, and pauses at the human-only `npm` approval gate. ## Step 7 — Verify before committing ```bash grep -nE "^## \[\]" packages//CHANGELOG.md git -C diff --stat origin/main ``` - The `grep` must match. This is the extractor's own regex — if it does not match, the release fails later, not now. - `package.json` version must equal the heading version exactly. - The diff must show **exactly two files**: `package.json` and `CHANGELOG.md`. Anything else means something was swept in. Then commit on a branch off `origin/main` — never on `main`, never on a `release-*` branch: ```bash git -C worktree add -b /bump-- origin/main ``` Commit title follows the repo convention `TICKET prefix[tags]: subject`, with `chore` as the prefix (precedent: `chore: bump llm-llamacpp to 0.38.1`): ``` QVAC-12345 chore: bump to for ``` Add no `[api]` tag unless the public API or config surface actually changed — widening the set of *accepted values* is not an API change. Add no AI signature or `Co-Authored-By` trailer. PR body uses the repo's four-section format: problem / how / tested / breaking changes. ## What CI actually enforces Be accurate about this; two in-repo docs are wrong. For addons, `release-merge-guard` (`.github/actions/release-merge-guard/`) asserts exactly three things: 1. the branch matches `release--x.y.z`; 2. `package.json` version equals the version in the branch name; 3. `CHANGELOG.md` was **modified** (skipped on initial branch push and on `workflow_dispatch`). It does **not** validate the heading text for addons, and **no** CI check anywhere compares the new version against the previous one. So a malformed heading will not fail the guard — it fails `/release` Step 1, and would fail the release-body extractor for the SDK and the two lib packages that still use it. Write the bracketed heading regardless: `/release`, `/addon-changelog`, `/release-patch` and `/release-fabric-consumers` all assume it. Do not copy from `ci-validation.mdc:234` (claims the guard checks the heading, and shows an unbracketed `## x.y.z`) or from `/addon-changelog`'s level-2 section example. Both are stale. ## Bash discipline Per repo `CLAUDE.md`: no heredocs, no `$()` command substitution, no `&&`/`||`/`;` chaining, no pipes or redirects. Use Read / Write / Edit / Grep / Glob instead of `cat` / `sed` / `grep` / `find`, and `git -C ` instead of `cd`. Put anything longer than a single command in a script file and run `bash `. ## Error handling - **`release-merge-guard` fails on merge** — version not bumped, the branch name does not match the version, or `CHANGELOG.md` was not modified. - **`/release` Step 1 fails** — the changelog heading is missing or unbracketed. Fix the heading. - **npm publish rejects the version** — it already exists. Check `npm view @qvac/ versions` and pick the next free one. - Never weaken, skip, or delete a test to get a bump through.