Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generate and publish concise, evidence-based notes in the body of the latest existing GitHub Release. Use only when the user explicitly invokes `$release-notes` or explicitly asks to update the latest existing GitHub Release body. Do not invoke for general release planning, changelog, tag, or version tasks.
.claude/skills/kenryu42-release-notes/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-07 | ✗→✓ | ▲ Improved | 234% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 52% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 155% | 0% |
| case-14 | ✗→✓ | ▲ Improved | 91% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 166% | 0% |
Update only the body of the latest existing GitHub Release. Treat the repository diff as the source of truth. Use pull requests and commits only as supporting evidence.
gh for all GitHub Release, pull request, issue, and API operations. Use git fetch only when required tag objects or history are missing.Run all checks from the repository that the user wants to release:
sh git rev-parse --is-inside-work-tree
gh is available:sh command -v gh
gh can resolve and access the current GitHub repository. Save its URL and get the GitHub host from it:sh REPO_URL="$(gh repo view --json url --jq '.url')" GH_HOSTNAME="${REPO_URL#*://}" GH_HOSTNAME="${GH_HOSTNAME%%/*}"
sh gh auth status --active --hostname "$GH_HOSTNAME"
Stop with a clear error if a check fails. Do not initialize a Git repository, change authentication, or select another repository as a fallback.
Use GitHub's descending release-list order as the release sequence. Include drafts and prereleases because they are existing releases:
shgh release list --limit 2 --order desc \ --json tagName,name,createdAt,publishedAt,isDraft,isPrerelease,isImmutable,isLatest
publishedAt value.Set LATEST_TAG and, when present, PREVIOUS_TAG from this result. Quote both values in every command. Then load and record the latest release before any edit:
shgh release view \ --json tagName,name,body,isDraft,isPrerelease,isImmutable,publishedAt,targetCommitish,url \ -- "$LATEST_TAG"
Confirm that the returned tagName is LATEST_TAG. Record tagName, name, isDraft, isPrerelease, isImmutable, and targetCommitish from this result. Also record isLatest, createdAt, and publishedAt from the release-list result. Use these values for the final safety check. If isImmutable is true, stop and report that GitHub does not permit the body update.
Set full Git refs so that a tag that starts with - cannot become a command option:
shLATEST_REF="refs/tags/$LATEST_TAG" PREVIOUS_REF="refs/tags/$PREVIOUS_TAG"
Set PREVIOUS_REF only when PREVIOUS_TAG exists. Confirm that each selected release tag resolves to a local commit:
shgit rev-parse --verify "$LATEST_REF^{commit}" git rev-parse --verify "$PREVIOUS_REF^{commit}"
Run the second command only when PREVIOUS_TAG exists. If a tag is missing, inspect the Git remotes, identify the remote for the same GitHub repository, and use git fetch to get the required tags. Do not assume that the remote is named origin. Stop if the remote is ambiguous or a release tag still does not resolve. Do not guess a replacement range.
Check for a shallow repository:
shgit rev-parse --is-shallow-repository
If the result is true, use the identified GitHub remote to fetch complete history and tags:
shgit fetch --unshallow --tags "$GITHUB_REMOTE"
Then run the shallow-repository check again. Continue only when the result is false and both required tag commits resolve. If the full history cannot be fetched, stop. Do not generate notes from an incomplete history.
When PREVIOUS_TAG exists, check whether it is an ancestor of LATEST_TAG:
shgit merge-base --is-ancestor "$PREVIOUS_REF^{commit}" "$LATEST_REF^{commit}"
A non-ancestor result is not an automatic failure. State it in the working notes, use the tree diff to identify the shipped-state change, and inspect both sides of the history so that the commit list does not cause a false claim.
When a previous release exists, start with:
shgit log --date=short --format='%H%x09%ad%x09%s' "$PREVIOUS_REF..$LATEST_REF" git diff --stat "$PREVIOUS_REF" "$LATEST_REF" git diff --name-status "$PREVIOUS_REF" "$LATEST_REF" git diff "$PREVIOUS_REF" "$LATEST_REF"
For non-linear history, also inspect:
shgit log --left-right --graph --oneline "$PREVIOUS_REF...$LATEST_REF"
For the first release, inspect all history reachable from the latest tag and compare its shipped tree with an empty tree:
shgit log --reverse --date=short --format='%H%x09%ad%x09%s' "$LATEST_REF" EMPTY_TREE="$(git hash-object -t tree /dev/null)" git diff --stat "$EMPTY_TREE" "$LATEST_REF" git diff --name-status "$EMPTY_TREE" "$LATEST_REF" git diff "$EMPTY_TREE" "$LATEST_REF"
Account for every changed path. Inspect the relevant diff hunks, not only the statistics or file names. For generated or binary files, inspect the source, manifest, configuration, or other evidence that explains their user impact. Do not include working-tree changes or commits after LATEST_TAG.
Use this evidence order:
Use PR data when it resolves an unclear purpose, user impact, migration step, or reference. Find associated PRs for a commit when needed:
shgh api -H 'Accept: application/vnd.github+json' \ "repos/{owner}/{repo}/commits/$COMMIT_SHA/pulls" gh pr view "$PR_NUMBER" \ --json number,state,mergedAt,title,body,files,commits,closingIssuesReferences,url
Use PR context only when state is MERGED and mergedAt is present. Verify every PR or issue association before adding its number. A commit prefix, PR label, or PR title does not prove user impact.
For each changed behavior, identify what a user can observe and the evidence that supports it. Include a change only when it materially affects one or more of these areas:
Normally exclude refactoring, formatting, lint changes, tests, CI changes, build maintenance, merge commits, routine dependency updates, internal cleanup, and documentation-only changes. Include one only when the diff proves material user impact.
Investigate an ambiguous change before classification. If the available evidence cannot support a useful claim, describe only the supported fact or omit the change. Never invent behavior, fixes, performance results, security effects, breaking effects, migration steps, or PR and issue links.
If no change has material user impact, write only a short, factual summary that says the release contains maintenance changes with no material change to user-visible behavior. Do not create empty sections.
Create an isolated temporary directory and Markdown file:
shRELEASE_NOTES_DIR="$(mktemp -d)" RELEASE_NOTES_FILE="$RELEASE_NOTES_DIR/release-notes.md"
Arrange the body in this exact order, and omit all empty sections:
### Highlights### Added### Changed### Fixed### Breaking Changes### Deprecated### Removed### SecurityStart with a concise one-sentence or two-sentence summary. Do not add a version heading and do not repeat the release title. Use this shape only for sections that contain entries:
markdownA concise summary of the release and its most important user impact. ### Highlights - Major user-facing improvement. ### Added - Added support for ... (#123) ### Changed - Improved ... ### Fixed - Fixed an issue where ... ### Breaking Changes - Replaced `--old-flag` with `--new-flag` for users of the command-line interface. - **Migration:** Replace `--old-flag` with `--new-flag` in scripts and configuration.
Apply these writing rules:
Added, Improved, Changed, Fixed, Deprecated, or Removed.Highlights only for approximately one to three important items. Omit it for a small release.Highlights entry gives useful emphasis.Full Changelog link.For each breaking change, explain what changed and who is affected. Add a nested **Migration:** instruction when the evidence supports one. Mention an important breaking change in the opening summary. Do not use the breaking label without evidence.
Use ### Security only for material public security impact. State the improvement at a safe public level. Do not include sensitive vulnerability details or exploitation instructions.
Read the complete temporary Markdown file. Correct all problems before the edit. Confirm that:
Full Changelog linkImmediately before the edit, run the release-list query from step 2 again. Confirm that its first tag is still LATEST_TAG and that the recorded list metadata for this release did not change. Then run gh release view -- "$LATEST_TAG" again and confirm that the recorded release metadata still identifies the intended release. Stop if CI or another actor created a newer release or changed the selected release.
Run exactly this release edit command. Do not add any other gh release edit option:
shgh release edit --notes-file "$RELEASE_NOTES_FILE" -- "$LATEST_TAG"
If the command fails, report the error. Do not create a replacement release and do not change release settings to make the edit pass.
Read the release again after success. Confirm that its body matches RELEASE_NOTES_FILE and that tagName, name, isDraft, isPrerelease, and isImmutable match the values recorded before the edit:
shgh release view \ --json tagName,name,body,isDraft,isPrerelease,isImmutable,publishedAt,targetCommitish,url \ -- "$LATEST_TAG" gh release list --limit 2 --order desc \ --json tagName,name,createdAt,publishedAt,isDraft,isPrerelease,isImmutable,isLatest
Also confirm that targetCommitish, createdAt, publishedAt, and isLatest did not change. Confirm that LATEST_TAG is still the first item. If a new release appeared during the edit, report the race and do not claim that the edited release is still the latest.
Report the updated tag and URL, the sections that were published, and any omitted ambiguous change. Do not claim success if the readback does not match.
Remove the temporary file and directory after success or failure. Delete the file first, then remove the empty directory. Do not use a recursive removal command.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 11,528 | 7,481 | -35% | 1 | 1 | 0% | 1,635 | 3,408 | +108% | 0 | 0 | — |
case-02 | fail→fail | 17,742 | 8,322 | -53% | 1 | 1 | 0% | 894 | 3,321 | +271% | 0 | 0 | — |
case-03 | fail→fail | 10,323 | 9,499 | -8% | 1 | 1 | 0% | 291 | 3,420 | +1075% | 0 | 0 | — |
case-04 | pass→fail | 6,298 | 11,879 | +89% | 1 | 1 | 0% | 1,011 | 3,681 | +264% | 0 | 0 | — |
case-05 | pass→pass | 12,023 | 32,601 | +171% | 1 | 1 | 0% | 1,185 | 4,973 | +320% | 0 | 0 | — |
case-06 | pass→fail | 24,773 | 12,183 | -51% | 1 | 1 | 0% | 1,754 | 3,574 | +104% | 0 | 0 | — |
case-07 | fail→pass | 10,453 | 22,303 | +113% | 1 | 1 | 0% | 1,489 | 4,973 | +234% | 0 | 0 | — |
case-08 | fail→pass | 17,825 | 5,807 | -67% | 1 | 1 | 0% | 2,465 | 3,742 | +52% | 0 | 0 | — |
case-09 | fail→fail | 38,338 | 3,692 | -90% | 1 | 1 | 0% | 1,805 | 3,472 | +92% | 0 | 0 | — |
case-10 | pass→pass | 23,749 | 5,682 | -76% | 1 | 1 | 0% | 1,422 | 3,614 | +154% | 0 | 0 | — |
case-11 | fail→pass | 10,088 | 13,130 | +30% | 1 | 1 | 0% | 1,688 | 4,298 | +155% | 0 | 0 | — |
case-12 | fail→fail | 14,974 | 13,589 | -9% | 1 | 1 | 0% | 1,945 | 3,938 | +102% | 0 | 0 | — |
case-13 | pass→fail | 25,025 | 4,985 | -80% | 1 | 1 | 0% | 1,981 | 3,559 | +80% | 0 | 0 | — |
case-14 | fail→pass | 14,519 | 6,848 | -53% | 1 | 1 | 0% | 1,950 | 3,725 | +91% | 0 | 0 | — |
case-15 | pass→pass | 16,466 | 14,048 | -15% | 1 | 1 | 0% | 1,248 | 4,071 | +226% | 0 | 0 | — |
case-16 | pass→pass | 9,873 | 3,183 | -68% | 1 | 1 | 0% | 1,108 | 3,504 | +216% | 0 | 0 | — |
case-17 | fail→pass | 22,345 | 34,670 | +55% | 1 | 1 | 0% | 1,442 | 3,837 | +166% | 0 | 0 | — |
case-18 | fail→pass | 12,424 | 19,288 | +55% | 1 | 1 | 0% | 1,783 | 3,600 | +102% | 0 | 0 | — |
case-19 | fail→pass | 42,077 | 4,021 | -90% | 1 | 1 | 0% | 2,319 | 3,631 | +57% | 0 | 0 | — |
case-20 | fail→pass | 12,220 | 7,698 | -37% | 1 | 1 | 0% | 1,874 | 3,956 | +111% | 0 | 0 | — |
case-21 | fail→pass | 12,752 | 5,826 | -54% | 1 | 1 | 0% | 1,967 | 3,776 | +92% | 0 | 0 | — |
case-22 | pass→pass | 11,174 | 49,338 | +342% | 1 | 1 | 0% | 1,685 | 3,889 | +131% | 0 | 0 | — |
case-23 | fail→pass | 66,514 | 4,914 | -93% | 1 | 1 | 0% | 1,992 | 3,751 | +88% | 0 | 0 | — |
case-24 | pass→pass | 31,982 | 5,817 | -82% | 1 | 1 | 0% | 2,204 | 3,905 | +77% | 0 | 0 | — |
case-25 | fail→fail | 14,447 | 5,527 | -62% | 1 | 1 | 0% | 2,142 | 3,693 | +72% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 20 counted toward the lift figure. The other 5 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +28 percentage points is the difference between those two pass rates over the 20 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.