Skip to content

Commit 6f5ca24

Browse files
delexwclaude
andauthored
Add cut-release skill for automated versioning and release workflow (#115)
* Add cut-release skill adapted for codex-trace Copy the cut-release skill from delexw/claude-code-trace and adapt it to this repo: PixelPaw-Labs/codex-trace identity and commit links, "Codex Trace" artifact names, the single test-reflection pre-commit hook (no spec hook), and the absence of a GitHub Actions release pipeline yet -- Phase 7 now publishes the release manually from the CHANGELOG slice, and project-shape documents the target release.yml shape for when one is added. Docs only: thirteen markdown files under .claude/skills/cut-release/. * Add release pipeline and CHANGELOG; restore skill pipeline flow Add .github/workflows/release.yml (guard -> notes -> build macOS/Linux/Windows via tauri-action -> publish) triggered on v* tags, mirroring the shape the cut-release skill expects. Add an inaugural CHANGELOG.md (Keep a Changelog format) with a 0.1.0 section the workflow's notes job slices for release bodies. With the pipeline now in place, restore the cut-release skill's pipeline-driven narrative: Phase 6 verifies the workflow run starts, Phase 7 watches it with gh run watch and verifies the published release, and project-shape/SKILL no longer carry the "no pipeline yet" caveat. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0f62569 commit 6f5ca24

15 files changed

Lines changed: 1170 additions & 0 deletions
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
---
2+
name: cut-release
3+
description: |
4+
Cuts a new versioned release of codex-trace end-to-end without asking
5+
any questions. Detects commits since the last ancestral tag, classifies them
6+
with conventional-commit rules to pick the semver bump, bumps every
7+
version-bearing file in sync, writes the CHANGELOG entry, commits and tags
8+
on a release branch, pushes the tag to trigger the GitHub Actions release
9+
pipeline, fast-forwards `main` onto the release commit and pushes it, then
10+
cleans up the local branch. Use this skill whenever the user mentions
11+
cutting, tagging, bumping, or shipping a release — including "cut a
12+
release", "release v1.2.3", "tag and release", "bump version", "publish a
13+
release", "do a patch release", or "make a new release". Trigger even when
14+
the user does not name the version — the skill computes it. Also use
15+
proactively when the user finishes a unit of work and asks to publish it.
16+
---
17+
18+
# Cut a Release — `codex-trace`
19+
20+
Turns "we should release this" into a tagged, pushed, pipeline-triggered, **publicly
21+
published** release with a curated CHANGELOG, in a way that survives the project's strict
22+
pre-commit hook and stays honest about what's actually shipping.
23+
24+
The skill is project-local because the steps depend on this repo's specific shape (three
25+
version files, two lockfiles, GH Actions release on `v*` tag, the test-reflection
26+
pre-commit hook).
27+
28+
## Operating mode
29+
30+
The skill is **fully automated end-to-end** and **fully synchronous**. It never calls
31+
`AskUserQuestion`, never waits for "yes", never branches on user preference, and
32+
**never runs any command in the background**. Every Bash invocation runs in the
33+
foreground so the session holds continuously from Phase 1 through Phase 9 — no
34+
`run_in_background: true`, no trailing `&`, no `nohup`, no `disown`. This includes
35+
long-running steps like `gh run watch` in Phase 7; set the Bash `timeout` parameter to
36+
match the expected duration (e.g. 1800000 ms for the release pipeline) rather than
37+
detaching.
38+
39+
Defaults are deterministic:
40+
41+
- **Scope** — always linear: every commit since `git describe --tags --abbrev=0` ships.
42+
- **Bump tier** — highest conventional-commit tier in the subset (see
43+
`${CLAUDE_SKILL_DIR}/references/conventional-commits.md`). `BREAKING CHANGE:` or `!:`
44+
produces a **minor** bump while the version is `0.X.Y` (pre-1.0 caveat) and a **major**
45+
bump otherwise; the skill never silently promotes to 1.0.0.
46+
- **CHANGELOG** — every Added/Fixed bullet is written from commit subjects + diff reads;
47+
`chore:` / `docs:` / `test:` / `ci:` are always skipped.
48+
- **Push order** — tag first, then main (so CI sees the tag immediately; main catches up
49+
after).
50+
51+
The skill aborts (loudly) only on hard preconditions that would corrupt the release:
52+
53+
- **Duplicate version** — the proposed `vX.Y.Z` tag already exists on `origin`. Abort
54+
with `Error: vX.Y.Z already exists on origin (commit <sha>). Refusing to release the
55+
same version twice.` The user must delete the remote tag intentionally if they want a
56+
re-release, or bump again.
57+
- **Dirty working tree** — uncommitted changes the skill didn't create. Abort with a
58+
list of dirty files.
59+
- **`npm run check` fails** — abort and surface the failure verbatim.
60+
61+
## Before you start
62+
63+
Read these references on the first invocation in a session — they describe constants the
64+
phase files refer to.
65+
66+
- `${CLAUDE_SKILL_DIR}/references/project-shape.md` — version files, lockfiles, pre-commit
67+
hooks, release pipeline wiring.
68+
- `${CLAUDE_SKILL_DIR}/references/conventional-commits.md` — how commit prefixes map to
69+
semver bump level.
70+
71+
`${CLAUDE_SKILL_DIR}/references/changelog-template.md` is loaded when you reach Phase 4.
72+
73+
## Execution
74+
75+
### Phase 1 — Inspect and decide
76+
77+
Read `${CLAUDE_SKILL_DIR}/steps/phase1-inspect-and-scope.md` and follow all instructions.
78+
79+
### Phase 2 — Build the release branch
80+
81+
Read `${CLAUDE_SKILL_DIR}/steps/phase2-build-release-branch.md` and follow all instructions.
82+
83+
### Phase 3 — Bump version files
84+
85+
Read `${CLAUDE_SKILL_DIR}/steps/phase3-bump-versions.md` and follow all instructions.
86+
87+
### Phase 4 — Write the CHANGELOG entry
88+
89+
Read `${CLAUDE_SKILL_DIR}/steps/phase4-changelog.md` and follow all instructions.
90+
91+
### Phase 5 — Verify, commit, tag
92+
93+
Read `${CLAUDE_SKILL_DIR}/steps/phase5-verify-commit-tag.md` and follow all instructions.
94+
95+
### Phase 6 — Push the tag (with duplicate-version preflight)
96+
97+
Read `${CLAUDE_SKILL_DIR}/steps/phase6-push-tag.md` and follow all instructions.
98+
99+
### Phase 7 — Watch the pipeline and verify the release is public
100+
101+
Read `${CLAUDE_SKILL_DIR}/steps/phase7-publish-github-release.md` and follow all
102+
instructions.
103+
104+
### Phase 8 — Push the release commit to `main`
105+
106+
Read `${CLAUDE_SKILL_DIR}/steps/phase8-back-to-main.md` and follow all instructions.
107+
108+
### Phase 9 — Clean up
109+
110+
Read `${CLAUDE_SKILL_DIR}/steps/phase9-cleanup.md` and follow all instructions.
111+
112+
## What the skill will NOT do automatically
113+
114+
- Bypass pre-commit hooks with `--no-verify` — always fix the underlying issue.
115+
- Force-push (`git push -f`) to `main`, the release tag, or any shared branch.
116+
- Delete a remote tag or remote branch.
117+
- Cut a **major** bump (`X.0.0`) while the version is still `0.X.Y` — pre-1.0 the skill
118+
caps at minor even when a breaking change is detected.
119+
- Re-tag an existing version — the duplicate-version preflight in Phase 6 aborts first.
120+
121+
The harness's auto-mode classifier may still intercept pushes to shared branches (e.g.
122+
`git push origin main`) — that is out of skill scope. When it does, surface the error
123+
and continue with the remaining phases; the user resolves the push themselves.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# CHANGELOG Template
2+
3+
The repo uses [Keep a Changelog](https://keepachangelog.com/) conventions.
4+
5+
## First-time setup
6+
7+
If `CHANGELOG.md` doesn't yet exist, create it with this header:
8+
9+
```markdown
10+
# Changelog
11+
12+
All notable changes to codex-trace are documented here. Versions follow
13+
[semantic versioning](https://semver.org/).
14+
```
15+
16+
Then append the per-release section below.
17+
18+
## Per-release section template
19+
20+
```markdown
21+
## [X.Y.Z] — YYYY-MM-DD
22+
23+
One paragraph framing what this release is fundamentally about. Speak to a user, not a
24+
maintainer — explain visible impact, not implementation.
25+
26+
### Added
27+
28+
- **Headline name** ([`<sha7>`](https://github.com/PixelPaw-Labs/codex-trace/commit/<sha>)).
29+
Two or three sentences. Why it exists, what the user sees, any caveat. End with a
30+
pointer to verification or docs if useful.
31+
32+
### Fixed
33+
34+
- **Headline name** ([`<sha7>`](https://github.com/PixelPaw-Labs/codex-trace/commit/<sha>)).
35+
What was broken, when it surfaced, how it surfaces now. Avoid jargon-only entries like
36+
"fix race condition" — say which user-visible behavior was wrong and is now right.
37+
38+
### Changed
39+
40+
- Internal refactors with no behavior change usually don't belong here. Only mention if
41+
the user has to update their own integration (e.g. config file rename).
42+
43+
### Removed
44+
45+
- Anything a user could notice as gone (CLI flag, config key, exported function from a
46+
consumed package).
47+
48+
[X.Y.Z]: https://github.com/PixelPaw-Labs/codex-trace/releases/tag/vX.Y.Z
49+
```
50+
51+
## Bucket rules
52+
53+
- **Added** for `feat:` commits.
54+
- **Fixed** for `fix:` / `perf:` commits.
55+
- **Changed** for `refactor:` / config / build adjustments that the user must notice.
56+
- **Removed** for deletions of public surface.
57+
- **Breaking Changes** (new bucket, comes first, before Added) for major releases. Lead
58+
with what broke and how the user migrates.
59+
60+
Always skip `chore:` / `docs:` / `test:` / `ci:` — they clutter the CHANGELOG without
61+
informing users. The skill is non-interactive; never include these even if the count
62+
looks short.
63+
64+
## Linking
65+
66+
Each bullet **must** link to its commit so readers can dig in:
67+
68+
```
69+
([`ebb2ca5`](https://github.com/PixelPaw-Labs/codex-trace/commit/ebb2ca5))
70+
```
71+
72+
7-character SHAs are enough — git accepts the abbreviation.
73+
74+
The reference-link at the bottom (`[X.Y.Z]: https://...releases/tag/...`) is what GitHub
75+
uses to make the version header a clickable link in rendered markdown. Keep it.
76+
77+
## Style
78+
79+
Write substantive bullets. Read the diff if the subject is terse — "fix off-by-one" is
80+
useless to a user, "the picker no longer skips the first session card after a refresh"
81+
is what they care about. Lean on the "what was broken / what is now right" frame for
82+
Fixed, "what's new and why" for Added.
83+
84+
## After writing
85+
86+
Always format:
87+
88+
```bash
89+
npx oxfmt CHANGELOG.md
90+
```
91+
92+
oxfmt enforces consistent table / code-fence formatting that survives the project's
93+
`fmt:check` gate.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Conventional Commit → Semver Bump
2+
3+
The bump level for a release is the highest tier among the commits in the release
4+
subset. One `feat:` upgrades the whole release to minor.
5+
6+
| Prefix or marker in commit subject / body | Bump tier | Notes |
7+
| ------------------------------------------------ | ------------------------------ | ------------------------------------------------------------------------------------ |
8+
| `feat:` or `feat(scope):` | **minor** | new user-facing feature |
9+
| `fix:` `fix(scope):` | **patch** | bug fix |
10+
| `perf:` `perf(scope):` | **patch** | observable performance improvement |
11+
| `refactor:` `refactor(scope):` | **patch** | internal restructure with no behavior change |
12+
| `chore:` `docs:` `test:` `ci:` `build:` `style:` | **patch** (or "no release") | usually skip in CHANGELOG; if the whole release is only these, still call it a patch |
13+
| `!` after type — e.g. `feat!:`, `fix!:` | **major** (capped — see below) | breaking change in API or schema |
14+
| `BREAKING CHANGE:` footer (anywhere) | **major** (capped — see below) | breaking change in API or schema |
15+
| Anything else / no convention | **patch** | default conservatively — most non-conventional commits are docs/chore-shaped |
16+
17+
## Algorithm (fully automated)
18+
19+
1. List commits: `git log $LAST_TAG..HEAD --pretty=format:'%H %s%n%b' --no-merges`
20+
2. Classify each by the table above; pick the highest tier.
21+
3. Apply the pre-1.0 cap (next section).
22+
4. Compute the next version:
23+
- **major**: `X+1.0.0` (e.g. `1.2.3``2.0.0`)
24+
- **minor**: `X.Y+1.0` (e.g. `0.5.1``0.6.0`)
25+
- **patch**: `X.Y.Z+1` (e.g. `0.5.0``0.5.1`)
26+
5. State the chosen bump inline (not as a question) and proceed.
27+
28+
## Pre-1.0 cap (currently active — version is `0.X.Y`)
29+
30+
While the major version is still `0`, the skill **caps the bump at minor** even when a
31+
`BREAKING CHANGE:` footer or `!:` marker is present. Rationale: `0.X.Y` semver does not
32+
imply API stability, so breaking changes are expected; promoting to `1.0.0` is a
33+
deliberate product decision, not an automated one.
34+
35+
The cap is automatic — the skill never promotes to `1.0.0` without the user explicitly
36+
typing "release as 1.0.0" or similar in the same turn. If the user did, honor it.
37+
38+
Remove this cap (in code review of this file, not at runtime) once the project
39+
intentionally crosses `1.0.0`.
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Project Shape — `codex-trace`
2+
3+
The skill-specific facts the phase files rely on. For everything that's already in the
4+
codebase, this file points at the source of truth rather than repeating it.
5+
6+
## Read these first if you don't know the codebase
7+
8+
- `AGENTS.md` (with `CLAUDE.md` symlinked to it) — toolchain commands (`npm run check`,
9+
`oxfmt`, `oxlint`, etc.) and the "format + lint + test before committing" rule.
10+
- `.claude/hooks/pre-commit.sh` — header comments describe exactly what the hook checks
11+
(format, lint, tsc, Rust fmt/clippy) and how the per-session test-reflection flag
12+
bypass works.
13+
- `.claude/settings.json` — the `Bash(*git commit*)` matcher that wires the hook into
14+
`git commit` calls.
15+
16+
## Version-bearing files (skill-specific rule)
17+
18+
Three files must agree on the next-version string. Nothing in the codebase enforces this
19+
sync — the skill does.
20+
21+
| File | Owns | Bumps with |
22+
| --------------------------- | ------------------------------------ | ------------------------------ |
23+
| `package.json` (root) | Node/TS workspace + binary entry | the Rust crate (lockstep) |
24+
| `src-tauri/Cargo.toml` | Rust crate version | root `package.json` (lockstep) |
25+
| `src-tauri/tauri.conf.json` | Tauri bundle filenames + app version | the Rust crate (lockstep) |
26+
27+
Root + Cargo + `tauri.conf.json` move together because the desktop binary the user installs
28+
is built from all three — `tauri.conf.json`'s `version` field is what `tauri-action`
29+
templates into the released artifact filenames (`Codex.Trace_<version>_*.dmg`, etc., from
30+
the `productName` "Codex Trace"). Missing this file silently ships a release whose
31+
artifacts are stamped with the previous version.
32+
33+
There is currently no separate sub-package (TUI or otherwise) with its own version
34+
manifest. If a versioned manifest is ever introduced (e.g. a `pyproject.toml` or a nested
35+
`package.json`), add it to the lockstep set and update the skill's Phase 3 step.
36+
37+
## Lockfile regen after a version bump
38+
39+
The lockfiles embed the local workspace's version, so they have to be regenerated after
40+
editing version files — `npm run check` won't fix this on its own. Run:
41+
42+
```bash
43+
npm install --package-lock-only # → package-lock.json
44+
( cd src-tauri && cargo check --offline ) # → src-tauri/Cargo.lock
45+
```
46+
47+
`--package-lock-only` skips the full reinstall (nothing in `node_modules` needs to
48+
change) and `--offline` skips the registry round-trip — only the local crate's version
49+
moved.
50+
51+
## Release pipeline (delegated to CI)
52+
53+
`.github/workflows/release.yml` is the source of truth. Its job graph for `v*` tag
54+
pushes:
55+
56+
1. `guard` — refuses to run if a non-draft GitHub release for the tag already exists.
57+
This is the duplicate-release defence-in-depth complement to the skill's Phase 1
58+
preflight; if a stale tag was pushed, the CI aborts before any artifact upload.
59+
2. `notes` — slices `CHANGELOG.md` for the version's section and exposes it as a
60+
workflow output. Fails if the heading isn't in the exact `## [X.Y.Z] — YYYY-MM-DD`
61+
format.
62+
3. `build-macos` / `build-linux` / `build-windows` — three parallel `tauri-action` runs
63+
each creating / updating a draft release with platform artifacts.
64+
4. `publish` — flips the draft to public and marks it latest.
65+
66+
`workflow_dispatch` mode is a dry-run for the notes job only; nothing is built or
67+
published. Use it to verify a CHANGELOG section parses correctly before tagging.
68+
69+
If the pipeline changes, edit the workflow and update Phase 7's narrative, not this
70+
file.
71+
72+
## GitHub repo identity
73+
74+
Read once with `git remote get-url origin`; the URL template for commits and releases
75+
follows from there:
76+
77+
- Commit: `<repo-url>/commit/<sha>`
78+
- Release: `<repo-url>/releases/tag/v<X.Y.Z>`
79+
80+
The CHANGELOG template (`changelog-template.md`) hardcodes `PixelPaw-Labs/codex-trace`
81+
in the link format. If the repo moves, update that file once.

0 commit comments

Comments
 (0)