agentleFS
ffrolivaffroliva/gflow-cli/skills/release/SKILL.md

release

Skill367 starsChanged 58 days agoMITView on GitHub

Cut a new gflow-cli release — bump version, update CHANGELOG, tag, push, and back-merge.

  • Commits and pushes

196 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use create-agent-adapter, changelog-style and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • MicrosoftSkill

    create-agent-adapter

    Technical guide for creating a new Paperclip agent adapter. Use when building a new adapter package, adding support for a new AI coding tool (e.g. a new CLI agent, API-based agent, or custom process), or when modifying the adapter system. Covers the required interfaces, module structure, registration points, and conventions derived from the existing claude-local and codex-local adapters.

    Also in 5 projects that use this

  • GrinvSkill

    changelog-style

    Turn a commit range into CHANGELOG.md entries following common-changelog style. Use when finishing a PR or cutting a release, to write/fix the [Unreleased] section's entries — moving [Unreleased] under a new dated version heading is a separate, automated step (see the release skill / scripts/sync-version.ts), not this skill's job.

    Also in 4 projects that use this

  • GrinvSkill

    docs-consistency-check

    Check README/manifest.json/server.json/CHANGELOG.md/AGENTS.md and docs/*.md for drift against the actual registered tools and source. Use after adding, renaming, or removing a tool, or as part of a live-audit pass.

    Also in 4 projects that use this

  • Microsoft AzureSkill

    component-release

    Prepares a verifiable Agent Landing Zone UI release. Use for release branches, VERSION, changelog entries, tags, release notes, and compatibility evidence; never publish without explicit human approval.

    Also in 4 projects that use this

  • Microsoft AzureSkill

    documentation-consistency

    Keeps AI Landing Zone contributor, user, and operator documentation aligned with shipped Bicep behavior. Use for parameters, defaults, outputs, topology, deployment flow, operations, or releases.

    Also in 8 projects that use this

  • rivet-devSkill

    launch-graphics

    Generate agentOS marketing/launch graphics (launch & social heroes, code-snippet cards) from the committed generators in docs-internal/graphics. Use when asked to make, update, or render an agentOS launch image, social image, "agentOS Apps" graphic, or technical code-snippet image.

    Also in 3 projects that use this

  • +3 more from Grinv

The file

Show its rules and text

We split this skill into the rules it gives an agent

22 rules. 0 are common, 8 are distinctive, and 14 are very specific to this project.

  • 0

    Common

    Most of the 186 edited versions we read keep these.

  • 8

    Distinctive

    This author's own choices. Worth a look.

  • 14

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Follow this sequence verbatim.

    line 10
  2. Distinctive:

    Ask the user (if not already provided):

    line 23
  3. Very specific:

    Run /gflow:changelog — confirm the [Unreleased] block is non-empty and accurate before proceeding.

    line 34
  4. Distinctive:

    If empty, continue. If not, triage before aborting — do not blindly stop:

    line 42
  5. Very specific:

    Genuine uncommitted work: STOP and tell the user to commit or stash on the appropriate branch (never commit straight to develop).

    line 48
  6. Very specific:

    If not on develop: git checkout develop && git pull origin develop.

    line 66
  7. Distinctive:

    If local is behind origin: git pull origin develop.

    line 67
  8. Very specific:

    Run /gflow:check — all gates must pass.

    line 74
  9. Very specific:

    Before cutting, run ListAgents (or the equivalent in your harness) and message every other session working on this repo: *"Cutting …

    line 98
  10. Distinctive:

    8. Update version assertion tests: if present:

    line 164
  11. Very specific:

    Update the link footer. Match the repo's existing convention — every prior entry uses the compare/vPREV...vNEW form, so use that for the …

    line 174
  12. Very specific:

    12. Tag the release commit: Use -s for a signed annotated tag so GitHub shows "Verified" AND .github/workflows/release.yml passes the …

    line 236
  13. Very specific:

    If it is non-zero: git merge origin/develop, move the newcomers' [Unreleased] entries under ## [<NEW_VERSION>], re-run steps 4, 4b and 10 — …

    line 247
  14. Distinctive:

    Run git config --global user.signingkey to confirm a key is configured.

    line 268
  15. Very specific:

    Wait for the Release run to report completed / success and confirm the GitHub Release published before continuing (gh release view …

    line 296
  16. Very specific:

    Wait for PR CI to go green (gh pr checks <N> --watch).

    line 307
  17. Very specific:

    Before merging, leave the release worktree and remove it — --delete-branch deletes the local branch too, and git refuses to delete a branch …

    line 311
  18. Very specific:

    After the release PR is merged, bring the bump commit back to develop so branches stay aligned.

    line 347
  19. Very specific:

    If there are conflicts (rare — only if develop has commits that touched the same lines as the bump), resolve them, keeping develop's …

    line 361
  20. Very specific:

    NEVER: add Co-Authored-By: Claude (or any AI co-author) to the release commit.

    line 391
  21. Distinctive:

    If quality gates fail at step 4, STOP.

    line 397
  22. Distinctive:

    If doc-review fails at step 10, STOP.

    line 398

“Common” is measured: kept by at least 6 in 10 of the 186 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

/gflow:release — Cut a new release

Follow this sequence verbatim. Every step matters.

Branch-protection note: main blocks direct pushes. The release commit travels via a chore/release-vX.Y.Z branch PR. The signed tag is pushed independently (tag pushes bypass branch protection and trigger the CI release workflow immediately).

Source-branch note (read first): develop is the integration branch — it carries ALL unreleased work and main usually lags it. The release branch is cut from develop, NOT main. The PR chore/release-vX.Y.Z → main then brings the full integration history onto main. Do not expect the work to already be on main.

Inputs

Ask the user (if not already provided):

  1. Version — the new version (e.g. 0.4.0, 0.4.0a3, 1.0.0rc1). Use PEP 440 prerelease suffixes (aN, bN, rcN). If they don't know, run /gflow:changelog first and propose the next bump (PATCH for fixes only, MINOR for new features, MAJOR for breaks).
  2. Pre-release? — prerelease versions stay marked as GitHub prereleases. Only the user can say when a release line is ready for the stable tag.

Sequence

1. Review what's queued.

Run /gflow:changelog — confirm the [Unreleased] block is non-empty and accurate before proceeding.

2. Verify (or triage) a clean working tree.

git status --short

If empty, continue. If not, triage before aborting — do not blindly stop:

  • Auto-injected boilerplate (e.g. a context-mode routing block appended to CLAUDE.md by an MCP plugin's SessionStart hook): this is plugin-injected, not project content — git restore it. Confirm with the user if unsure.
  • Build/temp artifacts (e.g. a stray tmp*.tar.gz sdist at repo root): delete them.
  • Genuine uncommitted work: STOP and tell the user to commit or stash on the appropriate branch (never commit straight to develop).

The tree must be clean before you create the release branch.

3. Verify develop is the release source and up-to-date.

The release is cut from develop, NOT main (see Source-branch note above). Confirm direction explicitly — a backwards divergence means a prior back-merge was skipped.

git fetch origin
git rev-parse --abbrev-ref HEAD                      # expect "develop"
git rev-list --count HEAD..origin/develop            # local behind origin — expect 0
git rev-list --count origin/main..origin/develop     # develop AHEAD of main — expect > 0 (the work to release)
git rev-list --count origin/develop..origin/main     # main AHEAD of develop — expect 0

If not on develop: git checkout develop && git pull origin develop. If local is behind origin: git pull origin develop. If main is AHEAD of develop (last count > 0): STOP. A prior release skipped its main → develop back-merge — recover first (see the release-back-merge-gap-recovery memory) or the release branch will hit conflicts on pyproject.toml / __init__.py / CHANGELOG.md.

4. Run quality gates.

Run /gflow:check — all gates must pass. Abort if any fail.

4b. Live-verify the release's user-facing features (REQUIRED gate).

For every new/changed user-facing feature in this release, exercise it against live Flow (credit-free wherever possible — image gen, entity attach, upscale, and scene/timeline ops cost no Veo credits) and write the evidence to docs/LIVE_VERIFICATION_v<NEW_VERSION>.md using the 5-layer ledger (file count + magic bytes + dimensions/shape + structlog invariants + a user-confirmable artifact). Add it to the "what was live-verified" entry in docs/INDEX.md. This doc shipped for every release v0.7.0→v0.13.0, then lapsed for v0.14.0–v0.15.1 — which is why it is now an explicit gate. If a feature genuinely cannot be verified this cycle, record that and the reason in the doc; never silently omit it. Include one plain gflow video t2v on the release tree, whatever changed (live-verify step 0, #1153). Stage the doc into the release-prep commit (step 11).

5. Create a release branch off develop — in its own worktree, after telling every other session.

git worktree add -b chore/release-v<NEW_VERSION> .claude/worktrees/release-v<NEW_VERSION> origin/develop
cd .claude/worktrees/release-v<NEW_VERSION>

Before cutting, run ListAgents (or the equivalent in your harness) and message every other session working on this repo: "Cutting v<NEW_VERSION> from develop@<sha>; do not push to develop until the back-merge lands." Then cut in a dedicated worktree (the repo convention is .claude/worktrees/<slug>), never in the shared checkout. The cut is from origin/develop, which step 3 just verified local develop is not behind — a local develop that goes stale later no longer matters (unpushed local commits on develop are deliberately NOT in the release; push them first if they should be). Steps 6–13 run inside this worktree; step 14 returns to the main checkout and removes it.

Why. On 2026-09-05 the v0.68.0 release branch was cut in the shared checkout and then switched out from under the release runner by another session's git checkout, costing a recovery; separately, an unrelated PR landed on develop between the cut and the tag, so the signed tag had to be deleted and re-signed on a merged head (nothing had been pushed, so no public tag moved — step 12 now checks for this). A worktree makes the branch immune to sibling checkouts; the announcement makes the develop race visible instead of discovered at tag time.

This branch now contains all of develop (⊇ main) plus your release prep. All release prep commits live here; the PR into main (step 14) carries the full integration history forward.

6. Bump the shared release version — SEVEN sites, and no single gate sees them all.

This is the canonical list. It is the only one that spans all three gates; the previous version of this step named four sites, check_repo_hygiene.py's docstring named three, and its own function checked five. A release engineer then met each omission as a gate failure mid-release (#839). If you add a version site, add it here.

#SiteCaught by
1pyproject.toml [project].versioncheck_repo_hygiene._check_version_agreement
2src/gflow_cli/__init__.py __version__same
3.codex-plugin/plugin.json "version"same
4uv.lock (the gflow-cli package block)same — re-resolved by uv lock
5server.json .version and .packages[*].version (twice)same
6plugins/gflow/.claude-plugin/plugin.json "version"tests/test_plugin_manifests.py::test_plugin_version_tracks_pyproject
7docker/Dockerfile ARG GFLOW_VERSIONtests/test_dockerfile_version_pin.py

server.json is the one that bites quietly: it is the MCP Registry's copy of our metadata, and the registry publish carries whatever the ref says. Forget it and the listing points at a superseded version with nothing downstream noticing.

Do not bulk-replace the old version across the repo. docker/README.md quotes measured results (gflow_cli : <version>, ✅ measured); rewriting those turns a record of what was tested into a false claim. Bump declaration sites only — the seven above — and leave measurement records alone unless the measurement was re-run.

Sites 1–3 and 6:

[project]
version = "<NEW_VERSION>"
{
  "version": "<NEW_VERSION>"
}

7. Bump package version in src/gflow_cli/__init__.py:

__version__ = "<NEW_VERSION>"

8. Update version assertion tests if present:

rg -n "__version__|<OLD_VERSION>|version assertion" tests src pyproject.toml .codex-plugin/plugin.json plugins/gflow/.claude-plugin/plugin.json

9. Migrate CHANGELOG.

  • Move all entries under ## [Unreleased] to a new ## [<NEW_VERSION>] — YYYY-MM-DD section.
  • Leave ## [Unreleased] empty.
  • Update the link footer. Match the repo's existing convention — every prior entry uses the compare/vPREV...vNEW form, so use that for the new version too (NOT the releases/tag/ form), or /gflow:doc-review will flag the inconsistency:
    [Unreleased]: https://github.com/ffroliva/gflow-cli/compare/v<NEW_VERSION>...HEAD
    [<NEW_VERSION>]: https://github.com/ffroliva/gflow-cli/compare/v<PREV_VERSION>...v<NEW_VERSION>

9b. Update docs/PROJECT_STATUS.md — this is an ACTION, not a review finding.

Rewrite the ## Current release section to describe the release being cut, and add a milestone-history row for its headline change. Demote the previous release into a <details><summary>vPREV — …</summary> block rather than deleting it.

The file's own header says "Updated on every signed tag" — a promise that went unkept for five consecutive releases, and again in v0.64.0, where the section still announced v0.63.0 as current at tag time. It was caught only because a human council happened to read the file. scripts/ci/check_release_artifacts.py now enforces it (violation 6): the version being cut must appear in that section specifically, not merely somewhere in the file — every past release is still named further down, so a whole-file search would pass on a fully stale header. Run it before committing:

uv run python scripts/ci/check_release_artifacts.py

Doing this at step 9b rather than discovering it at step 10 is the point: doc-review is a detector, and a gate that only detects still costs a round trip every release.

10. Run the documentation review gate.

Run /gflow:doc-review — audit all version refs, INDEX completeness, evidence files, the published website/docs/ mirror (PII gate + content-drift check, §4b), code↔docs parity via git log (§4c), skill files, CHANGELOG footer, and memory files. Fix every FAIL before continuing. Fold all discovered fixes into the release prep commit — including any website/docs/ re-sync (the mirror is anonymized and hand-synced; a canonical doc change this release must be mirrored, and CHANGELOG.md must never appear under website/docs/).

Also consolidate shipped planning artifacts here: extract any durable patterns into auto-memory, then remove the now-shipped docs/superpowers/ plan / spec / verification files (keep only in-flight work). check_repo_hygiene.py enforces the root-doc allowlist, so a stray review doc or session marker left at the repo root will fail the gate.

11. Commit the release prep.

# All seven version sites from step 6 — a bumped-but-unstaged site fails the gate
# on the release branch, after the tag is already in your fingers.
git add pyproject.toml .codex-plugin/plugin.json plugins/gflow/.claude-plugin/plugin.json
git add src/gflow_cli/__init__.py uv.lock server.json docker/Dockerfile CHANGELOG.md
git add docs/PROJECT_STATUS.md                 # step 9b — enforced by check_release_artifacts
git add docs/ website/docs/ skills/ .claude/commands/gflow/   # include any doc-review + mirror fixes
# doc-review version-currency fixes often also touch ROOT docs — stage them too:
git add README.md PLAN.md KNOWN_ISSUES.md AGENTS.md llms.txt 2>/dev/null || true
git status --short                            # review EVERYTHING staged before committing
git commit -m "chore(release): v<NEW_VERSION>"
  • uv.lock changes on every version bump (the editable package version is pinned in the lockfile) — it is easy to forget and must ship in this commit.
  • .codex-plugin/plugin.json tracks the package version so marketplace installs receive a new cache path for every release.
  • server.json carries the version TWICE — top level and inside the PyPI package entry. _check_version_agreement checks both; a half-bump fails the gate.
  • The release-prep commit must NOT carry a Co-Authored-By trailer (see reminders).

12. Tag the release commit. Use -s for a signed annotated tag so GitHub shows "Verified" AND .github/workflows/release.yml passes the signed-tag gate (unsigned or lightweight tags are rejected by CI).

First confirm develop has not moved since step 5 — anything merged there in the meantime is not in this branch and would ship in the next release while its CHANGELOG entry sits under a heading that no longer exists:

git fetch origin
git rev-list --count HEAD..origin/develop   # expect 0

If it is non-zero: git merge origin/develop, move the newcomers' [Unreleased] entries under ## [<NEW_VERSION>], re-run steps 4, 4b and 10 — 4b because the newcomers are user-facing features that just entered this release and each needs its LIVE_VERIFICATION_v<NEW_VERSION>.md row (v0.68.0 shipped #672 this way and the ledger had no row until a reviewer supplied one) — amend or add to the step 11 commit, and only then tag. Exception: if the only newcomers are docs(sponsors): refresh hall of fame commits from .github/workflows/sponsors.yml (a daily bot that cannot see a release freeze), git merge origin/develop and move on — they carry no [Unreleased] entry, no 4b row and nothing to re-verify. If a tag was already created locally, git tag -d v<NEW_VERSION> and re-sign it on the merged head — this is safe only while the tag is unpushed (see the NEVER force-push a release tag reminder below). develop can still move between this check and the step 13 push; that cannot corrupt the tag, it only means a late commit ships in the next release — re-run the rev-list immediately before pushing if you care.

git tag -s v<NEW_VERSION> -m "v<NEW_VERSION>"

Signing requirements:

  • SSH signing (preferred): git config --global gpg.format ssh + user.signingkey pointing at your public key.
  • GPG: any registered GPG key works.
  • Run git config --global user.signingkey to confirm a key is configured.

Confirm the tag actually carries a signature (this is what CI checks):

git cat-file -p v<NEW_VERSION> | grep -c "BEGIN SSH SIGNATURE"   # expect 1 (or "BEGIN PGP SIGNATURE" for GPG)

Benign local-verify error: git tag -v v<NEW_VERSION> may fail with gpg.ssh.allowedSignersFile needs to be configured. This is a local verification-config gap only — the tag IS validly signed and CI still passes (CI greps for the signature header, above). To make local verify work once and for all, create an allowed-signers file (<your-email> ssh-rsa AAAA...) and run git config --global gpg.ssh.allowedSignersFile <path>. See the release-signing memory for the exact recipe. Do NOT treat this error as a signing failure.

13. Push the tag first (bypasses branch protection; triggers the CI release workflow immediately):

⚠ POINT OF NO RETURN — confirm with the user before this push. Pushing the tag immediately triggers .github/workflows/release.yml → PyPI publish + public GitHub Release. A pushed release tag must NOT be force-replaced (ship a PATCH instead). Get an explicit go-ahead, then push.

git push origin v<NEW_VERSION>

CI will start building the release. Watch https://github.com/ffroliva/gflow-cli/actions. Wait for the Release run to report completed / success and confirm the GitHub Release published before continuing (gh release view v<NEW_VERSION>).

14. Push the release branch and open the PR.

git push -u origin chore/release-v<NEW_VERSION>
gh pr create --base main --head chore/release-v<NEW_VERSION> \
  --title "chore(release): v<NEW_VERSION>"

Wait for PR CI to go green (gh pr checks <N> --watch). The SonarCloud analysis check must be green (gate passed) — not just the test matrix. If it is red or you want the verdict, run /gflow:sonar <N> and drive it to zero before merging.

Before merging, leave the release worktree and remove it — --delete-branch deletes the local branch too, and git refuses to delete a branch a worktree has checked out (error: cannot delete branch … used by worktree). The branch is pushed, so nothing is lost:

cd <main checkout>                                   # e.g. C:/development/github/gflow-cli
git worktree remove --force .claude/worktrees/release-v<NEW_VERSION>
git worktree prune

Then merge with a merge commit — never squash:

gh pr merge <N> --merge --delete-branch

On Windows the removal can fail on the worktree's .venv file lock. git worktree prune does not help — it only forgets worktrees whose directory is already gone, so the branch stays held and --delete-branch still fails. In that case merge without it and delete the remote ref explicitly; remove the directory and the local branch later (CLAUDE.md § Worktrees):

gh pr merge <N> --merge
git push origin --delete chore/release-v<NEW_VERSION>

NEVER --squash this PR. Because the branch was cut from develop, the PR carries the entire batch of unreleased integration commits. A squash collapses them into one opaque commit on main and destroys that history. --merge preserves it. (The release workflow already ran from the tag push in step 13 — this PR is to bring the bump commit + integration history onto main.)

15. Back-merge main into develop.

After the release PR is merged, bring the bump commit back to develop so branches stay aligned. This runs in the main checkout (step 14 already returned you there) — develop is checked out there, and git refuses git checkout develop from any other worktree (fatal: 'develop' is already used by worktree at …):

cd <main checkout>
git checkout develop
git pull origin develop
git fetch origin main
git merge origin/main --no-ff -m "chore: back-merge main (v<NEW_VERSION>) into develop"
git push origin develop

If there are conflicts (rare — only if develop has commits that touched the same lines as the bump), resolve them, keeping develop's unreleased work and main's version bump.

16. Report.

Tell the user:

  • Tag push triggered .github/workflows/release.yml.
  • Watch https://github.com/ffroliva/gflow-cli/actions for the release workflow.
  • On success: PyPI publish + GitHub Release with auto-generated notes.
  • On failure (most common: PyPI Trusted Publishing not yet configured): point to https://pypi.org/manage/account/publishing/.
  • develop is now synced with main (back-merge done in step 15).
  • The release worktree is gone (step 14); if Windows held its .venv, name the directory that still needs a manual delete.
  • Leftovers swept: run uv run python scripts/dev/cleanup.py (dry run), then --apply (add --remote to delete merged same-repo branches on origin), and paste its summary.
  • Message the sessions you announced to in step 5: the back-merge has landed, develop is open again.
  • Next development cycle starts on develop — open ## [Unreleased] in CHANGELOG is ready.

Pipeline Continuation (Next Step Handoff)

Upon completing a Release:

  1. Proactively announce: "Release v<NEW_VERSION> shipped to PyPI & GitHub Releases! Back-merge to develop complete. Next step: Phase 1 Triage (/gflow:issue-assessment <N>) for the next development cycle."

Critical reminders

  • ALWAYS cut the release branch from develop, not main — develop carries the work.
  • NEVER --squash the release PR into main — it destroys the integration history the branch carries. Use --merge.
  • NEVER skip the main → develop back-merge (step 15) — skipping it guarantees conflicts at the next release.
  • NEVER add Co-Authored-By: Claude (or any AI co-author) to the release commit.
  • NEVER force-push a release tag once it's on GitHub. Ship a PATCH fix instead.
  • NEVER --no-verify past hooks. Fix the underlying issue.
  • NEVER push directly to main — branch protection will reject it. Always use a PR.
  • A git tag -v allowedSignersFile error is benign (verify-only) — the tag is still signed and CI passes. Don't treat it as a failure.
  • CONFIRM with the user before the step 13 tag push — it's the irreversible PyPI + public Release trigger.
  • If quality gates fail at step 4, STOP. Surface the failures to the user.
  • If doc-review fails at step 10, STOP. Fix before committing.

See also

  • RELEASE.md — full release protocol, prerelease policy, and checklist
  • README § Releases — release policy and cadence
  • PLAN § Phase 5 — first-release exit criteria

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.