draft-release-notes

Author the committed release-notes file for a Prisma 8 release (stable or `8.0.0-rc.N`) by enumerating the merged PRs since the previous release `v*` tag (stable or `-rc.N`), 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 release".

Install
npx skills add 'https://github.com/prisma/orm/tree/main/skills-contrib/draft-release-notes'
Download bundle ↓
main · fac8604Scanned 2026-09-17

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

SKILL.md

SKILL.mdBrowse 1 file
View on GitHub

Draft release notes

This skill fires inside a release cut (stable or RC-line, both published under latest). 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 release is being cut (stable or 8.0.0-rc.N) — the target version $NEXT is known (computed by publish-npm-version step 1, e.g. 8.0.0-rc.2).
  • You are in the release/<version> worktree with the bumped root version and reviewed, assembled upgrade guides committed, following the upgrade instruction lifecycle.
  • docs/releases/v$NEXT.md needs drafting or refreshing after new fragments were incorporated (the PR-mode release-notes gate, pnpm check:release-notes --mode pr, requires the notes file).

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 release tag

Reuse the actual prior published stable/RC ref resolved during release preparation. When invoked independently, resolve and confirm that published ref before proceeding. The range is "everything since the last release", so the lower bound is the most recent published release v* tag — stable or -rc.N — excluding -dev.* / -beta.* build tags (an -rc.N tag is a release and deliberately not excluded, so an RC respin's notes cover exactly what changed since the previous RC):

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 release tags gives the full set of changes since consumers last saw a 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:

# 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/orm/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 (@internal/* public API), CLI commands/flags, prisma.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:

  1. Breaking changes — API removals/renames, semantic changes to existing APIs, contract-format changes. Say what the reader must do, not just what changed.
  2. Features — new capabilities.
  3. Fixes — bug fixes.
  4. 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/orm/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

Release preparation synthesizes feature-PR fragments into one reviewed guide per audience before this step, following the upgrade instruction lifecycle. Link these published guides, never pending fragments or source archives. The transition label — written <transition-label> below — names both ends of the hop, each end rendered the way versionSegment() in scripts/check-upgrade-coverage.mjs renders it: a stable version truncates to major.minor (v0.11.00.12.0 gives 0.11-to-0.12), and a prerelease keeps its full version string (8.0.0-rc.1-to-8.0.0-rc.2). Point the breaking note at the recipe directory rather than restating the migration.

Recipe links must be absolute, tag-pinned URLshttps://github.com/prisma/orm/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/orm/blob/v$NEXT/skills/prisma-8/upgrading/app/upgrades/<transition-label>/
  • Extension-author migrations: https://github.com/prisma/orm/blob/v$NEXT/skills/prisma-8/upgrading/extension/upgrades/<transition-label>/

A breaking change can affect one or both audiences — link the relevant audience's guide. Both directories exist for chain continuity, but an empty guide is not a migration recipe for a breaking change.

If a required guide is absent or omits a migration, return to release preparation to repair and review it before finalizing notes. Inline action summaries do not replace release completeness.

For a skipped-publish range, use the assembled guide from the actual previous published release to the target (e.g. 0.11-to-0.13 when 0.12 never shipped). Do not invent an intermediate release boundary. Existing historical published chains remain intact.

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 assembled <transition-label> upgrade guide contains the reviewed migration instructions — lift before/after code 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.
  • When the guide has no useful code example, derive it from the PR diff or omit the example and describe the action in prose. Never invent a migration the diff does not show; missing required guides must be repaired during release preparation.
  • 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/orm/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/orm/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/orm/pull/<pr>))

  Before:

  ```ts
  <old shape>
  ```

  After:

  ```ts
  <new shape>
  ```

## Features

- <new capability>. ([#<pr>](https://github.com/prisma/orm/pull/<pr>))

## Fixes

- <bug fix>. ([#<pr>](https://github.com/prisma/orm/pull/<pr>))

## New contributors

- [@<handle>](https://github.com/<handle>) made their first contribution in [#<pr>](https://github.com/prisma/orm/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 published stable/RC tag (first release under this convention). For notes enumeration, fall back to the repo root and note in the summary that this is the first curated release. Do not substitute a dev/beta tag for a published release boundary.
  • Skipped-publish multi-minor range. Enumerate the whole range since the actual previous published stable/RC ref and anchor to the assembled actual-release-to-target guide.
  • 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).

  1. PREV_TAG=$(git describe --abbrev=0 --tags --match 'v[0-9]*' --exclude '*-dev.*' --exclude '*-beta.*')v0.11.0.
  2. 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.
  3. 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.
  4. 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.
  5. Categorize: Breaking changes (#1240) → Features (#1234) → Fixes (#1242) → New contributors (@somebody, #1238).
  6. The breaking change's transition is 0.11-to-0.12. The recipe dir skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/ exists in the checkout → the breaking note links it as a tag-pinned URL, https://github.com/prisma/orm/blob/v0.12.0/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/. (If it were absent, return to release preparation before finalizing notes.)
  7. #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/orm/pull/1238).
  8. 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/orm/blob/v0.12.0/skills/prisma-8/upgrading/app/upgrades/0.11-to-0.12/). ([#1240](https://github.com/prisma/orm/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/orm/pull/1234))

## Fixes

- Null values in `returning()` projections no longer throw. ([#1242](https://github.com/prisma/orm/pull/1242))

## New contributors

- [@somebody](https://github.com/somebody) made their first contribution in [#1238](https://github.com/prisma/orm/pull/1238)

Then prepend the same body under ## v0.12.0 to CHANGELOG.md.

  1. git add docs/releases/v0.12.0.md CHANGELOG.md && git commit -s -m "docs(release): add release notes for v0.12.0".

Reference

Discovery context

Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.