Generate Release Notes
Objective
Produce a redis-py release-notes draft that matches the structure of the project's published GitHub
releases, derived from the merged PRs on a given release branch since the previous release and
categorized by PR label. The release branch is the skill's primary input (step 1). Save the result as a Markdown file. Never publish it — drafting only; the user publishes the release themselves.
Do not rely on any automated release-drafter output. We do not use it to decide which PRs are
included or how they are categorized — it misses PRs and miscategorizes them. Collect the PRs
yourself from the git history (step 2) and categorize every one of them manually using this skill's
references, which are the complete and authoritative rules:
references/release-notes-template.md — the section order, titles, line format, and full skeleton.
references/pr-labels-guide.md — the authoritative label→category mapping, what each label means,
and how to categorize a PR/commit that has no category label.
Constraints
- Read-only GitHub access only. Per the repository owner's standing rule, never run
gh (or any
GitHub API call) that writes — no creating/editing releases, comments, labels, or tags — unless the
user explicitly authorizes that specific action in the same turn. Use gh ... view/list,
git log, or GET-only gh api/curl to gather data. gh may fail with a TLS error inside the
command sandbox; run it with the sandbox disabled when needed.
- This skill only writes the draft Markdown file in the working tree. It does not commit, tag,
push, or create a GitHub release.
Workflow
1. Determine the release range and version
- Read the release branch as input. The branch the release is cut from is the primary input to
this skill — it defines which changes are included. Take it from the skill invocation/args if
provided; otherwise ask the user for it (e.g.
8.0, release/8.0.2, master). Do not guess.
Verify it exists (git rev-parse --verify <branch>; fetch first if it is a remote branch,
origin/<branch>). Every change to include is a commit reachable from this branch and not from
the previous release tag.
- Identify the previous release tag: the most recent
v* tag reachable from the release branch,
git describe --tags --abbrev=0 <branch> (or gh release list -L 5, read-only). Confirm with the
user if a non-default base is intended.
- Decide the new version. redis-py ships major, minor, and patch releases (e.g.
8.0.1, 7.4.1).
Infer minor vs patch from the change set (any Breaking/New Feature → at least a minor bump) and
confirm the exact version with the user if unclear. The tag form is v<version>.
2. Collect the merged PRs / commits in range
- List the commits on the release branch since the previous tag:
git log --oneline --no-merges <prev-tag>..<branch>. Each squashed/merged commit title typically
ends with (#<pr-number>); extract that number. For merge-commit workflows, use
git log --merges or the GitHub compare API instead.
- For each PR number, fetch its title, labels, and author (read-only):
gh pr view <n> --json number,title,labels,author or
gh api repos/redis/redis-py/pulls/<n>. Use the PR title for the change line (it is what the
published notes use), not the raw commit subject, when they differ.
- Drop any PR labeled
skip-changelog.
- If a commit has no associated PR (direct push), use the commit subject and treat it as unlabeled.
3. Categorize each commit
For each remaining commit, categorize it using references/release-notes-template.md and
references/pr-labels-guide.md:
- If the PR has one or more category labels, list it under every matching section — a PR with
two category labels appears in both (e.g.
deprecation + breakingchange → both ⚠️ Deprecations
and 🔥 Breaking Changes).
- If the PR has no category label, or only topic/process labels, or a non-category repo label
(e.g.
bug-fix, security, techdebt), infer the section(s) using
references/pr-labels-guide.md — apply every inference rule that matches, so a change may land
in more than one section. Note the inferred labels you would suggest applying to the PR.
- When a category is genuinely ambiguous, place your best guess and leave a
<!-- TODO: confirm category for #<n> --> marker, then surface it to the user.
4. Assemble the draft
Follow references/release-notes-template.md:
- Start with
# Changes.
- Optionally add
## ✨ Highlights (or a lead prose paragraph) — hand-authored ### subsections
describing each highlight with API names and doc/spec links. Include it only when the release has
something significant to focus the reader on: a notable new feature, or a breaking change that
may affect a large share of users. Omit for routine patch releases. When the release contains
Breaking Changes or notable New Features, ask the user whether highlights are wanted.
- Emit the category sections in the order from
release-notes-template.md (🚀 New Features,
🧪 Experimental, 🔥 Breaking Changes, ⚠️ Deprecations, 🐛 Bug Fixes, 🧰 Maintenance), omitting any
empty section. Each line is - <PR title> (#<n>). Combine PRs that deliver the same
feature/change onto one line with all their numbers in a single bracket group in ascending order,
e.g. - <title> (#3434 #3456 #3467) (common for paired sync/async PRs or a feature split across
follow-ups) — see the grouping rule in references/release-notes-template.md.
- End with the contributors footer: the thank-you line and the de-duplicated, space-separated list
of
@<author> handles for every included PR.
5. Save the draft as a local Markdown file
Write the assembled notes to a local Markdown file so the maintainer can review it and copy the
contents into the GitHub release UI by hand. This file is a local review artifact only:
- Default path:
release_notes/release_notes_<version>.md at the repo root (e.g.
release_notes/release_notes_8.0.2.md). Create the release_notes/ folder if it does not exist.
Confirm or accept an override path from the user.
- Do not commit, tag, push, or create/edit a GitHub release. The skill only writes the file in
the working tree; the maintainer reviews it and publishes manually. (If the
release_notes/ folder
is not already git-ignored, mention that so it is not accidentally committed.)
- The file content is exactly what should be pasted into the release body — no extra wrapper or
commentary inside the file.
After writing, report the file path and a short summary: the version, the previous tag, the
per-section commit counts, the count of PRs whose category was inferred (and which ones), and any
TODO markers left in the draft.
Verification
- Cross-check the commit count: the number of distinct PR numbers referenced across all change
lines (a grouped line contributes all of its numbers), plus any
skip-changelog/excluded PRs,
should equal the commits in git log <prev-tag>..<branch> (minus merge commits). Grouping related
PRs reduces the number of lines but not the number of PR references, so reconcile on PR numbers, not
lines.
- Confirm section order and titles match
references/release-notes-template.md and no empty section
was emitted.
- Confirm every included PR number resolves to a real PR and every contributor handle is included
once.
- Spot-check against the previous published release's formatting (run
gh release view <prev-tag>).
1---2name: generate-release-notes3description: Generate Release Notes4---56# Generate Release Notes78## Objective910Produce a redis-py release-notes draft that matches the structure of the project's published GitHub11releases, derived from the merged PRs on a given **release branch** since the previous release and12categorized by PR label. The release branch is the skill's primary input (step 1). Save the result as a Markdown file. Never publish it — drafting only; the user publishes the release themselves.1314**Do not rely on any automated release-drafter output.** We do not use it to decide which PRs are15included or how they are categorized — it misses PRs and miscategorizes them. Collect the PRs16yourself from the git history (step 2) and categorize every one of them manually using this skill's17references, which are the complete and authoritative rules:1819- `references/release-notes-template.md` — the section order, titles, line format, and full skeleton.20- `references/pr-labels-guide.md` — the authoritative label→category mapping, what each label means,21 and how to categorize a PR/commit that has no category label.2223## Constraints2425- **Read-only GitHub access only.** Per the repository owner's standing rule, never run `gh` (or any26 GitHub API call) that writes — no creating/editing releases, comments, labels, or tags — unless the27 user explicitly authorizes that specific action in the same turn. Use `gh ... view/list`,28 `git log`, or GET-only `gh api`/`curl` to gather data. `gh` may fail with a TLS error inside the29 command sandbox; run it with the sandbox disabled when needed.30- This skill **only writes the draft Markdown file** in the working tree. It does not commit, tag,31 push, or create a GitHub release.3233## Workflow3435### 1. Determine the release range and version36371. **Read the release branch as input.** The branch the release is cut from is the primary input to38 this skill — it defines which changes are included. Take it from the skill invocation/args if39 provided; otherwise ask the user for it (e.g. `8.0`, `release/8.0.2`, `master`). Do not guess.40 Verify it exists (`git rev-parse --verify <branch>`; fetch first if it is a remote branch,41 `origin/<branch>`). Every change to include is a commit reachable from this branch and not from42 the previous release tag.432. Identify the previous release tag: the most recent `v*` tag reachable from the release branch,44 `git describe --tags --abbrev=0 <branch>` (or `gh release list -L 5`, read-only). Confirm with the45 user if a non-default base is intended.463. Decide the new version. redis-py ships major, minor, and patch releases (e.g. `8.0.1`, `7.4.1`).47 Infer minor vs patch from the change set (any Breaking/New Feature → at least a minor bump) and48 confirm the exact version with the user if unclear. The tag form is `v<version>`.4950### 2. Collect the merged PRs / commits in range51521. List the commits on the release branch since the previous tag:53 `git log --oneline --no-merges <prev-tag>..<branch>`. Each squashed/merged commit title typically54 ends with `(#<pr-number>)`; extract that number. For merge-commit workflows, use55 `git log --merges` or the GitHub compare API instead.562. For each PR number, fetch its **title, labels, and author** (read-only):57 `gh pr view <n> --json number,title,labels,author` or58 `gh api repos/redis/redis-py/pulls/<n>`. Use the PR title for the change line (it is what the59 published notes use), not the raw commit subject, when they differ.603. Drop any PR labeled `skip-changelog`.614. If a commit has no associated PR (direct push), use the commit subject and treat it as unlabeled.6263### 3. Categorize each commit6465For each remaining commit, categorize it using `references/release-notes-template.md` and66`references/pr-labels-guide.md`:6768- If the PR has one or more **category labels**, list it under **every** matching section — a PR with69 two category labels appears in both (e.g. `deprecation` + `breakingchange` → both ⚠️ Deprecations70 and 🔥 Breaking Changes).71- If the PR has **no category label**, or only topic/process labels, or a non-category repo label72 (e.g. `bug-fix`, `security`, `techdebt`), infer the section(s) using73 `references/pr-labels-guide.md` — apply **every** inference rule that matches, so a change may land74 in more than one section. Note the inferred labels you would suggest applying to the PR.75- When a category is genuinely ambiguous, place your best guess and leave a76 `<!-- TODO: confirm category for #<n> -->` marker, then surface it to the user.7778### 4. Assemble the draft7980Follow `references/release-notes-template.md`:81821. Start with `# Changes`.832. Optionally add `## ✨ Highlights` (or a lead prose paragraph) — hand-authored `###` subsections84 describing each highlight with API names and doc/spec links. Include it **only when the release has85 something significant to focus the reader on**: a notable new feature, or a breaking change that86 may affect a large share of users. Omit for routine patch releases. When the release contains87 Breaking Changes or notable New Features, ask the user whether highlights are wanted.883. Emit the category sections **in the order from `release-notes-template.md`** (🚀 New Features,89 🧪 Experimental, 🔥 Breaking Changes, ⚠️ Deprecations, 🐛 Bug Fixes, 🧰 Maintenance), omitting any90 empty section. Each line is `- <PR title> (#<n>)`. **Combine PRs that deliver the same91 feature/change onto one line** with all their numbers in a single bracket group in ascending order,92 e.g. `- <title> (#3434 #3456 #3467)` (common for paired sync/async PRs or a feature split across93 follow-ups) — see the grouping rule in `references/release-notes-template.md`.944. End with the contributors footer: the thank-you line and the de-duplicated, space-separated list95 of `@<author>` handles for every included PR.9697### 5. Save the draft as a local Markdown file9899Write the assembled notes to a **local Markdown file** so the maintainer can review it and copy the100contents into the GitHub release UI by hand. This file is a local review artifact only:101102- Default path: `release_notes/release_notes_<version>.md` at the repo root (e.g.103 `release_notes/release_notes_8.0.2.md`). Create the `release_notes/` folder if it does not exist.104 Confirm or accept an override path from the user.105- **Do not commit, tag, push, or create/edit a GitHub release.** The skill only writes the file in106 the working tree; the maintainer reviews it and publishes manually. (If the `release_notes/` folder107 is not already git-ignored, mention that so it is not accidentally committed.)108- The file content is exactly what should be pasted into the release body — no extra wrapper or109 commentary inside the file.110111After writing, report the file path and a short summary: the version, the previous tag, the112per-section commit counts, the count of PRs whose category was inferred (and which ones), and any113`TODO` markers left in the draft.114115## Verification116117- Cross-check the commit count: the number of **distinct PR numbers** referenced across all change118 lines (a grouped line contributes all of its numbers), plus any `skip-changelog`/excluded PRs,119 should equal the commits in `git log <prev-tag>..<branch>` (minus merge commits). Grouping related120 PRs reduces the number of lines but not the number of PR references, so reconcile on PR numbers, not121 lines.122- Confirm section order and titles match `references/release-notes-template.md` and no empty section123 was emitted.124- Confirm every included PR number resolves to a real PR and every contributor handle is included125 once.126- Spot-check against the previous published release's formatting (run `gh release view <prev-tag>`).