prisma/draft-release-notes
>- Author the committed release-notes file for a stable Prisma Next release by enumerating the merged PRs since the previous stable `v*` tag, resolving triaging public-worthiness, and writing categorized notes — breaking changes first — into `docs/releases/v<version>.md` plus a mirrored `CHANGELOG.md` entry. Use when cutting a release, when the `publish-npm-version` skill reaches its "draft the release notes" step, when asked to "draft the release notes", "write the changelog for this release", "author docs/releases/v<x>.md", or "summarize what shipped since the last stable tag".
npx skills add https://github.com/prisma/prisma --skill draft-release-notes
This skill fires inside a stable-release cut. The release-cutting agent runs it from the release/<version> worktree that publish-npm-version created, with the target version already known, and produces the committed notes file that is the GitHub Release body (the publish workflow ships it verbatim via gh release create --notes-file docs/releases/v<version>.md). There is no --generate-notes fallback — the file you author here is what every consumer reads.
The skill is prose-driven: there is no codemod or script to run. You — the agent — do the enumeration, the Linear-context lookup, the triage, and the writing directly, the same way record-upgrade-instructions walks you through authoring an upgrade entry rather than running one for you.
Run this skill when all of the following hold:
latest) release is being cut — the target version $NEXT is known (computed by publish-npm-version step 1, e.g. 0.12.0).release/<version> worktree checked out at the bump commit (HEAD carries the bumped root version).docs/releases/v$NEXT.md does not exist yet (the PR-mode release-notes gate, pnpm check:release-notes --mode pr, fails the release PR until it does).Do not run it for -dev.N or -beta.N builds: those create no GitHub Release and are not gated. Do not run it to backfill notes for an already-shipped release — the convention starts from the first release cut after it landed.
These are project requirements, not style preferences. A draft that violates either is wrong even if everything else is perfect.
Linear is a summarization-context input only. You read a ticket to understand the user-facing outcome of an opaque PR title, then you write a fresh, public, user-facing sentence describing that outcome. You never paste ticket prose into the notes. Specifically, the following must never appear in docs/releases/*.md or CHANGELOG.md:
TML-NNNN: issue prefix or issue title — link the PR (#NNN) instead.If you cannot describe a change for a public audience without leaning on internal context, that is a signal the entry needs rethinking (or is internal-only and should be excluded — see the triage rubric), not a licence to paraphrase the ticket closely. When in doubt, describe only the externally-observable behaviour change.
Every entry links its PR (#NNN) so the human reviewer can check your one-line summary against the actual diff during the release-PR review. The human review is the backstop for triage and summarization judgment calls — write for that reviewer.
The range is "everything since the last latest release", so the lower bound is the most recent stable v* tag, excluding -dev.* / -beta.* pre-release tags:
PREV_TAG=$(git describe --abbrev=0 --tags --match 'v[0-9]*' --exclude '*-dev.*' --exclude '*-beta.*')
echo "$PREV_TAG" # e.g. v0.11.0
Equivalently, list and filter explicitly (useful when describe can't find an ancestor tag):
git tag --list 'v*' --sort=-v:refname | grep -Ev -- '-(dev|beta)\.' | head
The dev/beta exclusion matters: a -dev.N tag is cut on most merges, so an unfiltered "previous tag" would scope the range to a single PR. Filtering to stable gives the full set of changes since consumers last saw a latest release.
Take the commit set from git log and resolve each commit to its PR via gh:
git log --first-parent --oneline "$PREV_TAG"..HEAD
For each merged PR in the range, resolve the metadata you need with gh / gh api — PR number, title, author (login), labels, and whether the author is a first-time contributor:
# PRs merged in the range (adjust the search window to the range you enumerated)
gh pr list --state merged --base main --limit 200 \
--json number,title,author,labels,mergeCommit,mergedAt
# Or resolve a single PR by its merge commit:
gh api "repos/prisma/prisma-next/commits/<sha>/pulls" \
--jq '.[] | {number, title, author: .user.login, labels: [.labels[].name]}'
Use --first-parent so squash-merged PRs each show up as one commit; cross-check against gh pr list so you do not miss a PR or double-count.
When a PR title is an internal shorthand (TML-NNNN: <terse handle>), read the referenced Linear issue (via the Linear MCP) to understand what changed for the user. This is enrichment only — re-read Rule 1 before writing anything. Summarize the outcome in your own public words; cite the PR, never the ticket.
Decide, per PR, whether it earns a line in the public notes. The rubric biases toward a clean, user-facing changelog over exhaustiveness — the human PR review is the backstop for judgment calls.
Always include:
@prisma-next/* public API), CLI commands/flags, prisma-next.config.ts fields, the contract format (contract.json / contract.d.ts shape), on-disk migration shape, or error codes.Default-exclude unless user-relevant:
Excluded PRs are dropped silently — no "internal changes" catch-all section. If a default-exclude PR has a genuine user-facing consequence, include it and describe that consequence.
Write the entries under the fixed section order from docs/releases/README.md, omitting any section with no entries:
Breaking changes lead because they are what a reader scanning the notes most needs to see. Every line links its PR as an absolute markdown link — #NNN, never bare #NNN. Bare references only autolink inside the GitHub Release body; they render as plain text when the committed docs/releases/v<version>.md is read as a repo file or in PR review, so the explicit link form is what makes every reference work in every context.
A breaking change shipping in this release has a matching upgrade-instructions directory keyed to the minor transition, following the convention enforced by scripts/check-upgrade-coverage.mjs and authored via record-upgrade-instructions. The transition label is <prev.major>.<prev.minor>-to-<head.major>.<head.minor> — computed from the previous stable tag's minor and $NEXT's minor (e.g. v0.11.0 → 0.12.0 gives 0.11-to-0.12). Point the breaking note at the recipe directory rather than restating the migration.
Recipe links must be absolute, tag-pinned URLs — https://github.com/prisma/prisma-next/blob/v$NEXT/.... The notes file becomes the GitHub Release body via --notes-file, and the Release page does not reliably resolve repo-relative links, so a relative recipe path would publish as a dead migration link. Pinning to the release tag (/blob/v$NEXT/) means the link always resolves and never rots as the recipe tree evolves on main:
https://github.com/prisma/prisma-next/blob/v$NEXT/skills/upgrade/prisma-next-upgrade/upgrades/<prev.minor>-to-<head.minor>/https://github.com/prisma/prisma-next/blob/v$NEXT/skills/extension-author/prisma-next-extension-upgrade/upgrades/<prev.minor>-to-<head.minor>/A breaking change can affect one or both audiences — link whichever recipe directories exist.
If the recipe directory is absent, do not fail authoring: still list the breaking change and describe the required action inline. The missing recipe is check:upgrade-coverage's concern to enforce, not this skill's.
For a skipped-publish range (more than one minor in this release — see graceful degradation below), the recipe is a *chain* of consecutive transition directories (e.g. 0.11-to-0.12 + 0.12-to-0.13 for a v0.11.0 → 0.13.0 publish), mirroring how check-upgrade-coverage aggregates the chain. Anchor each breaking entry to the step that introduced it.
Prose tells a reader *that* something changed; a short before/after snippet shows them *what it looks like*, which is what they actually need to act. For the most code-visible breaking changes — contract-shape changes, authoring-surface changes, runtime-option or builder-API changes — nest a compact before / after example under the prose bullet.
<prev.minor>-to-<head.minor> upgrade recipe (authored via record-upgrade-instructions) already contains authoritative before/after migration code — lift the snippet from there so it stays accurate. If the change is only visible in the emitted contract.json / contract.d.ts, a minimal shape diff from the recipe or the PR diff is fine. prisma , never psl ), per the repo's authoring-surface convention. Use TS or JSON only when the change is genuinely a TS-surface change (a builder/runtime option, a consumer reading the emitted .d.ts) or an emitted-shape change with no PSL form.The format is the prose bullet, then the nested example:
- **<title>** — <what changed and what the reader must do; recipe link>. ([#<pr>](https://github.com/prisma/prisma-next/pull/<pr>))
Before:
…
After:
…
Preserve the "New contributors" credit that --generate-notes gave for free. Each first-time contributor gets a line naming the PR that welcomed them, with both the handle and the PR as absolute links:
- [@<handle>](https://github.com/<handle>) made their first contribution in [#<pr>](https://github.com/prisma/prisma-next/pull/<pr>)
Resolve first-time status from PR author metadata (e.g. gh api author_association of FIRST_TIME_CONTRIBUTOR / FIRST_TIMER, or by checking whether the author appears in the range before this PR).
Fill the docs/releases/README.md template into docs/releases/v$NEXT.md:
# v<version>
<optional one- or two-sentence summary of the release's theme>
## Breaking changes
- **<short title>** — <what changed and what the reader must do; link the upgrade recipe>. ([#<pr>](https://github.com/prisma/prisma-next/pull/<pr>))
Before:
<old shape>
After:
<new shape>
## Features
- <new capability>. ([#<pr>](https://github.com/prisma/prisma-next/pull/<pr>))
## Fixes
- <bug fix>. ([#<pr>](https://github.com/prisma/prisma-next/pull/<pr>))
## New contributors
- [@<handle>](https://github.com/<handle>) made their first contribution in [#<pr>](https://github.com/prisma/prisma-next/pull/<pr>)
Then prepend a ## v$NEXT entry to CHANGELOG.md, mirroring the notes-file body (newest-first). The CHANGELOG is a plain newest-first mirror — no second authoring format, no "Keep a Changelog" headers; copy the section bodies under the ## v$NEXT header at the top of the entry list (below the file's intro and the <!-- New release entries go here … --> marker).
Commit the notes file + CHANGELOG as their own commit on the release/<version> branch (keeping publish-npm-version's chore(release): bump commit clean), so the notes ride in the bump PR diff and satisfy the check:release-notes PR-mode gate. Use explicit staging and sign off:
git add docs/releases/v$NEXT.md CHANGELOG.md
git commit -s -m "docs(release): add release notes for v$NEXT"
Control then returns to publish-npm-version for the push + PR-open steps.
git describe finds no stable v* tag, fall back to the earliest tag or the repo root and note in the summary that this is the first curated release; enumerate the whole range.$NEXT (a minor was bumped in-tree but never shipped), enumerate across the *whole* range and treat breaking-change anchoring as a chain of consecutive transition directories, mirroring check-upgrade-coverage's skipped-publish handling.publish-npm-version runs pnpm install --frozen-lockfile --ignore-scripts, so the .claude/ / .agents/ skill mirrors may not be materialized there. This skill is invoked by reading its canonical path, skills-contrib/draft-release-notes/SKILL.md, which exists in the checkout regardless.--notes-file wiring (scripts/check-release-notes.mjs, .github/workflows/). This skill *produces* the file; the gate and workflow *consume* it.publish-npm-version and updating docs/oss/versioning.md — handled separately; this file is the authoring logic only..github/release.yml label config or any third-party release-notes tool. Categorization is done here, by reasoning over the diff + Linear context — not from PR labels or an external generator.Cutting v0.12.0 from origin/main (previous stable tag v0.11.0).
PREV_TAG=$(git describe --abbrev=0 --tags --match 'v[0-9]*' --exclude '*-dev.*' --exclude '*-beta.*') → v0.11.0.git log --first-parent --oneline v0.11.0..HEAD lists 14 merged PRs. gh pr list --state merged --base main --json number,title,author,labels,mergedAt resolves their metadata.TML-2536: contract deserializer seam. Read TML-2536 in Linear → the user-facing outcome is "contract deserialization now goes through an explicit adapter seam". Write that outcome in public words; cite #1240, not TML-2536.includeMany capability (#1234) → feature. A null-handling bug fix (#1242) → fix. First-time contributor @somebody on #1238.0.11-to-0.12. The recipe dir skills/upgrade/prisma-next-upgrade/upgrades/0.11-to-0.12/ exists in the checkout → the breaking note links it as a tag-pinned URL, https://github.com/prisma/prisma-next/blob/v0.12.0/skills/upgrade/prisma-next-upgrade/upgrades/0.11-to-0.12/. (If it were absent, the note would describe the required adapter migration inline instead.)0.11-to-0.12 recipe (a TS runtime change, so a ts fence). @somebody's contributor line, with absolute links: - @somebody made their first contribution in #1238.docs/releases/v0.12.0.md (every PR ref + handle an absolute link; the breaking entry carries a before/after):# v0.12.0
Contract deserialization gains an explicit adapter seam, and queries can now eager-load related records.
## Breaking changes
- **Contract deserialization requires an adapter seam** — deserialization now goes through an explicit seam adapter; existing code must register one. See the [0.11-to-0.12 upgrade recipe](https://github.com/prisma/prisma-next/blob/v0.12.0/skills/upgrade/prisma-next-upgrade/upgrades/0.11-to-0.12/). ([#1240](https://github.com/prisma/prisma-next/pull/1240))
Before:
const contract = deserializeContract(json);
After:
const contract = deserializeContract(json, { adapter: postgresAdapter });
## Features
- `includeMany` eager-loads related records in a single query. ([#1234](https://github.com/prisma/prisma-next/pull/1234))
## Fixes
- Null values in `returning()` projections no longer throw. ([#1242](https://github.com/prisma/prisma-next/pull/1242))
## New contributors
- [@somebody](https://github.com/somebody) made their first contribution in [#1238](https://github.com/prisma/prisma-next/pull/1238)
Then prepend the same body under ## v0.12.0 to CHANGELOG.md.
git add docs/releases/v0.12.0.md CHANGELOG.md && git commit -s -m "docs(release): add release notes for v0.12.0".docs/releases/README.md — the committed-notes-file convention, the no-fallback design, the section order, and the template this skill fills.CHANGELOG.md — the rolling newest-first mirror this skill prepends.publish-npm-version — the release-cut skill that invokes this one from the release/<version> worktree.record-upgrade-instructions — the breaking-change upgrade-recipe authoring flow whose upgrades/<prev.minor>-to-<head.minor>/ directories the breaking-change section anchors to.scripts/check-upgrade-coverage.mjs — the transition-label convention (<major>.<minor>-to-<major>.<minor>) and skipped-publish chain handling.docs/oss/versioning.md — the version contract and release procedure these notes are part of.Take prisma/draft-release-notes from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.
The instructions reference npm.
Without those the skill loads but fails at the first command.