Semantic Versioning Release Process
Intro
Releases are routine, not heroic. Decide the version bump from the
nature of the changes, draft a changelog, update version files in
one commit, tag with vX.Y.Z, and publish. The goal is a release
anyone on the team can perform from the same checklist.
Overview
Determine the version bump
Semantic versioning has three numbers: MAJOR.MINOR.PATCH.
- Patch (0.1.0 → 0.1.1): bug fixes only, no API changes.
- Minor (0.1.0 → 0.2.0): new features, backward-compatible.
- Major (0.1.0 → 1.0.0): breaking changes.
Pre-1.0 projects bend the rules: minor bumps are allowed to break
compatibility, and patch bumps cover both features and fixes. Once
you ship 1.0, the rules become strict.
Pre-release checklist
- All tests pass on the designated release-integration branch.
- No uncommitted changes in the working tree.
- Dependencies reviewed and up to date where appropriate.
CHANGELOG.md has a curated, dated section for the exact release tag;
it includes only user-visible changes, uses standard categories, and has
correct comparison links.
- A root
LICENSE file exists and matches the intended project
license. If the project ships release archives, the archive build must
include that LICENSE at the top level. For processkit,
scripts/build-release-tarball.sh copies root LICENSE into
processkit-vX.Y.Z/.
Draft the changelog
Group entries under Added, Changed, Fixed, and
Removed. Reference issue and PR numbers. Note breaking changes
prominently — call them out in their own section if a major bump
is involved.
The release flow (single turn, bulletproof)
A release is one continuous sequence — you do not stop halfway.
/pk-release vX.Y.Z runs every step below in order, in the same
turn, and is not considered done until the final verification
passes. See DEC-20260422_1348-SnowyWolf.
- Bump version in every relevant file (
aibox.lock,
Cargo.toml, pyproject.toml, package.json, lockfiles).
- Finalize CHANGELOG — rename the
[vX.Y.Z-candidate] section
to [vX.Y.Z] — YYYY-MM-DD; ensure Added/Changed/Fixed/Removed
grouping; call out breaking changes if a major bump. Confirm the
root CHANGELOG.md contains a ## [vX.Y.Z] heading; the archive
build rejects a release without it.
- Regenerate provenance / transitive artifacts — for processkit,
scripts/stamp-provenance.sh vX.Y.Z.
- Commit the bump with message
chore(release): bump to vX.Y.Z.
- Tag the commit:
git tag -a vX.Y.Z -m "<tag summary>".
- Push the release-integration branch then tag. For a stable release,
merge that tagged branch into
main immediately afterwards; for a v1
prerelease, retain the tag on v1.x-pre-release.
A tag push alone does not create a GitHub Release — it is a
git ref, not a distribution-channel artifact.
- Create the GitHub Release with notes extracted from the
CHANGELOG and attach the release archive plus checksum when the
project produces binary/archive assets. For processkit, build first
with
scripts/build-release-tarball.sh vX.Y.Z; the tarball must
contain top-level LICENSE and CHANGELOG.md. The tag commit must
also contain root LICENSE, so GitHub's generated source archives
carry the license. The single canonical command:gh release create vX.Y.Z \
dist/processkit-vX.Y.Z.tar.gz \
dist/processkit-vX.Y.Z.tar.gz.sha256 \
--repo <org>/<repo> \
--title "vX.Y.Z — <one-line summary>" \
--notes-file <(awk -v v=X.Y.Z '
$0 ~ "^## \\[v" v "\\]" { f=1; next }
f && /^## \[/ { f=0 }
f
' CHANGELOG.md) \
--latest
- Verify the Release is live. This is the release's
completion gate. For archive-based releases, verify the Release has
the expected tarball/checksum assets and that extracting the tarball
shows a top-level
LICENSE; also verify the tag itself contains
root LICENSE:gh release view vX.Y.Z --repo <org>/<repo>
tar -tzf dist/processkit-vX.Y.Z.tar.gz | grep '/LICENSE$'
git show vX.Y.Z:LICENSE >/dev/null
If this exits non-zero, the release is incomplete — return
to step 7. Do not report the release as done until step 8
succeeds.
- Other channels (crates.io, PyPI, npm, container registry,
homebrew tap, etc.) — run after step 8 so the GitHub Release
remains the canonical artifact pointer.
Recovery pattern — if a tag was pushed in a prior session
without the Release, run gh release create and gh release view
for that tag directly; /pk-publish exists as an alias for this
recovery scenario.
pk-doctor release_integrity check (detection layer) — walks
every local v* git tag, probes GitHub for a matching Release, and
WARNs on any tag without one. Run /pk-doctor --category=release_integrity
after a release to confirm; run /pk-doctor routinely to catch any
historical gap. Requires gh CLI + auth; emits INFO when gh is
unavailable.
Example
User says: "Let's release — we fixed two bugs and added a feature."
The agent recommends a minor bump (because of the new feature),
drafts changelog entries grouped Added/Fixed, updates version
files, creates the bump commit, and tags it.
This skill also provides the /pk-release and /pk-publish commands
for direct invocation — see commands/pk-release.md and
commands/pk-publish.md.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Bundling a breaking change into a patch or minor release. Consumers rely on semver guarantees to control when they take breaking changes. A breaking change in a patch release violates the contract and causes silent runtime failures in consumer code.
- Skipping the changelog because "git log has it". Git log is not a changelog. It lacks context, grouping, and the human judgment that makes a changelog useful. Every release needs a curated changelog with Added/Changed/Fixed/Removed sections.
- Bumping all version files in separate commits. A brief window where
Cargo.toml says 1.2.0 but pyproject.toml still says 1.1.0 is a broken state. Update all version files in a single atomic commit.
- Tagging before CI passes. A tag should point to a commit that has been fully built and tested. Tagging a commit that later fails CI means the published artifact may not match what was verified.
- Publishing from a developer machine without a clean checkout. Uncommitted local changes — WIP files, debug flags,
console.log statements — may sneak into the artifact. Publish from CI with a clean checkout or from a fresh clone.
- Re-tagging a published version to fix a mistake. Package registries (crates.io, PyPI, npm) prohibit or block re-publishing to an existing version. Cut a new patch version (e.g.
1.2.1) to fix a mistake in 1.2.0; never overwrite a tag.
- Pre-1.0 projects that promise stability they cannot keep. If the API is still moving, stay below 1.0 and say so. Cutting
1.0.0 too early creates a compatibility obligation that slows future improvements.
- Treating
git push --tags as "the release is out." Pushing a tag creates a git ref; it does not create a GitHub Release, a crates.io release, a PyPI release, or any other distribution-channel artifact. Downstream consumers (package managers, gh api, the Releases page, auto-sync tools like aibox) read from the channel, not the tag. Until gh release create (or the equivalent channel publish command) has run, the release is not visible. The flow above bakes this in: the release is not complete until step 8 (gh release view) succeeds. See DEC-20260422_1348-SnowyWolf.
- Stopping after
/pk-release prepare-like work. In an older split where /pk-release prepared and /pk-publish published, it was easy to do only half. The current flow is single-turn: /pk-release runs every step through GitHub-Release verification. /pk-publish remains only as a recovery alias for historical tags without Releases.
Full reference
When pre-1.0 ends
Cut 1.0 when the public API is something you are willing to
support without breaking. Until then, keep the version below 1.0
honestly — there is no shame in 0.17.0, and it signals to users
that the surface may still move.
Version bumps for special changes
| Change |
Bump |
| Bug fix that does not change behavior contract |
patch |
| Performance improvement, no API change |
patch |
| New feature behind a feature flag |
minor |
| New optional parameter with a default |
minor |
| Removing or renaming a public symbol |
major |
| Changing default behavior of an existing API |
major |
| Tightening input validation that rejects previously valid input |
major |
| Dependency bump that surfaces in your public API |
major |
Anti-patterns
- Bundling breaking changes into a patch release
- Skipping the changelog "because git log has it"
- Tagging a commit that was not built and tested
- Publishing from a developer machine without a clean checkout
- Re-tagging a published version to fix a mistake (cut a new
patch instead)
- Pre-1.0 projects that promise stability they cannot keep
1---2name: release-semver3description: Semantic versioning releases — version bumps, changelogs, tags, publishing. Use when preparing a new release: deciding the version bump, drafting the changelog, tagging the commit, and publishing to the project's distribution channel.4---56# Semantic Versioning Release Process78## Intro910Releases are routine, not heroic. Decide the version bump from the11nature of the changes, draft a changelog, update version files in12one commit, tag with `vX.Y.Z`, and publish. The goal is a release13anyone on the team can perform from the same checklist.1415## Overview1617### Determine the version bump1819Semantic versioning has three numbers: MAJOR.MINOR.PATCH.2021- **Patch** (0.1.0 → 0.1.1): bug fixes only, no API changes.22- **Minor** (0.1.0 → 0.2.0): new features, backward-compatible.23- **Major** (0.1.0 → 1.0.0): breaking changes.2425Pre-1.0 projects bend the rules: minor bumps are allowed to break26compatibility, and patch bumps cover both features and fixes. Once27you ship 1.0, the rules become strict.2829### Pre-release checklist3031- All tests pass on the designated release-integration branch.32- No uncommitted changes in the working tree.33- Dependencies reviewed and up to date where appropriate.34- `CHANGELOG.md` has a curated, dated section for the exact release tag;35 it includes only user-visible changes, uses standard categories, and has36 correct comparison links.37- A root `LICENSE` file exists and matches the intended project38 license. If the project ships release archives, the archive build must39 include that `LICENSE` at the top level. For processkit,40 `scripts/build-release-tarball.sh` copies root `LICENSE` into41 `processkit-vX.Y.Z/`.4243### Draft the changelog4445Group entries under **Added**, **Changed**, **Fixed**, and46**Removed**. Reference issue and PR numbers. Note breaking changes47prominently — call them out in their own section if a major bump48is involved.4950### The release flow (single turn, bulletproof)5152A release is **one continuous sequence** — you do not stop halfway.53`/pk-release vX.Y.Z` runs every step below in order, in the same54turn, and is not considered done until the final verification55passes. See DEC-20260422_1348-SnowyWolf.56571. **Bump version** in every relevant file (`aibox.lock`,58 `Cargo.toml`, `pyproject.toml`, `package.json`, lockfiles).592. **Finalize CHANGELOG** — rename the `[vX.Y.Z-candidate]` section60 to `[vX.Y.Z] — YYYY-MM-DD`; ensure Added/Changed/Fixed/Removed61 grouping; call out breaking changes if a major bump. Confirm the62 root `CHANGELOG.md` contains a `## [vX.Y.Z]` heading; the archive63 build rejects a release without it.643. **Regenerate provenance / transitive artifacts** — for processkit,65 `scripts/stamp-provenance.sh vX.Y.Z`.664. **Commit** the bump with message `chore(release): bump to vX.Y.Z`.675. **Tag** the commit: `git tag -a vX.Y.Z -m "<tag summary>"`.686. **Push** the release-integration branch then tag. For a stable release,69 merge that tagged branch into `main` immediately afterwards; for a v170 prerelease, retain the tag on `v1.x-pre-release`.71 A tag push alone does **not** create a GitHub Release — it is a72 git ref, not a distribution-channel artifact.737. **Create the GitHub Release** with notes extracted from the74 CHANGELOG and attach the release archive plus checksum when the75 project produces binary/archive assets. For processkit, build first76 with `scripts/build-release-tarball.sh vX.Y.Z`; the tarball must77 contain top-level `LICENSE` and `CHANGELOG.md`. The tag commit must78 also contain root `LICENSE`, so GitHub's generated source archives79 carry the license. The single canonical command:80 ```bash81 gh release create vX.Y.Z \82 dist/processkit-vX.Y.Z.tar.gz \83 dist/processkit-vX.Y.Z.tar.gz.sha256 \84 --repo <org>/<repo> \85 --title "vX.Y.Z — <one-line summary>" \86 --notes-file <(awk -v v=X.Y.Z '87 $0 ~ "^## \\[v" v "\\]" { f=1; next }88 f && /^## \[/ { f=0 }89 f90 ' CHANGELOG.md) \91 --latest92 ```938. **Verify** the Release is live. This is the release's94 completion gate. For archive-based releases, verify the Release has95 the expected tarball/checksum assets and that extracting the tarball96 shows a top-level `LICENSE`; also verify the tag itself contains97 root `LICENSE`:98 ```bash99 gh release view vX.Y.Z --repo <org>/<repo>100 tar -tzf dist/processkit-vX.Y.Z.tar.gz | grep '/LICENSE$'101 git show vX.Y.Z:LICENSE >/dev/null102 ```103 If this exits non-zero, the release is **incomplete** — return104 to step 7. Do not report the release as done until step 8105 succeeds.1069. **Other channels** (crates.io, PyPI, npm, container registry,107 homebrew tap, etc.) — run after step 8 so the GitHub Release108 remains the canonical artifact pointer.109110**Recovery pattern** — if a tag was pushed in a prior session111without the Release, run `gh release create` and `gh release view`112for that tag directly; `/pk-publish` exists as an alias for this113recovery scenario.114115**pk-doctor `release_integrity` check (detection layer)** — walks116every local `v*` git tag, probes GitHub for a matching Release, and117WARNs on any tag without one. Run `/pk-doctor --category=release_integrity`118after a release to confirm; run `/pk-doctor` routinely to catch any119historical gap. Requires `gh` CLI + auth; emits INFO when `gh` is120unavailable.121122### Example123124User says: "Let's release — we fixed two bugs and added a feature."125The agent recommends a minor bump (because of the new feature),126drafts changelog entries grouped Added/Fixed, updates version127files, creates the bump commit, and tags it.128129This skill also provides the `/pk-release` and `/pk-publish` commands130for direct invocation — see `commands/pk-release.md` and131`commands/pk-publish.md`.132133## Gotchas134135Agent-specific failure modes — provider-neutral pause-and-self-check items:136137- **Bundling a breaking change into a patch or minor release.** Consumers rely on semver guarantees to control when they take breaking changes. A breaking change in a patch release violates the contract and causes silent runtime failures in consumer code.138- **Skipping the changelog because "git log has it".** Git log is not a changelog. It lacks context, grouping, and the human judgment that makes a changelog useful. Every release needs a curated changelog with Added/Changed/Fixed/Removed sections.139- **Bumping all version files in separate commits.** A brief window where `Cargo.toml` says `1.2.0` but `pyproject.toml` still says `1.1.0` is a broken state. Update all version files in a single atomic commit.140- **Tagging before CI passes.** A tag should point to a commit that has been fully built and tested. Tagging a commit that later fails CI means the published artifact may not match what was verified.141- **Publishing from a developer machine without a clean checkout.** Uncommitted local changes — WIP files, debug flags, `console.log` statements — may sneak into the artifact. Publish from CI with a clean checkout or from a fresh clone.142- **Re-tagging a published version to fix a mistake.** Package registries (crates.io, PyPI, npm) prohibit or block re-publishing to an existing version. Cut a new patch version (e.g. `1.2.1`) to fix a mistake in `1.2.0`; never overwrite a tag.143- **Pre-1.0 projects that promise stability they cannot keep.** If the API is still moving, stay below 1.0 and say so. Cutting `1.0.0` too early creates a compatibility obligation that slows future improvements.144- **Treating `git push --tags` as "the release is out."** Pushing a tag creates a git ref; it does **not** create a GitHub Release, a crates.io release, a PyPI release, or any other distribution-channel artifact. Downstream consumers (package managers, `gh api`, the Releases page, auto-sync tools like aibox) read from the channel, not the tag. Until `gh release create` (or the equivalent channel publish command) has run, the release is not visible. The flow above bakes this in: the release is not complete until step 8 (`gh release view`) succeeds. See DEC-20260422_1348-SnowyWolf.145- **Stopping after `/pk-release` prepare-like work.** In an older split where `/pk-release` prepared and `/pk-publish` published, it was easy to do only half. The current flow is single-turn: /pk-release runs every step through GitHub-Release verification. /pk-publish remains only as a recovery alias for historical tags without Releases.146147## Full reference148149### When pre-1.0 ends150151Cut 1.0 when the public API is something you are willing to152support without breaking. Until then, keep the version below 1.0153honestly — there is no shame in 0.17.0, and it signals to users154that the surface may still move.155156### Version bumps for special changes157158| Change | Bump |159|---|---|160| Bug fix that does not change behavior contract | patch |161| Performance improvement, no API change | patch |162| New feature behind a feature flag | minor |163| New optional parameter with a default | minor |164| Removing or renaming a public symbol | major |165| Changing default behavior of an existing API | major |166| Tightening input validation that rejects previously valid input | major |167| Dependency bump that surfaces in your public API | major |168169### Anti-patterns170171- Bundling breaking changes into a patch release172- Skipping the changelog "because git log has it"173- Tagging a commit that was not built and tested174- Publishing from a developer machine without a clean checkout175- Re-tagging a published version to fix a mistake (cut a new176 patch instead)177- Pre-1.0 projects that promise stability they cannot keep