Skip to content

feat(skills): add cut-release skill#1527

Open
danielmeppiel wants to merge 4 commits into
mainfrom
danielmeppiel/cut-release-skill
Open

feat(skills): add cut-release skill#1527
danielmeppiel wants to merge 4 commits into
mainfrom
danielmeppiel/cut-release-skill

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

TL;DR

Adds .apm/skills/cut-release/ -- a reusable skill that encodes the v0.16.0 release process (decide bump, sanitize [Unreleased], bump pyproject.toml + uv.lock, run CI-mirror lint, open the release PR). Stops at "PR open"; tagging stays a human gate.

Why

The v0.16.0 cycle (#1526) was cut by walking the same steps manually for the third time:

  1. Read git log v0.15.0..HEAD + gh pr view for each PR.
  2. Decide patch vs minor by hand (BREAKING markers + new commands -> minor).
  3. Rewrite each verbose [Unreleased] bullet into a one-line "so what" entry.
  4. Drop internal-only churn (.apm/, tests/, .github/instructions/ -- nothing user-visible).
  5. Bump pyproject.toml, regenerate uv.lock, run the four-step CI lint mirror.
  6. Open the release PR with a rationale paragraph.

That procedure is now a primitive. Activation: "cut a release", "ship v0.x", "what kind of release do we need", etc.

How (architecture)

Designed via the genesis skill; the step-6 handoff packet is persisted in the session plan.md (architecture is the source of truth for future refactors, not the natural-language body).

  • Single-thread sequential, not a panel. One lens (release strategy), one rubric -- pattern-tradeoffs matrix Add ARM64 Linux support to CI/CD pipeline #4 (NO SHARED STATE + SEQUENTIAL) cuts the choice.
  • 3 B10 HUMAN CHECKPOINTS: version pick, sanitized changelog diff, lint-fail recovery. Irreversible steps (push, PR open) are gated.
  • 3 S7 deterministic tool bridges (under scripts/):
    • list-changes-since-tag.sh -- git log + gh pr view, emits JSON, classifies internal-vs-user-facing by path prefix.
    • bump-version.sh -- edits pyproject.toml version line, runs uv lock, emits unified diff.
    • verify-lint-mirror.sh -- mirrors the four-step CI Lint job verbatim from .apm/instructions/linting.instructions.md.
  • 3 assets (under assets/): semver-rubric.md (signal table + pre/post-1.0 framing), entry-sanitizer.md (per-entry rubric + DROP list), pr-body-template.md (variable-substituted PR body).

Composition

Dependency Mode Why
apm-strategy (existing) LOCAL SIBLING, auto-loads via CHANGELOG.md trigger Owns release positioning + breaking-change calls. Depend, don't re-derive.
.apm/instructions/linting.instructions.md LOCAL SIBLING (path-attached rule) Canonical CI-mirror command list. verify-lint-mirror.sh is a thin invoker.
.github/instructions/changelog.instructions.md LOCAL SIBLING (path-attached rule) Owns Keep-a-Changelog format. Reference only.

No EXTERNAL MODULE declared. No PHANTOM DEPENDENCY risk.

Boundary

The skill explicitly does NOT:

  • Tag the release (post-merge, human-gated, triggers the release workflow).
  • Run the full test suite or build PyInstaller binaries (CI gates handle that; the skill mirrors only the Lint job that the changelog claims green).
  • Publish release notes anywhere else (no GitHub Release body, no blog, no announcement).
  • Bump to >= 1.0.0 without explicit operator confirmation (B10 escalation).

Self-dogfooded

The worked examples in assets/semver-rubric.md and assets/entry-sanitizer.md cite the v0.16.0 cycle (#1526) as calibration. Next bug-fix release will be the first real run of the skill end-to-end -- if it diverges from what we'd do manually, the assets get tightened.

Evals

  • 20 trigger evals (10 should-fire, 10 near-miss should-not-fire) in evals/evals.json.
  • Content evals deferred to real-task refinement on the next release cycle (with-skill vs without-skill diff is captured manually).

How to test

bash .apm/skills/cut-release/scripts/list-changes-since-tag.sh --help
bash .apm/skills/cut-release/scripts/verify-lint-mirror.sh --help
bash .apm/skills/cut-release/scripts/bump-version.sh --help

Each prints usage and exits 0. The first dry-runnable end-to-end test is "cut a release" on the next worktree that has merged PRs since v0.16.0.

Encode the v0.16.0 release process as a reusable APM primitive:
sanitize the [Unreleased] CHANGELOG block into a dated version
block (one 'so what' entry per merged PR), pick patch vs minor
per a semver rubric, bump pyproject.toml + uv.lock, run the
CI-mirror lint chain, commit, push, and open the release PR.

Architecture (per genesis step 6 handoff packet, persisted in
session plan.md):

- Single-thread sequential pipeline with 3 B10 HUMAN CHECKPOINTS
  (version pick, sanitized changelog, lint-fail recovery). Not a
  PANEL -- one lens (release strategy), one rubric. Cites
  pattern-tradeoffs matrices #4, #6, #9, #1.
- Depends on existing apm-strategy skill as the versioning lens
  (LOCAL SIBLING, auto-loads via CHANGELOG.md trigger). Depends
  on .apm/instructions/linting.instructions.md and
  .github/instructions/changelog.instructions.md as
  path-attached rules. No EXTERNAL MODULE.
- 3 S7 deterministic tool bridges:
  - list-changes-since-tag.sh -- git log + gh pr view, emits
    JSON, classifies internal-vs-user-facing by path prefix.
  - bump-version.sh -- edits pyproject.toml version line, runs
    uv lock, emits unified diff.
  - verify-lint-mirror.sh -- mirrors the 4-step CI Lint job
    from .apm/instructions/linting.instructions.md verbatim.
- 3 assets:
  - semver-rubric.md -- signal table, pre/post-1.0 framing,
    worked v0.16.0 example.
  - entry-sanitizer.md -- per-entry rubric, DROP list, section
    mapping, worked v0.16.0 example.
  - pr-body-template.md -- variable-substituted PR body.

Boundary: STOPS at 'PR open'. Does NOT tag the release --
tagging stays a human-gated trigger for the release workflow.
Refuses to bump to >= 1.0.0 without explicit operator
confirmation.

Self-dogfooded: the v0.16.0 cycle (PR #1526) was cut by
following this exact procedure manually; the worked examples in
semver-rubric.md and entry-sanitizer.md cite that cycle.

Evals: 20 trigger evals (10 should-fire, 10 near-miss). Content
evals deferred to real-task refinement on the next release
cycle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 12:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new .apm/skills/cut-release/ skill bundle that encodes the manual release-cut workflow (assess bump, sanitize [Unreleased], bump pyproject.toml+uv.lock, run the CI-mirror lint chain, open the release PR) and stops short of tagging, which remains a human gate. The skill ships three deterministic shell helpers, three rubric/template assets, and a trigger-only eval manifest. It composes with the existing apm-strategy skill and the canonical lint/changelog instructions rather than redefining them.

Changes:

  • New SKILL.md defining the seven-phase procedure, three B10 checkpoints, dependencies, and boundary.
  • Three S7 bridge scripts (list-changes-since-tag.sh, bump-version.sh, verify-lint-mirror.sh) plus three assets (semver-rubric.md, entry-sanitizer.md, pr-body-template.md).
  • Trigger-only evals/evals.json with 10 should-fire + 10 should-not-fire items and a stop list.
Show a summary per file
File Description
.apm/skills/cut-release/SKILL.md End-to-end procedure, checkpoints, anti-patterns, boundary.
.apm/skills/cut-release/scripts/list-changes-since-tag.sh Enumerates merged PRs since last tag, emits JSON with user-facing hint.
.apm/skills/cut-release/scripts/bump-version.sh In-place pyproject.toml version edit + uv lock refresh with diff output.
.apm/skills/cut-release/scripts/verify-lint-mirror.sh Runs the four command-based CI lint steps verbatim.
.apm/skills/cut-release/assets/semver-rubric.md Signal table + pre/post-1.0 framing + worked v0.16.0 calibration.
.apm/skills/cut-release/assets/entry-sanitizer.md Per-entry rubric, DROP list, section mapping, consolidation rules.
.apm/skills/cut-release/assets/pr-body-template.md Variable-substituted release PR body.
.apm/skills/cut-release/evals/evals.json 20 trigger evals + stop list; content evals deferred.

Copilot's findings

  • Files reviewed: 8/8 changed files
  • Comments generated: 3

Comment on lines +1 to +23
Cut release {version}.

- Bump `pyproject.toml` to {version} (and `uv.lock`).
- Move `[Unreleased]` to `[{version}] - {date}` in `CHANGELOG.md`, with one short "so what?" entry per PR merged since {prev_version}.

## Why {bump_kind} ({version}), not {alt_bump_kind} ({alt_version})

{bump_rationale}

{breaking_summary_block}

## Validation

Lint mirror green locally (ruff check + format, pylint R0801, auth-signals). See `.apm/instructions/linting.instructions.md` for the contract this mirrors.

## Post-merge

Tag `{version}` to trigger the release workflow:

```
git tag {version}
git push origin {version}
```
Comment thread .apm/skills/cut-release/SKILL.md Outdated
Comment thread .apm/skills/cut-release/assets/entry-sanitizer.md Outdated
danielmeppiel and others added 3 commits May 29, 2026 11:09
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…emplate

Copilot review noted that the PR body template used a single {version}
placeholder for both unprefixed contexts (CHANGELOG.md heading and
pyproject.toml version) and the v-prefixed git tag. The release
workflow's stable-release regex (^v[0-9]+\.[0-9]+\.[0-9]+$ in
.github/workflows/build-release.yml) only matches v-prefixed tags, so
substituting the unprefixed form into 'git tag {version}' produced a
tag the workflow would treat as prerelease.

Introduce {tag} (v-prefixed) for the post-merge tag block and keep
{version} (unprefixed) for the CHANGELOG/pyproject lines, matching
the existing repo conventions. Update SKILL.md Phase 6 substitution
list to document both placeholders and the rationale.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants