Release process¶
Changes to the plugin go through feat/* / fix/* branches off main and land
via PR. Releases are cut from accumulated [Unreleased] changelog entries.
The two-stage changelog gate¶
flowchart LR
PR[Implementation PR] -->|touches plugins/steer/**| ENTRY["Add CHANGELOG entry<br/>## steer → ### [Unreleased]"]
ENTRY --> MERGE[Merge to main]
MERGE --> REPEAT{More changes?}
REPEAT -->|yes| PR
REPEAT -->|cut release| RELEASE["Release PR:<br/>rename [Unreleased] → X.Y.Z<br/>bump plugin.json version"]
- Every behavior change under
plugins/steer/(skills, rules, hooks, templates, scripts, policy) needs aCHANGELOG.mdentry under## steer→### [Unreleased].check_changelog.py --baseenforces this on PRs;tests/are exempt. - Implementation PRs do not bump
plugins/steer/.claude-plugin/plugin.json. Theversionbump happens once, in the release PR that renames[Unreleased]to the newX.Y.Z— so a stream of PRs cuts one coherent release instead of a bump per PR.
check_changelog.py also validates that plugin.json's version equals the newest
released heading and that released headings are in descending semver order.
What does NOT need a changelog entry¶
Changes confined to CLAUDE.md, docs/, or .claude/ ship nothing in the
plugin and need no entry — this includes the documentation site itself.
Before you push¶
mise run check # fast gate: lint, plugin-check, actions (pre-commit equivalent)
mise run ci # full gate: adds fixtures, test, shell, hooktests, version-scan, docs:check
mise run ci is exactly what CI runs. See AUTHORING.md
for the per-change "what to run" matrix, or use the repo-local /preflight
helper.