Skip to content

release command

Command

Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. Takes the declared target's name as its only argument.

Invoke it
Claude Code/ca:release
Codex$ca-release
Pi/ca-release

Availability is read from each shipped host catalog. Preview-only commands are marked on the page.

This is the only sanctioned way a version tag gets created, and it holds no knowledge of any particular project. Everything it needs — the tag prefix, which manifests carry the version, which changelog to roll into, what counts as the payload, and any checks to run before tagging — is read from a file the project declares at .codearbiter/release-targets.md. One row means a single-artifact repository; several rows mean a monorepo where each artifact versions on its own.

It never invents a version number. It walks the commits in the selected target’s payload, classifies each by its Conventional Commits type, and applies whichever bump that history actually earns (a breaking change beats a feature, a feature beats a fix). The window is scoped to that target, so a sibling’s commit can never bump this one or land in its changelog. The changelog section is assembled the same mechanical way, pulled from commit footers rather than freehand summary, and a commit that should have carried one but didn’t is a hard stop rather than a gap silently papered over. Composing the tag locally and publishing it are two separate moments: nothing pushes to the remote or shows up as a GitHub Release until you explicitly authorize that second step.

/ca:release [target]

The only argument is the name of a row in the declared target file. It is optional when the file declares exactly one target and required when it declares more — with several declared, a bare invocation stops rather than guessing which one you meant.

There is no version argument, and no --auto or --dry-run. A number you type is a number nobody checked, and preventing exactly that is the reason this lane exists — so the version comes from the commit history and the declared manifests, every time, with no override. A project with no declared file yet enters the skill’s back-fill lane, which proposes a row from what it can detect and writes nothing without your confirmation.

> /ca:release ca-codex
Resolved target `ca-codex` from .codearbiter/release-targets.md.
tag series: ca-codex-v* payload: plugins/ca-codex/
Working tree clean. Branch: release/prep (not the default branch — OK).
Last tag in this series: ca-codex-v0.2.4 (a sibling's v2.8.13 is ignored).
Scanning 9 commits in the payload...
Classification: 2 feat, 4 fix, 3 chore -> minor bump.
Derived version: 0.3.0. Declared manifest plugins/ca-codex/.codex-plugin/plugin.json reads 0.2.4 — will update.
Changelog: all 6 feat/fix commits carry a CHANGELOG: footer.
Declared pre-tag checks: 2 ran, both clean, tree unchanged.
Composed annotated tag ca-codex-v0.3.0 locally. Nothing pushed — authorize to publish.

Reach for /ca:release once the target’s branch is clean and its suite is green and you’re ready to cut a version. Work still in progress lands through /ca:feature or /ca:fix first — a release aggregates what already passed, it doesn’t chase down anything new.

GateWhenEffect
target resolutionbefore anything is read or writtenthe target is looked up in the project’s declared release-targets file; an unrecognised or ambiguous name stops rather than being resolved to a guess
version derivationbefore a tag is composedthe SemVer bump is computed mechanically from Conventional Commits in the target’s own payload — there is no way to supply a version by hand
changelog completenesssame phase as version derivationevery feat/fix/perf commit in the release window must carry its own changelog note; a missing one blocks rather than being auto-filled
publish authorizationafter the tag is composed locallythe tag and the GitHub Release publish together, and only once you explicitly say to
Source — plugins/ca/commands/release.md (v2.11.0)
---
description: Cut a release the only sanctioned way — SemVer bump from the commit log, a CHANGELOG section, an annotated tag. Takes the declared target's name as its only argument. The only path to a version tag.
argument-hint: "[target]"
---
# /ca:release — tagged release
The only permitted path to a version tag. A release is a deployment-readiness assertion: the codebase at this SHA satisfies the bar for shipping. `/ca:release` aggregates existing compliance — it does not duplicate it.
## Flow
Routes to the `release` skill, which resolves everything about the release from the project's **declared target file**, `${CLAUDE_PROJECT_DIR}/.codearbiter/release-targets.md` — the tag prefix, the manifests, the changelog, the payload scope, and the optional checks. Nothing about any target is written here.
`/ca:release` takes the target's name as its only argument. When the declared file names exactly one target, a bare `/ca:release` uses it; when it names more, the argument is required and the skill STOPs rather than guessing. A project with no declared file at all enters the skill's own back-fill lane, which proposes a row and writes nothing without explicit confirmation.
1. **Pre-flight** — declared row resolved, working tree clean, not on the default branch, suite green, no blocking `[CONFIRM-NN]` open. Resolve `LAST_TAG` within that target's own tag series and scope the commit window to the row's declared payload, so a sibling target's commit never bumps this one or lands in its changelog.
2. **Version** — classify the window by Conventional Commits type and apply the highest-precedence bump, against a base that accounts for both the last tag and every declared manifest. Every bumping commit must carry a `CHANGELOG:` footer; a missing one BLOCKs and is never auto-filled.
3. **Surfaces** — roll the section into the declared changelog, update every declared manifest, and run the row's declared `pre-tag` checks. Those checks are check-only: one that mutates the tree BLOCKs.
4. **Tag** — commit the release edits, then compose the annotated tag. Never push it or publish a Release without explicit user authorization — publication is a separate decision.
## Arguments
- **`[target]`** — the name of a row in the declared target file (e.g. the name in `[ca]`). Optional only when exactly one target is declared. An unrecognised name STOPs; it is never resolved to a guess.
There are no version, `--auto`, or `--dry-run` arguments. The version is always derived from the commit log and the declared manifests — supplying one by hand is the thing this lane exists to prevent.
## Routes to
`release` (`${CLAUDE_PLUGIN_ROOT}/skills/release/SKILL.md`), which is authoritative for the phase contents and gates summarized above.
## When NOT to use
- Tagging an in-progress branch → land work first via `/ca:feature` / `/ca:fix`.
- Pushing an already-composed tag → that is a separate user-authorized step.
- A changelog only → it is a phase output, not a standalone deliverable.
- Creating a declared target file ahead of a release → that is `context-creation`'s job, or the skill's back-fill lane at release time.
## Hard gate
MUST NOT compose a tag on a red suite. MUST NOT guess the version — it is derived from the commit log and the declared manifests. MUST NOT auto-fill a missing `CHANGELOG:` footer. MUST NOT resolve an unrecognised or ambiguous target to a guess. MUST NOT write to the default branch or force-push. MUST NOT push the tag or publish a Release without explicit user authorization. Any BLOCK may be bypassed only via `/ca:override`.

View in repo