Use Keep a Changelog + GitHubFor any projectgeneral-purpose release automation skill.
Design principles: A single confirmation gate. The user reviews and approves the entire plan exactly once. After approval, it runs without interruption from file rewriting → commit → branch push → PR creation/update → merge to default branch → tag on default branch → GitHub Release.
Definition of Release complete: A release is not complete merely because "a tag and GitHub Release were created". It is considered complete when the target work and release bump have been merged into the default branch (usually main), the release tag points to a commit reachable from the default branch, and the GitHub Release has published that tag.
PR ready vs release ready
In Harness V2, do not confuse PR closeout with release closeout.
| Gate | Meaning | Required conditions | Stop lane |
|---|---|---|---|
| PR ready | The branch is reviewable and a merge decision can be made | harness-review APPROVE, focused tests PASS, evidence pack complete (accepted/rejected findings, tests, release-preflight warnings handling, residual risk) |
[lane:fast] / [lane:gate] may stop here |
| release ready | The public distribution path passes preflight | PR ready conditions + version surface sync + tag + GitHub Release + CI/public artifact verification | [lane:release] only |
- PR ready は
harness-reviewis determined by APPROVE + evidence pack.harness-reviewDo not push / PR / merge from there. - release ready は
harness-releaseOnly Preflight / Post-Gate determine this. version bump / tag / GitHub Release are exclusive to the release lane. - local tests passed alone does not mean PR ready or release ready (
not_observed != absent)。
Literal invocation note: The entry point for this skill is
harness-release,/release,/release patch,/release --dry-runuse literal commands such as this as-is.
Relationship with the CC runtime hard floor
The runtime hard floor of Claude Code 2.1.183+ structurally denies GitHub CLI release publish commands (Anthropic product specification, cannot be overridden bysettings.json の permissions.ask ). This skill does not perform publish itself, and delegates to.github/workflows/release.yml (tag push trigger). The skill completes its responsibility up to tag push, and then verifies publication by the workflow with scripts/release-verify-publish.sh .
Revert conditions: If CC provides a user explicit approval path for the runtime hard floor, consider returning the direct publish step to Post-Gate.
Bare invocation contract
if $ARGUMENTS == "": → Interpret it as "I want to commit the work so far, complete the PR/main reflection, and release", and execute Review Gate detection → Automatically proceed to Step 0 (Review Gate) only when the target work can be narrowed down to one → If the target is unclear or there is no review state, present options with AskUserQuestion before proceeding
On the first response when called without arguments, always output the following literal marker:
RELEASE_AUTOSTART: target=<work-summary>, base_ref=<ref>, mode=<patch|minor|major|auto>
"The task is unclear", "I will wait for instructions", "There is no task", and "I will wait for additional instructions" are prohibited behaviors.
Output Contract (P35: countermeasure for the "looks stopped" UX)
At skill conclusion, in the output's last linealways include the following literal:
↑この結果は Claude が要約します。Enter キーで次へ進むか、新規 prompt で別の指示を出してください。
This is <local-command-stdout> An explicit instruction for the UX problem where the user feels it has "stopped" when displayed as a text response via (patterns.md P35).
harness-release / /release When only is entered, this is
"I want to commit the work so far and complete it through PR/main reflection for release" It is treated as meaning that.
The old expression "I want to commit the work so far and release it" has the same intent, but the completion conditions must always include PR/main reflection.
You must not stop with "there are no tasks" or "I will wait for instructions".
In a bare release, before the normal release preflight, execute Review Gate と Work Commit Gate .
Review Gate は release ready For .[lane:fast] / [lane:gate] For work that only needs the PR ready of , do not launch harness-release unless the user explicitly requests release.
git status --porcelainとgit log @{upstream}..HEAD/main..HEADCheck and identify the target of "the work so far".claude/state/review-result.jsonと.claude/state/review-approved.jsonCheck and confirm that the target work hasAPPROVEa completed review and evidence pack- If there is no APPROVE-completed review, check with
AskUserQuestion. - If the user selects "start from review",
harness-reviewlaunch , and do not proceed to release untilAPPROVE. harness-reviewがREQUEST_CHANGESIf returns , hold the release andharness-workfix with and then re-executeharness-review. Repeat thisAPPROVEloop untilharness-reviewがAPPROVEAfter returns , create a work commit of the working tree- After the working tree becomes clean, proceed to the normal release preflight / confirmation gate / PR merge / tag / GitHub Release
Review Gate AskUserQuestion
harness-release If review approval cannot be confirmed at execution time, do not release by guessing.
Ask the following Ask.
question: "harness-release は今までの作業をコミットしてリリースしますが、この作業の APPROVE review が見つかりません。どう進めますか?"
options:
- label: "レビューから開始 (Recommended)"
description: "harness-review を実行し、APPROVE になった場合だけ commit/release へ進みます。"
- label: "release dry-run"
description: "ファイルを書き換えず、release 計画と不足 gate だけ確認します。"
- label: "中止"
description: "review も release も行わず止めます。"
If the user selects "start from review", within the same session start from harness-review .
harness-review The target determination of follows the bare review contract on the harness-review side.
If the review is APPROVE then, as is, harness-release it returns to the Work Commit Gate of .
If the review is REQUEST_CHANGES the release is held, and after fixing withharness-work , re-execute harness-review .
This post-fix re-review loop continues until APPROVE .
You may return to the user only in the following cases.
- The fix requires decisions such as the specification source of truth / Plans.md / API / permission / migration / billing, and
AskUserQuestionis required - There are multiple fix policies, and which one is adopted changes user value or compatibility
- When the user uses Ask to
release dry-runor中止selected
REQUEST_CHANGES You must not use alone as the final stopping reason.
Work Commit Gate
In a bare release, if there are uncommitted changes in the working tree, create a reviewed work commit first, separately from the release version bump commit.
git status --short
git diff --stat
git add <reviewed files>
git commit -m "<type>: <summary>"
Generate the commit message briefly from the review summary / Plans.md task / branch name.
If it cannot be determined, present 2-3 commit message candidates with AskUserQuestion .
After creating the work commit, check or update .claude/state/review-result.json の commit_hash , and proceed to the release preflight.
Once the normal release preflight has started, a dirty working tree continues to be treated as a failure as before. Do not proceed to version bump / tag / GitHub Release with a dirty tree.
Quick Reference
/release # 今までの作業を review gate → commit → PR/main merge → release する
/release patch # bump を patch に明示指定
/release minor # bump を minor に明示指定
/release major # bump を major に明示指定
/release --dry-run # 計画の表示のみ、実行しない
Prerequisites
A project in which this skill operates must satisfy the following:
CHANGELOG.mdが Keep a Changelog format[Unreleased]section exists- Has one of the following version files:
VERSION(standalone file)package.json(npm)pyproject.toml(Python,[project]or[tool.poetry])Cargo.toml(Rust,[package])
ghCLI is installed and authenticated- git remote
originpoints to GitHub - In the case of a Claude Code plugin project,
claudeCLI がplugin tagis supported
If these are not satisfied, they are detected in Preflight and it aborts.
prUrlTemplate Multi-host review URLs via are recognized as a future candidate,
but the release automation of this skill still uses the gh CLI and GitHub remote as the primary path.
Automatic acquisition of owner / branch / release asset / CI metadata varies greatly by host, so in Phase 56.2.3 it is kept docs-only.
Single Gate Flow
Bare release (0. Review Gate → 0.5 Work Commit Gate) →
Pre-Gate (1. Preflight → 2. Version file detection → 3. Version read → 4. plugin tag preflight → 5. bump estimation → 6. New version calculation → 7. CHANGELOG draft → 8. Release notes draft) →
Single confirmation gate(see "Confirmation Gate" below,yes / <修正指示> / cancel 's 3 options) →
Post-Gate (9. Version file rewrite → 10. CHANGELOG promotion → 11. commit → 12. branch push → 13. PR creation/update → 14. default branch merge → 15. Reachability confirmation → 16. plugin tag → 17. semver tag → 18. tag push → 19. workflow publish verify → 20. Completion report)
It proceeds in these 3 stages. For details of each stage, see "Pre-Gate Details", "Confirmation Gate", and "Post-Gate Details".
Pre-Gate Details
1. Preflight
release ready gate: In addition to the PR ready conditions, confirm version / tag / GitHub Release / CI artifact path.
# 必須ツール
command -v gh >/dev/null || { echo "gh CLI がありません"; exit 1; }
command -v python3 >/dev/null || { echo "python3 が必要です"; exit 1; }
# working tree
if [ -n "$(git status --porcelain)" ]; then
echo "working tree に未コミット変更があります"; exit 1;
fi
# CHANGELOG
[ -f CHANGELOG.md ] || { echo "CHANGELOG.md がありません"; exit 1; }
grep -q "^## \[Unreleased\]" CHANGELOG.md || { echo "[Unreleased] セクションがありません"; exit 1; }
# plugin/mirror projects
scripts/release-preflight.sh
This working tree clean check is the gate of the normal release preflight. If you want to commit "the work so far" in a bare release, complete the Review Gate and Work Commit Gate before this check. Do not simply abort and end just with this check for an unreviewed dirty tree.
scripts/release-preflight.sh also detects mirror drift of before tag creation. opencode/, skills-codex/, codex/.codex/skills/ If generates a diff, stop the release, commit that diff, and then proceed to the tag.node scripts/build-opencode.js is executed (fail-closed) against all dist hosts. If even 1 host FAILs, stop the release. This is the wiring that satisfies multi-host bar H7 (release-preflight consumes host gates fail-closed).
release preflight は host workflow smoke を REQUIRED=1(fail-closed) is executed against all dist hosts. If even 1 host FAILs, stop the release. This is the wiring that satisfies multi-host bar H7 (release-preflight consumes host gates fail-closed).scripts/release-preflight-host-smoke.sh See. The authoritative source of fail-closed is the preflight on the operator machine, and in the GitHub runner (GITHUB_ACTIONS=true), hosts for which the CLI is not provisioned are skipped with an explicit SKIP line (because re-execution of the tag-triggered workflow does not block all releases. Response to the regression in v5.3.0 run 29679591686).
2. Version File Auto-Detection
VERSION → package.json → pyproject.toml([project] / [tool.poetry])→ Cargo.toml search in that priority order, and the first one found is treated as canonical.
Details of detection snippets and read logic: version-files.md
3. Claude Plugin Tag Preflight
.claude-plugin/plugin.json exists, in addition to the normal GitHub Release tag, also create a Claude plugin release tag.
In a nutshell, before manually assemblinggit tag -a , pass it through Claude Code's own plugin validation, and then create the {plugin-name}--v{version} tag.
In the Pre-Gate, do not rewrite files, and confirm the following.
version sync is not picked up by grep / sed , and JSON is read with a structured parser:
command -v claude >/dev/null || { echo "claude CLI がありません"; exit 1; }
claude plugin validate .claude-plugin/plugin.json
HARNESS_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-.}"
python3 "${HARNESS_PLUGIN_ROOT}/scripts/check-release-version-sync.py" --root .
claude plugin tag .claude-plugin --dry-run
${HARNESS_PLUGIN_ROOT}/scripts/check-release-version-sync.py reads all existing release surfaces and determines the canonical in the order VERSION > package.json > .claude-plugin/plugin.json > .codex-plugin/plugin.json .
Then, if there is even one of the following mismatches or omissions, do not proceed to tag / release:
VERSIONpackage.jsonの.version.claude-plugin/plugin.jsonの.version.codex-plugin/plugin.jsonの.version.claude-plugin/marketplace.jsonの.metadata.version.claude-plugin/marketplace.jsonの.plugins[].version(each plugin entry in the array)
On mismatch, display which surface differs from canonical, or which field is missing / invalid.
When reading with machine processing or CI, use --json :
python3 "${HARNESS_PLUGIN_ROOT}/scripts/check-release-version-sync.py" --root . --json
This check exists to prevent three accidents:
VERSIONと.claude-plugin/plugin.jsonthe accident of cutting a tag while the version is out of syncpackage.jsonthe accident of proceeding to the release workflow while the version of the marketplace entry is old- the accident of not passing plugin manifest / marketplace entry validation and later getting stuck on the plugin install / update side
--dry-run in claude plugin tag you can see the tag name that will actually be created and the internal git tag -a / push-equivalent commands. Include the commands seen here in the Confirmation Gate plan.
4. Bump auto-estimation
[Unreleased] directly under the headings (### Breaking Changes/### Removed → major、### Added → minor、### Fixed/### Changed/### Security only → patch, empty section → error) to determine the bump level.
If the user explicitly specifies with /release patch|minor|major , prioritize that.
Details: bump-detection.md
5. CHANGELOG draft creation (in memory)
[Unreleased] and assemble the[<new>] - YYYY-MM-DD section and compare link (do not write yet).
Details: release-notes.md
6. Release Notes draft creation (in memory)
## [<new>] section, generate markdown for GitHub Release (What's Changed / Before-After / Added-Changed-Fixed / footer).
Details of required elements, generation method, and validation checks: release-notes.md
Confirmation Gate
Once all drafts are ready, present them to the user only once:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Release Plan: v<old> → v<new> (<bump>)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Version file: <detected file>
Bump reason: <why this level was chosen>
CHANGELOG changes:
[Unreleased] に <N> 項目の変更を検出
[<new>] - YYYY-MM-DD として確定
Compare link を追加
GitHub Release notes preview:
<最初の 10 行>
...
Files to modify:
- <version file>
- CHANGELOG.md
Final actions:
- git commit -m "chore: release v<new>"
- git push origin <release-branch>
- gh pr create/update + gh pr merge into <default-branch>
- git fetch origin <default-branch> && git checkout <default-branch>
- claude plugin tag .claude-plugin --push --remote origin # plugin project の場合。default branch 上で実行
- git tag -a v<new> # GitHub Release 用 semver tag が必要な場合。default branch 上で作成
- git push origin <default-branch> --tags
- (tag push 後は GitHub Actions release workflow が自動で release 公開)
Proceed? [yes / cancel / <修正指示>]
Post-Gate details
After approval, execute without interruption. On failure, the policy is as follows:
| Failure point | Recovery |
|---|---|
| File rewrite failure | Abort there; leave local dirty for a human to decide |
| Commit failure | Hook rejection, etc. Present the cause to the user and prompt a fix |
| PR creation/merge failure | Stop with the release incomplete. Do not proceed to tag / GitHub Release |
| Plugin tag validation failure | VERSION / .claude-plugin/plugin.json Fix the mismatch in / marketplace entry, and do not proceed to tag creation |
| Push failure | Problem on the remote side. Keep the local commit/tag |
PR / Main Merge Gate、plugin tag、Verify Publish
After the Post-Gate release commit and before creating the tag, merge the GitHub PR into the default branch (gh pr create → gh pr merge --merge → verify reachability of the release commit by fetching/checking out the default branch). Do not create a GitHub Release with a tag that points to a commit that exists only on the release branch.
.claude-plugin/plugin.json For projects that have it, after merging, re-check version sync on the default branch before creating the claude plugin tag .claude-plugin --push --remote origin で plugin tag({plugin-name}--v{version} format).
After pushing the tag, verify the bash scripts/release-verify-publish.sh で .github/workflows/release.yml publication result (polling at 5-second intervals × 60 times, exit 0=PASS / 2=WARN(timeout) / 3=ERROR).
For the full command text and the criteria for judging failures, refer to post-gate-detail.md .
--dry-run mode
Run all Pre-Gates and display the content up to the Confirmation Gate, butstop at the gate and do not proceed to Post-Gate。
For a Claude plugin project, even in dry-run, run python3 "${HARNESS_PLUGIN_ROOT}/scripts/check-release-version-sync.py" --root . と claude plugin tag .claude-plugin --dry-run and display the plugin tag name that will actually be created and the push targets. At this point, if VERSION / package.json / .claude-plugin/plugin.json / .codex-plugin/plugin.json / .claude-plugin/marketplace.json 's version surfaces are inconsistent or missing, stop at the dry-run stage.
Environment variables
Used for per-project adjustments:
| Variable | Description |
|---|---|
HARNESS_RELEASE_PROJECT_ROOT |
repository root (default: $(pwd)) |
HARNESS_RELEASE_BRANCH |
push target branch (default: current branch) |
HARNESS_RELEASE_DEFAULT_BRANCH |
PR merge destination default branch (default: main) |
HARNESS_RELEASE_HEALTHCHECK_CMD |
Command to additionally run in Preflight |
HARNESS_RELEASE_SKIP_GH |
1 Skip GitHub Release creation with |
CHANGELOG writing rules
[Unreleased] The section must have one of the KaCL standard subsections (### Added=minor / ### Changed・### Fixed・### Security=patch / ### Deprecated=minor / ### Removed・### Breaking Changes=major).
Because this skill mechanically parses these headings, variations in notation (### Fix / ### Bug Fixes , etc.) cannot be recognized.
For details on the required format for GitHub Release notes, the CHANGELOG "until now/from now on" notation, and the merge method (squash not adopted), refer to github-release.md . For details on SemVer criteria, the batch release policy, and the Release Train Proposal, refer to versioning.md .
Pre-ship acceptance decision (for non-engineers)
Before finalizing the release, propose harness-accept . With an "acceptance decision" screen that summarizes in a single HTML page whether each acceptance condition has been met and a ship/wait/reject recommendation, the requester can decide whether to ship without specialized knowledge.
Related skills
harness-release-internal- harness-specific preflight/finalization to additionally run when releasing the main claude-code-harness (not subject to distribution)harness-plan- Plans.md managementharness-review- pre-release code reviewharness-accept- acceptance decision HTML (for non-engineers, proposed before release)
Design philosophy
- Separation of PR ready / release ready: PR ready is review + evidence pack. Release ready covers version/tag/GitHub Release/CI. lane:fast / lane:gate may stop at PR ready
- Single gate: The user's decision point occurs only once. Inserting a mini-confirmation turns it into a rubber stamp and makes it meaningless.
- Draw everything in advance: Forbid "reconsidering" after entering Post-Gate. Assemble all drafts before the Gate.
- Completion condition is reflection in main: Create the release tag / GitHub Release only after it has been reflected in the default branch. Treat a branch-only release as incomplete.
- Failures are transparent: If a failure occurs midway, do not attempt automatic rollback; present the current state to the user and let them decide.
- Project-independent: Do not assume environment-specific prerequisites such as the VERSION file format, mirror, or residue check. Processing specific to the main harness is separated into
harness-release-internal.