release
Skill0 starsChanged 29 days agoApache-2.0View on GitHub
Cut a release properly: work out the version from what actually landed, derive the change set from the commit range, check for blockers, write notes someone would want to read, and drive the release out. Use it whenever a version is about to move — "cut a release", "ship 0.19", "tag a new version", "publish the new version", "bump the version", "write the changelog", "what's changed since the last release?", "is this ready to release?". Do NOT do any of it by hand instead: hand-tagging, editing a version string directly, writing notes off `git log`, or calling `gh release create` yourself skips the blocker check and gets the contents wrong, because a search for merged pull requests returns work the release does not contain and only the commit range is correct. This is releasing a version, not merging a change — shipping one piece of work is `/work-on`.
- Deletes or force-pushes
- Commits and pushes
196 other projects use a version of this.
Adopted by
Most recently taken up by
- zxcvbn-ts/zxcvbntheir own version, first seen Nov 9, 2026
- TechbeeAT/jtxBoardtheir own version, first seen Nov 9, 2026
- ddnexus/pagytheir own version, first seen Nov 9, 2026
- zerx-lab/FluxDowntheir own version, first seen Nov 9, 2026
- orioledb/orioledbtheir own version, first seen Nov 9, 2026
- AmruthPillai/Reactive-Resumetheir own version, first seen Nov 8, 2026
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.
MicrosoftSkillcreate-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
GrinvSkillchangelog-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
GrinvSkilldocs-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 AzureSkillcomponent-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 AzureSkilldocumentation-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-devSkilllaunch-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
Other versions196 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 196
- paperclipai/paperclip★ 112ksame name
- modelcontextprotocol/servers★ 92ksame name
- remotion-dev/remotion★ 72ksame name
- traefik/traefik★ 65ksame name
- usememos/memos★ 64ksame name
- tw93/Pake★ 63ksame name
- AmruthPillai/Reactive-Resume★ 45ksame name
- amruthpillai/reactive-resume★ 45ksame name
- reactive-resume/reactive-resume★ 45ksame name
- Yeachan-Heo/oh-my-claude-sisyphus★ 41ksame name
186 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository23 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- briefSkill
- charterSkill
- compSkill
- coordinateSkill
- decisionSkill
- deliverSkill
- designSkill
- executeSkill
- exploreSkill
- inflightSkill
- planSkill
- prdSkill
- private-contentSkill
- public-contentSkill
- review-planSkill
- roadmapSkill
- scopeSkill
- strategySkill
- visionSkill
- work-onSkill
- writing-styleSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
22 rules. 0 are common, 13 are distinctive, and 9 are very specific to this project.
0
Common
Most of the 186 edited versions we read keep these.
13
Distinctive
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 125
If no tag exists, this is the first release -- ask for version.
- Distinctive: line 140
All must pass before proceeding:
- Distinctive: line 143
CI green on HEAD: Check CI status using this fallback chain:
- Very specific: line 145
If state is pending with 0 check runs (common for squash-merge commits where CI ran on the PR branch, not the merge result), fall back to …
- Very specific: line 148
If the latest completed run has conclusion: success, treat as green
- Distinctive: line 149
Only fail if there's an actual failure, not just missing status data
- Very specific: line 153
If any exist, list them and stop.
- Very specific: line 154
Security-labeled PRs: read the labels of every pull request Phase 1 derived and keep the ones carrying security:
- Very specific: line 184
If found, flag them and use AskUserQuestion to decide how each is handled in the release notes, following the pattern in …
- Distinctive: line 189
Standard description (Recommended): when the PR is a hardening change with no exploitable window in a released version
- Distinctive: line 191
Redacted: when the PR fixes a live vulnerability and the detail would arm an attacker before users upgrade
- Distinctive: line 193
Excluded: when naming the change at all would point at an unfixed surface
- Very specific: line 204
Repository-declared checks: run what the repository lists under ## Release checks in its release extension, imported above from …
- Distinctive: line 215
Run these after checks 1-6, which are cheap reasons to stop first.
- Distinctive: line 267
Anything else: stop the release. Name the check (its command and file) and repeat the last lines of its output.
- Distinctive: line 324
If the user picks a different version than the one used to draft the notes, update the notes title to reflect the chosen version.
- Very specific: line 395
If draft creation fails, check gh auth status, close the session abandoned and stop.
- Distinctive: line 404
If found, dispatch with three inputs (version, tag, ref):
- Distinctive: line 413
Skill-only mode: If no release workflow found, print:
- Distinctive: line 428
Use timestamp correlation to match the dispatched run (created within 30 seconds of dispatch time)
- Very specific: line 431
Success: Verify draft promoted via gh release view v<version> --json isDraft.
- Very specific: line 438
When --dry-run is passed:
“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.
Files
!${CLAUDE_PLUGIN_ROOT}/scripts/skill-preflight.sh release 2>&1 || true
@.claude/shirabe-extensions/release.md @.claude/shirabe-extensions/release.local.md
Release
Cut a release. Handles version selection, precondition checks, blocker identification, release notes, draft creation, workflow dispatch, and monitoring.
Invocation
/release [version] [--dry-run]/release-- analyzes commits and recommends a version/release 0.3.0-- uses the specified version (v0.3.0also accepted)/release --dry-run-- previews without side effects
Phases
Phase 1: Version Analysis and Release Contents
Establish what the release contains, once, and keep it in the release's koto
session, release-<topic>. Phase 2's security check and Phase 3's notes are
both consumers of the pull request set derived here; neither re-derives it.
Later phases run as separate shell invocations, so a shell variable set here
is gone by Phase 2 and an empty list there would pass the security check by
finding nothing. The session keys are the release's membership, and /release
writes no file to the wip/ staging folder.
topic=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" topic) || exit
echo "Release session: release-$topic"
"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" open release "$topic" || exit
"${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" membership "$topic" || exit
echo "In the release, not in a pull request:"
"${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" read "$topic" unattributed<topic> below is what release-state.sh topic prints: the repository's
owner and name from gh repo view, joined and lowercased (tsukumogami-shirabe
for this repository). A later phase recomputes it with the same command.
openattaches to a liverelease-<topic>, replaces a finished one, or creates it. A non-zero exit stops the run with the script's message: 127 or 69 means koto is missing or too old, 6 means HEAD is detached, and the skill never falls back to files.refused=branch_mismatch(exit 5) means arelease-<topic>is still live from an interrupted run on another branch. Every run closes its session, so a live one is a run that never finished. Stop and tell the author to close it and run/releaseagain:"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" close release-<topic> abandonedmembershipcomputes the range (<last v* tag>..HEAD, orHEADwhen no tag exists), the pull requests in it and the commits that name none, and writes them aswork/range,work/prsandwork/unattributed, replacing whatever an earlier run left. It also removes awork/notes.mdan earlier run left, so a crashed run's notes can't be published. A release with no(#N)subjects gets an emptywork/prs, which reads back as an empty list.read <topic> <range|prs|unattributed|notes>prints one key. A key that is absent exits 1 saying Phase 1 did not run; stop there.
The membership step's rules, for the record:
- The range's guard is load-bearing:
..HEADis a range git ACCEPTS, and it resolves to zero commits, so without the guard a first release derives an empty set silently rather than failing. - The pull requests are the numbers in
(#N)suffixes, GitHub's squash-merge title, sorted and unique.grepexiting 1 on no match is a legal outcome, not a failure. - The unattributed commits are the release chore commits and anything pushed straight to the base branch.
work/prs is the release's membership, computed here and nowhere else; Phase
2 and Phase 3 read it. Do not re-derive it from a gh pr list --search
"merged:>..." query, for two reasons that compound. GitHub reads a bare
merged:>YYYY-MM-DD as "after the end of that day", so a date-shaped bound
silently drops every pull request merged on the previous tag's own calendar
day. And merged:> has no upper bound at all, so it also credits anything
merged after the release point -- against v0.16.0 with a release point of
v0.17.0 it returns 18 pull requests where the range holds 13. The commit
range has both bounds and no date semantics to get wrong.
The parse assumes GitHub wrote the (#N) suffix. That holds while the
repository squash-merges (allow_merge_commit and allow_rebase_merge both
false, with squash_merge_commit_title: PR_TITLE) -- a live repository setting,
not something this file can enforce, and one an adopting repository may not
share. The pattern is anchored to the end of the subject, so a pull request
whose own title ends in a parenthesized number is not misread.
The unattributed list printed above is what makes that assumption checkable
rather than assumed. A handful of entries is normal: every release carries the
version-bump chore commit. A list that is suddenly most of the range means the
suffix is no longer being written -- the merge strategy changed, or work is
landing by direct push -- and the pull request set is incomplete. Stop and
reconcile by hand before releasing when that happens (close the session
abandoned first); it is the one signal that the derivation has gone quiet
rather than empty. Printing it here, before the notes are drafted, is also what
makes it useful rather than a footnote.
If no tag exists, this is the first release -- ask for version.
Count commits by prefix over the range in work/range
(release-state.sh read <topic> range):
| Prefix | Bump signal |
feat!:, fix!:, BREAKING CHANGE | major |
feat: | minor |
fix:, docs:, chore:, ci:, refactor:, test: | patch |
Normalize version input: accept both 0.3.0 and v0.3.0.
Phase 2: Precondition Checks
All must pass before proceeding:
- Clean working tree:
git status --porcelainis empty - CI green on HEAD: Check CI status using this fallback chain:
- First try commit status:
gh api repos/{owner}/{repo}/commits/{sha}/status - If state is
pendingwith 0 check runs (common for squash-merge commits where CI ran on the PR branch, not the merge result), fall back to checking the latest workflow runs on the branch:gh run list --branch main --limit 3 --json conclusion - If the latest completed run has
conclusion: success, treat as green - Only fail if there's an actual failure, not just missing status data
- First try commit status:
- No existing tag:
git tag -l v<version>returns empty - No existing draft:
gh release view v<version>returns 404 - No release blockers: Query
gh issue list --label blocks-release --state open. If any exist, list them and stop. Also checkgh issue list --label priority:critical --state open. Security-labeled PRs: read the labels of every pull request Phase 1 derived and keep the ones carrying
security:topic=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" topic) || exit prs=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" read "$topic" prs) || exit printf '%s\n' "$prs" | while read -r pr; do [ -n "$pr" ] || continue gh pr view "$pr" --json number,title,labels \ --jq 'select([.labels[].name] | index("security")) | "\(.number) \(.title)"' \ || echo "UNRESOLVED #$pr: in the release range but not a pull request in this repo" doneThe
readruns first, outside the loop, so an absent key stops the run ("Phase 1 did not run") instead of passing the check on an empty list.The set is the one Phase 1 derived and Phase 3 writes the notes from, so a pull request cannot reach the notes without also reaching this check. That is the point: this check fails permissively -- a pull request it does not see is published with a standard description and nobody is prompted -- so it must not be reading a different, narrower set than the notes are.
The
|| echois not decoration. A(#N)suffix can name something that is not a pull request in this repository: a revert, a hand-written subject, or a commit cherry-picked from elsewhere.gh pr viewexits non-zero on those, and without the branch the number would be skipped in silence -- which is the failure mode this whole check exists to close. Treat anyUNRESOLVEDline as something to look at by hand before releasing, not as a pass.If found, flag them and use AskUserQuestion to decide how each is handled in the release notes, following the pattern in
${CLAUDE_PLUGIN_ROOT}/references/decision-presentation.md. Read the PR before asking, and recommend one of the three treatments rather than listing them:- Standard description (Recommended) when the PR is a hardening change with no exploitable window in a released version
- Redacted when the PR fixes a live vulnerability and the detail would arm an attacker before users upgrade
- Excluded when naming the change at all would point at an unfixed surface
Ground the recommendation in what the PR actually did -- cite the change and whether an affected version already shipped. Name the tiebreaker every time, not only when the call is close: default to the more conservative treatment. State it even when the call reads as clear-cut, and say that it did. A reader cannot tell from a recommendation alone whether it was near the line, so the rule that produced it is part of the recommendation; and the one case where the tiebreaker matters most is the one where the recommending agent was most confident it did not.
Repository-declared checks: run what the repository lists under
## Release checksin its release extension, imported above from.claude/shirabe-extensions/release.md(tracked) and.claude/shirabe-extensions/release.local.md(untracked, this host only). Each bullet under that heading is one check: the command in its backticks, which starts with a repository path. No extension file, or no## Release checksheading in either, means there are no declared checks: print nothing for this step and go on. Declared checks run in--dry-runtoo, because dry-run runs this phase; a check that must not write anything readsRELEASE_DRY_RUN.Run these after checks 1-6, which are cheap reasons to stop first. A declared check can be the slowest thing a release does.
a. Show the code the checks will run. From the repository root:
git diff --stat "$RANGE" -- scripts/ skills/ .claude/shirabe-extensions/RANGEis the release range Phase 1 recorded; read it back, since this is a new shell:RANGE=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" read <topic> range) || exit. When it readsHEAD(a first release, no tag) printgit diff --stat "$(git hash-object -t tree /dev/null)" HEAD -- scripts/ skills/ .claude/shirabe-extensions/instead.b. For each check, in order (
release.md's first, thenrelease.local.md's), print the command and the file it came from. A check fromrelease.local.mdis labelled local: that file is untracked and nobody reviewed it, so ask with AskUserQuestion before running it, and skip it on a no.c. Run it from the repository root (
git rev-parse --show-toplevel), exactly as written, with these variables set for the command:Variable Value RELEASE_VERSIONthe version Phase 1 recommends, or the one given, as bare X.Y.Z(strip a leadingv). Phase 3 may still change it; the## Release assetsstep in Phase 4 gets the confirmed oneRELEASE_LAST_TAGthe last release tag: RANGEabove without its..HEAD, empty whenRANGEisHEAD(a first release)RELEASE_DRY_RUN1under--dry-run,0otherwiseRELEASE_CONFIRMED_DROPSthe empty string, so a value inherited from the environment can't pre-confirm anything For example, with the check
scripts/example-check.sh --flag:RELEASE_VERSION=0.3.0 RELEASE_LAST_TAG=v0.2.0 RELEASE_DRY_RUN=0 RELEASE_CONFIRMED_DROPS= scripts/example-check.sh --flagA check can run for a long time. Give it the longest timeout the session allows, or run it in the background and wait for it to exit; never treat a timeout as a pass.
d. Act on the exit code.
- 0: the check passed. Go to the next one.
- 5: the check needs a person to confirm something before the
release goes on. Its last output line reads
confirm: <NAME>=<value>. Show the check's output and ask with AskUserQuestion whether to proceed, naming what the check reported. On a yes, run the same command again with<NAME>=<value>set in place of the empty value (do not ask again about a local check already approved). That run's exit 0 continues; any other exit, 5 included, stops the release as below, so a confirmation is asked for once per check. On a no, or when the question can't be put to a person, stop the release. - Anything else: stop the release. Name the check (its command and file) and repeat the last lines of its output.
A check that exits 5 without a
confirm:last line is treated as any other non-zero exit, and so is aconfirm:line whose<NAME>is not an upper-case variable name ([A-Z_][A-Z0-9_]*) or whose<value>holds anything beyond letters, digits,.,_,-and,: the value goes onto a command line, so it is never passed through when it could be read as shell.
Report the specific failure and stop on any check, closing the session first:
"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" close release-<topic> abandoned.
Phase 3: Release Notes and Version Confirmation
Generate notes, present them, and confirm the version with the user.
Gather commits over the range Phase 1 stored:
topic=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" topic) || exit range=$("${CLAUDE_PLUGIN_ROOT}/skills/release/scripts/release-state.sh" read "$topic" range) || exit git log --oneline "$range"Each subject carries the conventional-commit type, the description, and the
(#N)suffix, so this one read has everything the notes are written from.- Cross-check against
work/prsfrom Phase 1 (release-state.sh read <topic> prs) -- the release's membership. Every number in it must appear in a subject step 1 gathered, and no other pull request may be credited. It is a check, not a second gather: the key was derived from this same range, so a disagreement means one of the two reads went wrong and the notes should not be written until you know which. - Group by type (features, fixes, other)
- Draft user-facing notes:
- Focus on user impact
- One sentence per change
- Highlight breaking changes prominently
- Handle security-labeled PRs per user's Phase 2 decision
- Print the notes in chat so the user can read them,
followed by the output of
release-state.sh read <topic> unattributedunder a short heading such as "In the release, not in a pull request". Phase 1 already printed this list; repeating it here puts it in front of the author at the moment they are deciding whether the notes are complete. Use AskUserQuestion to present the recommended version with alternatives. Include the commit analysis from Phase 1:
Based on 3 feat, 8 fix, and 1 breaking change since v0.2.0:
- v0.3.0 (minor) -- Recommended because new features were added
- v1.0.0 (major) -- if the breaking change warrants a major bump
- v0.2.1 (patch) -- if the features are minor enough to treat as a patch
- Custom version -- enter a specific version
If the user picks a different version than the one used to draft the notes, update the notes title to reflect the chosen version.
- Allow the user to request edits to the notes before proceeding. Only
move to Phase 4 after the user confirms both version and notes. If the
author declines the version, stop and close the session
abandoned.
Phase 4: Draft Release
Phase 3 produces the notes in chat. Store the confirmed notes in the session,
then hand gh release create a copy of them in a scratch directory that is
removed when the command returns. Write the notes to $dir/notes.md with the
Write tool, then:
dir=$("${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" scratch) || exit
# write the confirmed notes to "$dir/notes.md", then:
"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" put release-<topic> work/notes.md "$dir/notes.md"
rm -rf "$dir"Then create the draft:
notes_dir=$("${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" scratch) || exit
notes_file=$("${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" get release-<topic> work/notes.md "$notes_dir") || { rm -rf "$notes_dir"; exit 1; }
rc=0
gh release create "v<version>" \
--draft \
--title "v<version>" \
--notes-file "$notes_file" || rc=$?
rm -rf "$notes_dir"
exit "$rc"The draft survives workflow failures and is editable in the GitHub UI.
Release assets. Once the draft exists, and only when this isn't a dry
run, run each bullet under ## Release assets in the release extension
(release.md, then release.local.md; ask before a local one, as in Phase 2
step 7). No heading means nothing to attach. Print each command and its file,
then run it from the repository root with RELEASE_VERSION set to the
version confirmed in Phase 3 (bare X.Y.Z), RELEASE_LAST_TAG set as in
Phase 2 (read the range back with release-state.sh read <topic> range, as
Phase 2 step 7a does, since this is a new shell),
RELEASE_DRY_RUN=0, and RELEASE_CONFIRMED_DROPS set to the empty
string, as in Phase 2.
Exit 0 with a last output line
asset: <path>names a file to attach. The path must name an existing regular file and hold no quotes,$, backticks or line breaks; anything else is reported as a failed asset, not uploaded.gh release upload "v<version>" "<path>" --clobber- Exit 0 without an
asset:line means there is nothing to attach. - A non-zero exit, or a failed upload, does not stop the release: the draft
already exists. Report it with the command to retry (the item's command, or
the
gh release uploadline above), and quote the extension's note on what that item's failure costs, if it gives one.
With the draft created and the assets handled (or none declared), the run's state is no longer needed. Close the session; its keys stay readable:
"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" close release-<topic> doneIf draft creation fails, check gh auth status, close the session abandoned
and stop; the stored notes stay readable (release-state.sh read <topic>
notes). Phases 5 and 6 read nothing from the session.
Phase 5: Workflow Dispatch
Detect the release workflow by scanning .github/workflows/*.yml for files
referencing tsukumogami/shirabe/.github/workflows/release.yml in uses: lines.
If found, dispatch with three inputs (version, tag, ref):
gh workflow run <workflow-file> \
-f version=<version> \
-f tag=v<version> \
-f ref=mainSkill-only mode: If no release workflow found, print:
No release workflow detected. To complete the release:
git tag -a v<version> -m "Release v<version>" && git push origin v<version>
Draft release with notes: <url>Phase 6: Monitoring
- Record dispatch timestamp
- Poll
gh run list --workflow=<name> --limit=5 --json databaseId,createdAt,status,conclusionevery 10 seconds - Use timestamp correlation to match the dispatched run (created within 30 seconds of dispatch time)
- Up to 5 minutes:
- Success: Verify draft promoted via
gh release view v<version> --json isDraft. Print release URL. - Failure: Print details. Suggest
gh run view <id> --log-failed. - Timeout: Print run URL: "Workflow still running -- monitor at
<url>"
- Success: Verify draft promoted via
Dry-Run Mode
When --dry-run is passed:
- Phases 1-3 run normally (version analysis and release contents, checks, notes + confirmation). Phase 1's derivation reads git and nothing else, so it runs on this path unchanged.
- The repository's declared checks (Phase 2 step 7) run, with
RELEASE_DRY_RUN=1, and stop the dry run the same way they would stop a release. - Phase 4-6 are skipped (no draft, no dispatch), so no
## Release assetscommand runs and nothing is attached. The session is closeddoneat the end of the dry run:"${CLAUDE_PLUGIN_ROOT}/scripts/skill-session.sh" close release-<topic> done. - Print what would happen: which files change, what tag, what dev version
Error Recovery
| Phase | Failure | Recovery |
| 1 | open prints refused=branch_mismatch | A release-<topic> is live from an interrupted run on another branch. Run skill-session.sh close release-<topic> abandoned, then /release again |
| 1 | work/prs is empty but the range has commits | Read work/unattributed (release-state.sh read <topic> unattributed): if every commit is listed there, the repository is not writing (#N) suffixes (the merge strategy changed) and the derivation's assumption no longer holds |
| 1 | work/range holds HEAD on a repo that has tags | git describe found no v* tag from this commit -- check you are on the release branch and that tags are fetched (git fetch --tags) |
| 2, 3 | read exits 1 saying Phase 1 did not run | The key is absent: Phase 1's membership step did not complete in this session. Run Phase 1 again |
| 2 | UNRESOLVED #N from the security check | The (#N) names something that is not a pull request here (a revert, a hand-written subject, a cherry-pick). Check it by hand before releasing |
| 2 | Dirty tree | git stash or commit |
| 2 | CI failing | Fix and push |
| 2 | Tag exists | git push --delete origin v<version> |
| 2 | Draft exists | gh release delete v<version> --yes |
| 2 | Blockers open | Resolve the listed issues first |
| 2 | A declared release check exits non-zero (other than 5) | The step repeated the last lines of the check's output; fix what they report, and run the release again; the check is named with the file that declared it |
| 2 | A declared check exits 5 and the answer is no, or nobody can answer | The release stopped on purpose. Look into what the check reported; run the release again, interactively, to confirm it |
| 4 | A ## Release assets command fails or its upload fails | The draft stands. Re-run the command from the repository root with the same RELEASE_* values, then gh release upload v<version> <path> --clobber |
| 4 | Draft creation fails | Check gh auth status; the notes are still in work/notes.md. Close the session abandoned and run again |
| 5 | Dispatch fails | Check workflow exists and permissions |
| 6 | Workflow fails | gh run view <id> --log-failed |
| 6 | Timeout | Check URL printed at timeout |
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
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.

