Why this command exists#
tag-release.yml generates release bodies from the changes since the last tag. That suits routine releases but reads poorly for milestones, where the story is what the version is. The curated-notes override has non-obvious rules (body-only format, the file must exist at the tagged ref), and release notes are the repo's most-read public text — this command encodes the review and hygiene checks that keep them accurate and safe to publish.
Instructions#
1. Decide whether to curate at all#
Not every release deserves curated notes. Routine patch releases should keep the generated changelog — skipping is a normal outcome, not a failure. Curate when the release is a milestone: a minor, a headline capability, or a version the documentation will reference. If the user invoked this command for a plain patch, say so and confirm they still want curated notes.
2. Establish the version and the range#
- The target version comes from the argument (e.g.
/release-notes 1.3.0). If none was given, ask what version the user intends to tag — the filename must match the eventual tag exactly, and a mismatched file is silently ignored. Derive it from the currentpackage.jsonversion plus the bump type the user will dispatch (1.2.15+minor→1.3.0). - Never bump the version yourself.
create-release.ymlbumpspackage.jsononmainas its first step and derives the tag from the result — a hand-bump collides with it. - The range depends on the release kind. A minor memorializes the whole series since the previous minor (
vX.(Y-1).0..origin/main) — patches got generated changelogs; the minor is the digest nobody gets from reading thirty of them. A curated patch or hotfix covers only the span since the last tag:
LAST=$(git tag --sort=-creatordate | head -1) # patch: last tag
# minor: previous minor tag, e.g. v1.2.0 when cutting v1.3.0
git log "$RANGE_START"..origin/main --merges --format='%s'
gh pr list --state merged --limit 30 --json number,title,mergedAtNote the generated links section will still compare against the last tag; the prose should state the span it covers (e.g. "since v1.2.0") explicitly.
3. Review the changes for real#
Do not write notes from commit subjects alone. Read the PR bodies (gh pr view <n>) and spot-check diffs where the description is thin. For a series-scale minor, group the merge subjects into themes first, then read the bodies of the load-bearing PRs per theme rather than all of them. Classify everything into features, fixes, infrastructure, and chores, then check specifically:
- Backend dependency. This app is a client of the RoboSystems API. If the release needs API behavior that only exists in a newer service version, the notes must say so — deploy order matters (API first, then app). Never describe a capability the deployed API doesn't yet serve.
- SDK bumps. This repo consumes
@robosystems/clientat source level (not through a facade), so a client major lands as real code changes here. A bump worth a line is one that changed call sites, not a lockfile-only refresh. - CloudFormation changes. Edits to
cloudformation/template.yamlorcloudformation/s3.yamlmean a stack update rides the deploy — note which stack. - Config surface. New GitHub Actions variables or environment variables need
npm run setup:ghabefore the deploy, not after. Call that out. - User-visible surface. Routes, pages, and flows added, renamed, or removed. This is the part users actually read the notes for.
4. Security disclosure review#
This repo is public, and release publication is decoupled from deployment — the notes are world-readable immediately. For any security-adjacent change:
- Keep the line at PR-title neutrality: what area was hardened, never how or against what.
- No exploit mechanics, no affected-route enumerations, no detection signatures or thresholds, no "previously protected only by X" tells.
- Never paste content from private analysis documents into the notes.
- When in doubt, terser.
5. Write the file#
Write .github/release-notes/v<version>.md — body only:
- No
# RoboSystems App v<version>heading, no release-statistics section, no links section, no generated-with footer. The workflow supplies all of those. Start at the first line of prose. - The archived
v1.0.0–v1.2.0files keep their original headings and footers — they are records, not templates for this format. - Lead with one or two sentences saying what the version is. Then sections as warranted: key features, breaking changes (only if any truly exist), bug fixes, infrastructure. Ground every line in a change you actually reviewed.
6. Hand off — sequencing matters#
The file must exist at the tagged ref, and there is no window to add it late: create-release.yml bumps the version on main, cuts release/<version> from the result, and tags it in the same run. So the notes have to be merged into main before the workflow is dispatched — a file added to the release branch afterwards is already too late.
Write the draft on a feature branch (created via npm run feature:create), never on main. Present it for review and leave the merge and the dispatch to the user.