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).
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 # 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.md should show only adds
in the new section + the moved bullets.
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.
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:
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 — 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.