---
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 step has a hard 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 downstream repos (kreuzberg, etc.) need a new pin

## Hard rules

1. **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.
2. **Always run `prek run --all-files`** to fix lint/format issues _before_
   committing. Re-stage anything the hooks rewrite. Only commit with
   `--no-verify` if a hook is genuinely broken in a way unrelated to the change
   — and then file an issue.
3. **No AI signatures** in commit messages, tag messages, or release notes.
   No `Co-Authored-By: Claude`, no `Generated by ...`. Never.
4. **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.
5. **Add tests for any fix that changed behavior.** A release that includes a
   fix without a regression test is a release that will regress.
6. **Use the Taskfile** for version setting — never hand-edit `Cargo.toml`,
   `alef.toml`, or `src/core/template_versions.rs`. `task set-version`
   rewrites all three in lockstep.
7. **Publish with `gh release create`** — a bare `git tag` is not a release.
   The GitHub release is what triggers downstream automation and what users
   discover. Alef ships as a single crate, so the tag push triggers exactly
   one `cargo publish` — no multi-crate sequencing, no index propagation race.

## Procedure

### 0. Pre-flight

```bash
git status              # working tree clean (or only release-prep diffs)
git pull --ff-only      # don't release on a stale main
cargo check --all-features
cargo test
```

If any of those fail, stop. Fix first, release second.

### 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

```bash
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**:

```bash
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

```bash
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

```bash
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

```bash
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:

```bash
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

```bash
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 (kreuzberg etc.) 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 = "..."` 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 | `cargo check --all-features && cargo test`                         | Clean build + green tests  |
| 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    |
