Skip to main content
ClaudeWave
Skill2.9k repo starsupdated yesterday

cut-release

The cut-release skill automates the end-to-end release preparation workflow for the APM project, handling changelog assessment, version bumping, linting, and opening a release pull request on the current worktree. Use this skill when initiating a release cycle, such as preparing version bumps, sanitizing changelog entries, and opening the release PR for team review before the human-gated tagging step.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/microsoft/apm /tmp/cut-release && cp -r /tmp/cut-release/.apm/skills/cut-release ~/.claude/skills/cut-release
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# cut-release -- assess, sanitize, bump, lint, open PR

This skill drives the release-cut workflow end-to-end on the current
worktree. It STOPS at "PR open"; tagging is the human-gated trigger
that fires the release workflow.

## When to use

Trigger this skill on any of these intents:

- "cut a release"
- "ship v0.x" / "ship the release"
- "release prep" / "prepare the release"
- "bump and open the PR"
- "open the release PR"
- "what kind of release do we need now"
- "we have enough merged for a release"
- Any phrasing that ends in opening a release PR.

Do NOT trigger on:

- "tag v0.16.0" -- tagging is the post-merge step the operator runs.
- "publish a blog post" / "draft release notes for the website".
- "what changed since v0.15.0" -- enumeration only, no release.
- "regenerate uv.lock" / raw version-string edits.
- "rollback to v0.15.0" -- release reversal is a different skill.

## Companion primitives

The skill DEPENDS on these existing primitives in the same source
tree. Do not duplicate their content; reference them.

- `.apm/skills/apm-strategy/SKILL.md` -- versioning judgement and
  breaking-change lens. Auto-loads via its own trigger on
  `CHANGELOG.md` and release-pipeline edits. Treat it as the
  authoritative lens for "is this BREAKING" and "does this
  positioning change warrant a migration note".
- `.apm/instructions/linting.instructions.md` -- canonical
  CI-mirror lint chain. Reference; do not re-derive ruff / pylint
  command lists in this skill.
- `.github/instructions/changelog.instructions.md` -- Keep-a-
  Changelog format contract. Reference; do not redefine the
  format.

## Output charset rule

Per `.github/instructions/encoding.instructions.md`, source files in
this bundle (SKILL.md, assets, scripts) MUST stay in printable ASCII.
The PR body and changelog entries this skill produces are also
written to repo files (CHANGELOG.md, PR body), so they MUST stay
ASCII too. Use `--` for em dashes, `[!]` / `[+]` / `[x]` for status
markers, and so on.

## Procedure

Run these phases in order. Reload `plan.md` (the session memento)
at the start of each phase and after every tool return.

### Phase 0 -- ground state

1. Read the current branch with `git rev-parse --abbrev-ref HEAD`.
   The skill assumes you are on a release-prep branch (or are
   willing to create one). If you are on `main`, STOP and ask the
   operator to put you on a branch.
2. Resolve the last release tag with `git describe --tags --abbrev=0`.
3. Persist the goal to plan.md:
   - Range: `<last-tag>..HEAD`.
   - Acceptance: release PR is open, lint mirror green, no version
     bumped beyond what the cycle warrants.

### Phase 1 -- enumerate merged PRs

Run `scripts/list-changes-since-tag.sh` (no arguments). It emits
JSON on stdout: one object per merged PR with fields `pr`, `title`,
`labels`, `author`, `paths_summary`, `is_user_facing_guess`.

The script's heuristic for `is_user_facing_guess` is
intentionally conservative -- it flags as INTERNAL only when the
diff is fully contained in `.apm/`, `.github/instructions/`,
`tests/`, `docs/`, or matches `chore(repo)`. Treat the field as a
HINT, not a verdict; the LLM makes the final call per
`assets/entry-sanitizer.md`.

DO NOT rely on training-recalled PR titles. Truth #5: pretraining
is frozen. Every PR title in the changelog must come from the
script's stdout.

### Phase 2 -- pick the bump (B10 CHECKPOINT 1)

Load `assets/semver-rubric.md`. Walk every PR from Phase 1
against the rubric. The rubric outputs PATCH, MINOR, or
ESCALATE-TO-MAJOR.

Show the operator:

- The chosen bump and the next version number.
- Per-PR rationale (which signal fired).
- The "why patch/minor" one-liner that will go into the PR body.

If the rubric outputs ESCALATE-TO-MAJOR (>= 1.0.0), STOP and
surface the escalation per `assets/semver-rubric.md` "Major bump"
section. Do not bump to 1.0.0 without explicit operator
confirmation.

Wait for the operator's confirm before continuing.

### Phase 3 -- sanitize the changelog (B10 CHECKPOINT 2)

Load `assets/entry-sanitizer.md`. For each PR classified as
user-facing in Phase 1:

1. Choose its section: Added (new feature, new command, new spec),
   Changed (behavior change in existing surface), Fixed (bug fix),
   Deprecated, Removed, Security, Performance.
2. Write ONE entry per PR. Two-sentence cap. "So what" framing.
   PR number in parentheses at the end. Preserve `by @author` for
   external contributors.
3. Consolidate any pre-existing Unreleased entries that point at
   the same PR.
4. Drop INTERNAL PRs entirely (the script flagged most of them;
   the rubric in entry-sanitizer.md confirms).
5. Mark BREAKING entries with `**BREAKING:**` prefix.

Rewrite the CHANGELOG.md `[Unreleased]` block in place:

```
## [Unreleased]

## [X.Y.Z] - YYYY-MM-DD

### Added
...
### Changed
...
### Fixed
...
```

Keep an empty `[Unreleased]` placeholder above the new version
heading. The date is today (read from the operator's environment,
not recall).

Show the operator the unified diff against CHANGELOG.md. Wait for
confirm before continuing.

### Phase 4 -- bump version files

Run `scripts/bump-version.sh <new-version>`. The script edits
`pyproject.toml` (the `version = "..."` line in `[project]`) and
runs `uv lock` to refresh `uv.lock`. It prints a unified diff to
stdout for human review.

If the script exits non-zero, surface the error and STOP -- do not
continue with a partial bump.

### Phase 5 -- verify lint mirror (S4 gate; B10 CHECKPOINT 3 on fail)

Run `scripts/verify-lint-mirror.sh`. It mirrors the four CI lint
steps from `.apm/instructions/linting.instructions.md` verbatim
(ruff check, ruff format --check, pylint R0801, auth-signals).

If it passes (exit 0), proceed to Phase 6.

If it fails (exit 1), STOP. Surface the failures to the operator.
Common cures:

- ruff diagnostics -- run `uv run --extra dev ruff check src/
  tests/ --fix` and `uv run --extra dev ruff format src/ tests/`.
- pylint R0801 -- duplication landed via a recent m