You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A project skill at .agents/skills/release/ (registered through .claude/skills/release, the same
symlink convention as every other skill here) that covers the path this repo actually takes to ship:
sweep the pull requests that are genuinely ready, then cut and publish a version.
It exists because the sequence has several traps that are individually small and collectively
expensive, and all of them were hit for real while cutting v0.1.0:
mergeStateStatus: BLOCKED on a green PR is the review ruleset wanting one approval, not a defect
— but a red check is not something --admin should paper over, and the two look similar from the
outside.
package.json, herdr-plugin.toml and the tag must all agree, asserted independently by CI's version-check and by the release workflow.
An empty CHANGELOG section fails the release after the workflow has installed a cross-compile
toolchain and built three binaries, with the tag already public.
CONTRIBUTING.md step 3 is stale: the workflow wipes tui/prebuilt/ and rebuilds from the tag's
own source, so there is nothing to build locally.
What's in it
SKILL.md — the two phases, with the reasoning rather than a command list, plus the repo's
specific traps written down.
scripts/pr-status.sh — a merge-readiness verdict per open PR: READY / STALE / WAIT / HOLD. Two of those are things the GitHub API does not surface on its own. A PR with zero
checks is HOLD, because absence of evidence is not evidence. And STALE catches a PR whose
checks passed against a base that has since moved — reported as green and mergeable, while those
tests never ran against the tree that would actually ship. Fork branches are compared as owner:branch, since a bare name silently resolves to nothing and makes an outside contribution
look fresher than it is.
scripts/release-preflight.sh — every pre-tag condition in one pass, reporting all of them
rather than stopping at the first, so you see whether it is one typo or the wrong branch entirely.
The sharpest trap, which this skill also documents
An open PR's changelog hunk is written against ## [Unreleased]. Once a release renames that
heading, the hunk's context still matches — so the bullets land inside the published version's
notes. Git reports a clean merge, version-check only compares the two version files, and nothing
fails: the shipped release quietly claims things it never contained, and the next release's section
comes out empty and hard-fails the workflow after its tag is public. main is in exactly that state
right now (no [Unreleased] heading), which is how the trap was found. The skill's answer is to keep
an empty heading alive and to check where a merged PR's bullets actually went before tagging.
Test plan
pr-status.sh run against the live open PRs — correctly reported both as STALE (2 commits
behind main, one with 6-day-old checks) where a naive read called them green
release-preflight.sh 0.1.0 correctly refused an already-taken tag; 0.2.0 correctly reported
the missing [Unreleased] heading and the version mismatch
Exercised end to end by three dry-run agents against this repo; two skill defects they found
(the [Unreleased] guidance being backwards, and no staleness detection) are fixed here
Next real release run confirms the merge sweep and cut behave as written
Pushed one more fix, found by using this skill to cut v0.2.0 rather than by reading it.
A fork PR's CI is held without saying so.#21 came from a first-time contributor, so its
workflows sat at action_required until released. The check rollup does not report that — it simply
had fewer entries, in that case only verification/cla-signed. To pr-status.sh as written that was
one check, green, none running, none failed: READY. The single most dangerous shape the script
can produce, since it looks proven and nothing had run.
It now probes the head sha's workflow runs for held ones and returns HOLD with the count, and SKILL.md says what releasing one actually means — an outsider's code executing in CI, so read the
diff first, then approve and wait for the real checks.
Verified as far as it currently can be: the probe's query returns the right run for a given head sha.
The action_required branch itself is logic-only for now — I had already approved #21's run, so that
state no longer exists to test against. It gets its real exercise on the next fork PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A project skill at
.agents/skills/release/(registered through.claude/skills/release, the samesymlink convention as every other skill here) that covers the path this repo actually takes to ship:
sweep the pull requests that are genuinely ready, then cut and publish a version.
It exists because the sequence has several traps that are individually small and collectively
expensive, and all of them were hit for real while cutting
v0.1.0:mergeStateStatus: BLOCKEDon a green PR is the review ruleset wanting one approval, not a defect— but a red check is not something
--adminshould paper over, and the two look similar from theoutside.
package.json,herdr-plugin.tomland the tag must all agree, asserted independently by CI'sversion-checkand by the release workflow.toolchain and built three binaries, with the tag already public.
CONTRIBUTING.mdstep 3 is stale: the workflow wipestui/prebuilt/and rebuilds from the tag'sown source, so there is nothing to build locally.
What's in it
SKILL.md— the two phases, with the reasoning rather than a command list, plus the repo'sspecific traps written down.
scripts/pr-status.sh— a merge-readiness verdict per open PR:READY/STALE/WAIT/HOLD. Two of those are things the GitHub API does not surface on its own. A PR with zerochecks is
HOLD, because absence of evidence is not evidence. AndSTALEcatches a PR whosechecks passed against a base that has since moved — reported as green and mergeable, while those
tests never ran against the tree that would actually ship. Fork branches are compared as
owner:branch, since a bare name silently resolves to nothing and makes an outside contributionlook fresher than it is.
scripts/release-preflight.sh— every pre-tag condition in one pass, reporting all of themrather than stopping at the first, so you see whether it is one typo or the wrong branch entirely.
The sharpest trap, which this skill also documents
An open PR's changelog hunk is written against
## [Unreleased]. Once a release renames thatheading, the hunk's context still matches — so the bullets land inside the published version's
notes. Git reports a clean merge,
version-checkonly compares the two version files, and nothingfails: the shipped release quietly claims things it never contained, and the next release's section
comes out empty and hard-fails the workflow after its tag is public.
mainis in exactly that stateright now (no
[Unreleased]heading), which is how the trap was found. The skill's answer is to keepan empty heading alive and to check where a merged PR's bullets actually went before tagging.
Test plan
pr-status.shrun against the live open PRs — correctly reported both asSTALE(2 commitsbehind
main, one with 6-day-old checks) where a naive read called them greenrelease-preflight.sh 0.1.0correctly refused an already-taken tag;0.2.0correctly reportedthe missing
[Unreleased]heading and the version mismatch(the
[Unreleased]guidance being backwards, and no staleness detection) are fixed here