release
GitHub release creation for cc-foundry plugins: per-plugin tags, release notes as the changelog, post-merge publishing, and moving the released plugins' XERJ Corpus Hub pins. Invoke whenever task invo
SKILL.md.claude/skills/release/SKILL.md
GitHubPlugin Releases
Every plugin version bump that lands on master ships with a GitHub release. The release notes are the plugin's changelog — there are no CHANGELOG.md files (see ADR 1.5). A merged version bump without a release is an incomplete ship.
Before the Push
Run vp run cli check --pre-push and push only when it reports no error. It fails on a plugin whose shipped files
changed since origin/master while its version did not — a change that never reaches an install, because Claude Code
keys its plugin cache on the version.
When to Release
- After every merge into master, check the merged range for plugin version bumps; create one release per plugin whose version changed.
- The user's authorization to merge covers release creation — do not ask a second time.
- Repo-level changes that bump no plugin version (ADRs, root docs, the dev CLI in
src/, CI, this skill) get no release. - Several plugins bumped in one merge → one release each.
Identify What to Release
- Find bumped plugins in the merged range:
git diff <old-origin-head>..origin/master -- '*/plugin.json'. When the old head is unknown, compare each plugin'splugin.jsonversion against its latest<plugin>-v*tag fromgh release list. - For each bumped plugin, resolve:
- the commit on master that changed its
plugin.jsonversion — the release target - the previous release tag
<plugin>-v*, if any — the notes range and compare-link anchor
- the commit on master that changed its
- Collect source material:
git log <prev-tag>..<target> -- plugins/<name>/plus the diff of that range. For a first release, use the commits of the bump itself.
Tag and Title Scheme
- Tag —
<plugin>-v<X.Y.Z>, e.g.the-coder-v1.5.0 - Title —
<plugin> v<X.Y.Z>, e.g.the-coder v1.5.0 - Target — the commit on master that landed the version bump, as its full 40-character SHA. GitHub rejects an
abbreviated SHA with
HTTP 422 Validation Failedand the misleading triotag_name is not a valid tag,Release.target_commitish is invalid,Published releases must have a valid tag. A rebase-merged pull request gives every commit a new SHA, so take the target fromorigin/masterafter the merge, never from the branch. Resolve it withgit rev-parse <short-sha>; a branch name also works. - Never run
git tag—gh release createcreates the tag on the remote at publish time.
Release Notes
Written for people who install the plugin, not for repo contributors.
<prerequisite>
Invoke the-writer:humanize before drafting. All release prose — the lead sentence and every bullet — is written in
its writing mode and passes its self-audit before gh release create. If the skill is unavailable, ask the user to
install the the-writer plugin before proceeding.
</prerequisite>
- Lead with one sentence stating what the release is about.
- Segment by changed component, one
###section per component:### `<name>` skill,### `<name>` output style— same pattern for hooks, commands, and agents. A plugin bumps its version even when a single component moved; the segments tell the installer exactly which parts did. - Plugin-wide changes that belong to no component (README, packaging, licensing) go under a final
### Pluginsection. - Within a segment, prefix each bullet with its change type in bold —
**Added:**,**Changed:**,**Fixed:**,**Removed:**— ordered Added → Changed → Fixed → Removed. - Each bullet states the change and, where not obvious, why it matters to a plugin user.
- Distill from commit messages and the diff. Never paste a commit list, shortlog, or auto-generated notes.
- No session artifacts (test counts, CI status) and no repo-internal jargon that means nothing to an installer.
- End with a compare link when a previous tag exists:
**Full diff:** https://github.com/xobotyi/cc-foundry/compare/<prev-tag>...<new-tag> - First release of a plugin: no compare link; notes cover this version's changes only, not the plugin's history.
Example
Sharpens commit-message validation and adds trailer support.
### `commit-message` skill
- **Added:** `Task:` and `Refs:` trailer validation — malformed trailers now fail validation instead of landing
silently.
- **Changed:** subject-length limit aligned with git conventions: 50 soft / 72 hard (was a flat 72).
### `commit` skill
- **Fixed:** validator crashed on commit messages containing unescaped backticks.
**Full diff:** https://github.com/xobotyi/cc-foundry/compare/git-commit-v1.1.3...git-commit-v1.1.4
Create the Release
Write the notes to a scratchpad file first — --notes-file avoids shell-quoting damage:
gh release create <plugin>-v<X.Y.Z> \
--target <bump-commit-sha> \
--title "<plugin> v<X.Y.Z>" \
--notes-file <path-to-notes.md>
- To stage a release for user review, add
--draft(visible to collaborators only; the tag is created at publish, not at draft creation). Publish withgh release edit <tag> --draft=false— re-set--targetto the bump commit first if the draft was created before the merge.
Verify
gh release view <tag>— confirm title, target, and notes.- Cross-check: every plugin bumped in the merged range has a release;
plugin.jsonversion equals the latest tag.
Move the XERJ Corpus Hub Pins
The XERJ Corpus Hub pins every cc-foundry corpus to a commit, so a release reaches hub users only once the corpora inside that plugin move to its release target. Do this after the releases of a merge are published. The user's authorization to merge covers this pull request as well. Work in this order:
- Find an open refresh pull request:
gh pr list --repo xerj-org/xerj --author xobotyi --base corpus-hub --state open. When one exists, add to its branch instead of opening a second one. - Clone the fork into a new temporary directory, partial and sparse:
Switch to the open pull request's branch from step 1, or creategit clone --filter=blob:none --sparse git@github.com:xobotyi/xerj.git <tmp> git -C <tmp> sparse-checkout set tools/xerj-code/hub tools/packs git -C <tmp> remote add upstream https://github.com/xerj-org/xerj.git git -C <tmp> fetch upstream corpus-hubcc-foundry-pins-<YYYY-MM-DD>fromupstream/corpus-hubwhen there is none. - From the cc-foundry root, after
git fetch origin, run the refresh once per release target, naming the plugins released at that target:
A plugin with no corpus moves nothing; when no manifest changed, delete the clone and stop. When the command reports that the LICENSE changed, stop and ask the user to review the licence, because the hub accepts a changed licence only with a human review.vp run cli xerj-hub-refresh --hub <tmp> --sha <release-target-sha> <plugin>... - Run the hub's CI check in the clone:
python3 -I tools/xerj-code/hub/validate_hub.py. - Commit in the clone with the subject
hub: move cc-foundry pins to <plugin> v<X.Y.Z>, naming every release the commit covers, and never add aCo-Authored-Bytrailer, which xerj's.github/AI_CONTRIBUTIONS.mdforbids. The commit author isxobotyi, the account that signed xerj's CLA. - Push the branch to the fork and open the pull request against
corpus-hub. The body opens with xerj's provenance line, naming the real tool and model:
Then link each release, and list each moved corpus with its old and new pin as the command printed them.Written by an AI coding agent (Claude Code, <model>), run by @xobotyi, who approved this release. - Confirm that
gh pr checks <number> --repo xerj-org/xerjreportsvalidateandverification/cla-signedas passing, then delete the clone.