| 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, poly 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
poly fmt --fix . then poly lint . to fix lint/format issues before
publishing. Re-stage anything the formatter rewrites. Only commit with
--no-verify if 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, no Generated by .... Never.
- Atomic commits —
chore(release): X.Y.Z carries 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, src/core/template_versions.rs, or schemas/alef.schema.json.
task set-version rewrites all four in lockstep.
- Publish with
gh release create — a bare git tag is not a release.
The Publish workflow triggers on release: types: [published], so the
GitHub release is literally what runs cargo publish; a tag push alone
runs nothing at all. This is not theoretical: v0.55.2 and v0.55.3 were
tagged and pushed with no release created, and neither ever reached
crates.io. Alef ships as a single crate, so one release means exactly one
cargo publish — no multi-crate sequencing, no index propagation race.
Procedure
0. Pre-flight
git status
git fetch origin
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.md should show only adds
in the new section + the moved bullets.
2. Set the version via Taskfile
task set-version -- X.Y.Z
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, regenerates
schemas/alef.schema.json (cargo run -- schema --schema-version), then runs
cargo update — all in one shot. Never hand-edit any of these — they must stay
in lockstep. The regenerated schema shows up in the release diff (its $id and
version both carry the new version); that is expected output, not drift.
After the task finishes, verify:
grep -E '^version' Cargo.toml
grep -E '^alef_version' alef.toml
grep ALEF_REV src/core/template_versions.rs
grep '"version"' schemas/alef.schema.json
All four must match X.Y.Z (the ALEF_REV line and the schema $id both
include a leading v).
3. Lint pass
poly fmt --fix .
poly lint .
Re-stage any files the formatter rewrote. If a lint 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
git tag -l vX.Y.Z
git ls-remote --tags origin vX.Y.Z
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 — the crate is never published at all.
Publish fires on release: published, not on the tag. This silently lost
v0.55.2 and v0.55.3; both are tagged on origin and absent from crates.io.
- Empty
## [Unreleased] rolled forward to a new version section.
- Hand-editing
version = "..." in Cargo.toml, alef_version in
alef.toml, or ALEF_REV in src/core/template_versions.rs instead of
using task set-version.
- Fix commits with no test added.
--no-verify to skip a real lint failure.
- AI attribution in commit/tag/release text.
- Squashing release prep with code fixes — keep
chore(release): X.Y.Z atomic.
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 | poly fmt --fix . && poly lint . | Lint 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 |