name: release-procedure
description: >-
Cut, tag, and publish an alef release end-to-end. Use this skill any time the
user asks for a release, a version bump, a hotfix tag, or a CHANGELOG roll-up
in this repo. Covers the full pipeline: changelog, version sync via Taskfile,
Cargo.toml verification, prek lint pass, atomic commit (no AI signatures, no
--no-verify when avoidable), git tag, and gh release create (not just a tag).
license: MIT
Alef Release Procedure
Cut a new alef version with a verifiable, reproducible procedure. Skip no step. Every release step has a concrete verification — never assume; always check.
When to apply
- User asks to cut/release/publish a new version
- User asks to bump alef version
- After a stack of fix/feat commits that need a version cycle
- After a breaking change is committed (must bump MINOR pre-1.0, MAJOR otherwise)
- When consumer repos need a new pin
Hard rules
- Always update
CHANGELOG.md— every release has a dated heading and one bullet per user-visible change. Move entries from[Unreleased]into the new version section. Group under### Added,### Changed (BREAKING),### Fixed,### Removed. Never tag a version with an empty section. - Always run
prek run --all-filesto fix lint/format issues before publishing. Re-stage anything the hooks rewrite. Only commit with--no-verifyif a hook is genuinely broken in a way unrelated to the change — and then file an issue. - No AI signatures in commit messages, tag messages, or release notes.
No
Co-Authored-By: Claude, noGenerated by .... Never. - Atomic commits —
chore(release): X.Y.Zcarries only the version bump and changelog roll. Code fixes live in their own commits, merged before the release commit. - Add tests for any fix that changed behavior. A release that includes a fix without a regression test is a release that will regress.
- Use the Taskfile for version setting — never hand-edit
Cargo.toml,alef.toml, orsrc/core/template_versions.rs.task set-versionrewrites all three in lockstep. - Publish with
gh release create— a baregit tagis not a release. The GitHub release is what triggers consumer automation and what users discover. Alef ships as a single crate, so the tag push triggers exactly onecargo publish— no multi-crate sequencing, no index propagation race.
Procedure
0. Pre-flight
git status # working tree clean (or only release-prep diffs)
git fetch origin # inspect freshness without rewriting local commits
If the release branch has diverged from its upstream, stop and ask how to reconcile it. Do not rebase or merge after committing unless the user explicitly asks for it.
1. Update CHANGELOG.md
- Open
CHANGELOG.md. - Move every bullet under
## [Unreleased]into a new section## [X.Y.Z] - YYYY-MM-DD(today's date, ISO format). - Re-create an empty
## [Unreleased]heading at the top. - If the release contains anything tagged
!(breaking) in commit messages, surface it under### Changed (BREAKING)with explicit migration guidance. - Verify no entries are lost:
git diff CHANGELOG.mdshould show only adds in the new section + the moved bullets.
2. Set the version via Taskfile
task set-version -- X.Y.Z # bumps Cargo.toml, alef.toml, ALEF_REV; runs cargo update
The set-version task is the only sanctioned way to bump versions in this
repo. It rewrites Cargo.toml (package.version), alef.toml
(alef_version), and src/core/template_versions.rs::ALEF_REV in one shot,
then runs cargo update. Never hand-edit any of these — they must stay in
lockstep.
After the task finishes, verify:
grep -E '^version' Cargo.toml # package version
grep -E '^alef_version' alef.toml # alef.toml mirror
grep ALEF_REV src/core/template_versions.rs # template version pin
All three must match X.Y.Z (the ALEF_REV line includes a leading v).
3. Lint pass
prek run --all-files
Re-stage any files the hooks rewrote. If a hook fails for a real reason, fix
that reason — never bypass with --no-verify to push past a lint failure.
4. Tests for changed behavior
For every fix: or feat: rolled into this release, confirm there is a test
that would have caught the bug or covers the new surface. Add the test now if
missing — release commit goes on top.
5. Commit
git add -A
git commit -m "chore(release): X.Y.Z"
The commit subject is exactly chore(release): X.Y.Z. No body unless the
release is large enough to warrant a summary; never add AI attribution.
If gitfluff or another commit-msg hook rewrites the subject in an unhelpful
way, prefer fixing the hook config over --no-verify. When the user has
explicitly authorized --no-verify for this run, document which hooks were
skipped in the release notes.
6. Tag and publish
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin main
git push origin vX.Y.Z
Then create the GitHub release — this is the part most likely to be skipped and the most important:
gh release create vX.Y.Z \
--title "vX.Y.Z" \
--notes-from-tag \
--verify-tag
If the changelog entry is rich enough to use as release notes, replace
--notes-from-tag with --notes-file <(awk '/^## \[X.Y.Z\]/,/^## \[/' CHANGELOG.md | head -n -1)
or build a small notes file from the new CHANGELOG section.
For pre-releases (RC, beta), add --prerelease.
7. Verify
gh release view vX.Y.Z # confirms the release exists with notes
git tag -l vX.Y.Z # confirms the tag exists locally
git ls-remote --tags origin vX.Y.Z # confirms it pushed
If any of those are empty, redo the failed step — do not move on.
8. Downstream pins
For any consumer repo that pins this version, open a follow-up PR that bumps the pin. Don't bundle that into the release commit.
Anti-patterns
- Tagging without a
gh release create— invisible release, breaks automation. - Empty
## [Unreleased]rolled forward to a new version section. - Hand-editing
version = "..."inCargo.toml,alef_versioninalef.toml, orALEF_REVinsrc/core/template_versions.rsinstead of usingtask set-version. - Fix commits with no test added.
--no-verifyto skip a real lint failure.- AI attribution in commit/tag/release text.
- Squashing release prep with code fixes — keep
chore(release): X.Y.Zatomic.
Quick reference
| Step | Command | What it verifies |
|---|---|---|
| Pre-flight | git status && git fetch origin |
Clean tree + remote state |
| Changelog | manual edit of CHANGELOG.md |
Every change is documented |
| Version | task set-version -- X.Y.Z then grep -E '^version' Cargo.toml |
Crate version updated |
| Lint | prek run --all-files |
Pre-commit clean |
| Commit | git commit -m "chore(release): X.Y.Z" |
Atomic release commit |
| Tag | git tag -a vX.Y.Z -m "vX.Y.Z" && git push --tags |
Tag exists remotely |
| Publish | gh release create vX.Y.Z --notes-from-tag --verify-tag |
GitHub release exists |
| Verify | gh release view vX.Y.Z |
Release is discoverable |