.agents/skills/release-changelog/SKILL.md
Generate the user-facing changelog for the stable Paperclip release.
Paperclip uses calendar versioning (calver):
YYYY.MDD.P (e.g. 2026.318.0)YYYY.MDD.P-canary.N (e.g. 2026.318.1-canary.0)vYYYY.MDD.P for stable, canary/vYYYY.MDD.P-canary.N for canaryThere are no major/minor/patch bumps. The stable version is derived from the intended release date (UTC) plus the next same-day stable patch slot.
Output:
releases/vYYYY.MDD.P.mdrelease Case, upserted by (caseType, key) when Cases are enabled, with a
body document revision containing the changelog bodyImportant rules:
2026.318.1-canary.0, the changelog file stays releases/v2026.318.1.mdStables promote a soaked beta, so the changelog describes the beta's
source commit, not the tip of master:
The release source is the commit the newest beta/v<beta-version>
tag points at ({beta-src} below). Resolve it with:
git fetch origin --tags
npm view paperclipai dist-tags # the beta dist-tag names the version
git rev-parse 'beta/v{beta-version}^{commit}'
Commits on master after {beta-src} ship in the next release.
Never include them; they are input for a "what's next" section, not the
changelog.
During the soak, the file lives at releases/beta/v{beta-version}.md
on the branch release-notes/v{beta-version} (PR to master). The
release workflow pushes that branch with a generated skeleton when the
beta publishes; work on it and rewrite the skeleton in place. If the
branch does not exist (a beta cut before the automation), create it
from origin/master and seed the skeleton:
./scripts/draft-stable-notes.sh {beta-version}
The PR must merge to master before the stable is dispatched: the
stable preflight reads the file from master and fails without it.
Never create releases/vYYYY.MDD.P.md yourself on this path — after
the stable ships, the workflow opens a canonicalization PR that renames
the beta-keyed file to it.
Fix path exception (patch releases from a candidate/release-*
branch): there the notes do go directly on the candidate branch as
releases/vYYYY.MDD.P.md, committed alongside the cherry-picked fixes.
Before generating anything, check whether the changelog already exists:
ls releases/beta/v{beta-version}.md 2>/dev/null # soak-window home
ls releases/vYYYY.MDD.P.md 2>/dev/null # canonicalized / fix path
git ls-remote origin 'refs/heads/release-notes/v{beta-version}'
A release-notes/v{beta-version} branch holding only the generated
skeleton is the normal starting state, not a conflict — rewrite it in
place.
If it exists:
Find the last stable tag and the beta source commit:
git tag --list 'v*' --sort=-version:refname | head -1
beta_src="$(git rev-parse 'beta/v{beta-version}^{commit}')"
git log v{last}..${beta_src} --oneline --no-merges
The changelog range is always v{last}..{beta-src} — never ..HEAD and
never ..origin/master.
The stable version comes from one of:
./scripts/release.sh stable --date YYYY-MM-DD --print-versiondoc/RELEASING.mdDo not derive the changelog version from a canary tag or prerelease suffix. Do not derive major/minor/patch bumps from API intent — calver uses the date and same-day stable slot.
Collect release data from:
.changeset/*.md filesgh when availableUseful commands:
git log v{last}..{beta-src} --oneline --no-merges
git log v{last}..{beta-src} --format="%H %s" --no-merges
ls .changeset/*.md | grep -v README.md
gh pr list --state merged --search "merged:>={last-tag-date}" --json number,title,body,labels
Look for:
BREAKING: or BREAKING CHANGE: commit signalsKey commands:
git diff --name-only v{last}..{beta-src} -- packages/db/src/migrations/
git diff v{last}..{beta-src} -- packages/db/src/schema/
git diff v{last}..{beta-src} -- server/src/routes/ server/src/api/
git log v{last}..{beta-src} --format="%s" | rg -n 'BREAKING CHANGE|BREAKING:|^[a-z]+!:' || true
If breaking changes are detected, flag them prominently — they must appear in the Breaking Changes section with an upgrade path.
Use these stable changelog sections:
Breaking ChangesHighlightsImprovementsFixesUpgrade Guide when neededExclude purely internal refactors, CI changes, and docs-only work unless they materially affect users.
Guidelines:
When a bullet item clearly maps to a merged pull request, add inline attribution at the end of the entry in this format:
- **Feature name** — Description. ([#123](https://github.com/paperclipai/paperclip/pull/123), @contributor1, @contributor2)
Rules:
Merge pull request #N from user/branch) to map PRs.([#10](url), [#12](url), @user1, @user2).The file path is the beta-keyed one from the Channel Process section
(releases/beta/v{beta-version}.md), but the content is titled with
the planned stable version. Resolve it with
./scripts/release.sh stable --date {planned-promotion-date} --print-version
(promotion is normally the beta publish date plus the 3-day soak). If the
promotion date slips, the version re-resolves at dispatch — the beta-keyed
filename makes that harmless; refresh the title when it happens.
The opening line of the changelog must be an H1 of the format # Paperclip {version}
(no braces), e.g. # Paperclip v2026.618.0. Always include the Paperclip prefix and
the v on the version.
Template:
# Paperclip vYYYY.MDD.P
> Released: YYYY-MM-DD
## Breaking Changes
## Highlights
## Improvements
## Fixes
## Upgrade Guide
## Contributors
Thank you to everyone who contributed to this release!
@username1, @username2, @username3
Omit empty sections except Highlights, Improvements, and Fixes, which should usually exist.
The Contributors section should always be included. List every person who authored
commits in the release range, @-mentioning them by their GitHub username (not their
real name or email). To find GitHub usernames:
git log v{last}..{beta-src} --oneline --merges — the branch prefix (e.g. from username/branch) gives the GitHub username.[email protected], the username is the part before @.gh api users/{guess} or the PR page.Never expose contributor email addresses. Use @username only.
Exclude bot accounts (e.g. lockfile-bot, dependabot) from the list.
Exclude specific folks from the list — the Contributors section credits
community contributors only. The canonical exclusion list (keep it here;
the Discord skill defers to it):
cryppadotta, forgottendev, devinfoley, sockmonster, scotttong,
nguyenm7, nickyleach, tonio-alucema
List contributors in alphabetical order by GitHub username (case-insensitive).
If there are no contributors left after exclusions, then just skip this section and don't mention it.
After writing releases/vYYYY.MDD.P.md, emit or refresh the top-level release
case when the run has Paperclip API context. Use skills/paperclip/references/cases.md
as the API contract. If the API returns 403 Cases are disabled, report that
Cases must be enabled and continue with the changelog file only.
Request:
POST /api/companies/:companyId/cases
{
"caseType": "release",
"key": "paperclip-release:vYYYY.MDD.P",
"title": "Paperclip vYYYY.MDD.P release",
"summary": "Stable Paperclip release notes for vYYYY.MDD.P.",
"status": "in_progress",
"fields": {
"schema_version": 1,
"version": "vYYYY.MDD.P",
"release_date": "YYYY-MM-DD",
"release_patch": 0,
"stable": true,
"channels": ["changelog", "blog_post", "tweet_storm"],
"artifacts": {
"changelog_path": "releases/vYYYY.MDD.P.md",
"github_release_url": null
},
"verification": {
"typecheck": "unknown",
"tests": "unknown",
"build": "unknown",
"smoke": "unknown"
},
"notes": null
}
}
This fields schema deliberately exercises every generic field value type: string, number, boolean, array, object, and null. Keep the keys stable across runs and send the full object on every upsert because fields are replaced, not deep-merged.
Then write the changelog into the case body document:
PUT /api/cases/:releaseCaseId/documents/body
{
"title": "Paperclip vYYYY.MDD.P changelog",
"format": "markdown",
"body": "<contents of releases/vYYYY.MDD.P.md>",
"changeSummary": "Initial stable changelog"
}
If updating an existing body document, fetch the case first and pass the latest
baseRevisionId. On 409 stale_base_revision, refetch, merge intentionally,
and retry once.
Before handing it off:
# Paperclip {version} (e.g. # Paperclip v2026.618.0) with the stable version only-canary language in the title or filenamerelease case exists or explain why Cases were unavailableThis skill never publishes anything. It only prepares the stable changelog artifact.