| name | draft-release-notes |
| description | Author the committed release-notes file for a stable Prisma Next release by enumerating the merged PRs since the previous stable `v*` tag, resolving opaque `TML-NNNN:` titles via Linear context (never copied verbatim), 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". |
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.
When to use
Run this skill when all of the following hold:
- A stable (
latest) release is being cut — the target version $NEXT is known (computed by publish-npm-version step 1, e.g. 0.12.0).
- You are in the
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.
The two hard rules
These are project requirements, not style preferences. A draft that violates either is wrong even if everything else is perfect.
Rule 1 — Never copy Linear content verbatim
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:
- Customer names, account names, or any identifying detail of who reported or requested the change.
- Internal rationale, sprint/standup chatter, estimate or priority talk, or links to internal docs.
- The raw
TML-NNNN: issue prefix or issue title — link the PR (#NNN) instead.
- Quoted sentences from the ticket body.
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.
Rule 2 — Every line is verifiable
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.
Procedure
1. Resolve the range lower bound — the previous stable tag
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"
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.
2. Enumerate the commit set, then resolve PR metadata
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:
gh pr list --state merged --base main --limit 200 \
--json number,title,author,labels,mergeCommit,mergedAt
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.
3. Resolve opaque titles via Linear context
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.
4. Triage public-worthiness
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:
- Anything flagged breaking.
- Anything touching the public surface: package exports (
@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:
- Pure-internal refactors with no observable behaviour change.
- CI / workflow / tooling changes.
- Test-only PRs.
- Chore / dependency bumps (include only if a bump changes user-observable behaviour or a peer requirement).
- Doc-only changes (include only if they document a user-facing behaviour change worth surfacing).
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.
5. Categorize into the fixed section order
Write the entries under the fixed section order from docs/releases/README.md, omitting any section with no entries:
- Breaking changes — API removals/renames, semantic changes to existing APIs, contract-format changes. Say what the reader must do, not just what changed.
- Features — new capabilities.
- Fixes — bug fixes.
- New contributors — first-time contributors, with the PR that welcomed them.
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](https://github.com/prisma/prisma-next/pull/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.
6. Anchor breaking-change entries to their migration recipe
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:
- User-facing migrations:
https://github.com/prisma/prisma-next/blob/v$NEXT/skills/upgrade/prisma-next-upgrade/upgrades/<prev.minor>-to-<head.minor>/
- Extension-author migrations:
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.
7. Show the impact of code-visible breaking changes with a before/after example
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.
- Source it from the recipe, don't invent it. The matching
<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.
- Keep it tight. A few lines before, a few lines after — enough to show the shape, not the whole file.
- Lead with PSL. When the change is on the authoring surface, write the example in PSL (
```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.
- Skip operational-only breaks. Version-floor bumps, peer-dependency changes, and package removals/extractions have no illuminating code diff — prose suffices for those.
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:
```ts
…
```
After:
```ts
…
```
8. Attribute contributors
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).
9. Write the notes file and prepend the CHANGELOG
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:
```ts
<old shape>
```
After:
```ts
<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).
10. Commit on the release branch
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.
Graceful degradation
- Linear unavailable, or a PR has no Linear ticket. Linear is enrichment, not a hard dependency. Summarize the change from the PR title + diff alone. Never block authoring on Linear.
- No prior stable tag (first release under this convention). If
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.
- Skipped-publish multi-minor range. If the previous stable tag is more than one minor behind
$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.
- Symlink trees may be absent in the release worktree.
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.
Out of scope
- The publish-time presence gate and
--notes-file wiring (scripts/check-release-notes.mjs, .github/workflows/). This skill produces the file; the gate and workflow consume it.
- Wiring this skill into
publish-npm-version and updating docs/oss/versioning.md — handled separately; this file is the authoring logic only.
- Dev/beta release notes. Those builds create no GitHub Release.
- Backfilling notes for already-shipped releases.
- A
.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.
Worked example
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.
- PR #1240's title is
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.
- Triage: #1240 changes the contract format → always-include, breaking. A CI-cache tweak (#1237) and a test-only refactor (#1239) → default-exclude, dropped silently. A new
includeMany capability (#1234) → feature. A null-handling bug fix (#1242) → fix. First-time contributor @somebody on #1238.
- Categorize: Breaking changes (#1240) → Features (#1234) → Fixes (#1242) → New contributors (@somebody, #1238).
- The breaking change's transition is
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.)
- #1240 is a code-visible contract-shape/runtime change, so it earns a before/after example — lifted from the
0.11-to-0.12 recipe (a TS runtime change, so a ts fence). @somebody's contributor line, with absolute links: - [@somebody](https://github.com/somebody) made their first contribution in [#1238](https://github.com/prisma/prisma-next/pull/1238).
- Write
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:
```ts
const contract = deserializeContract(json);
```
After:
```ts
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".
Reference
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.
- Linear ticket: TML-2758.