--- name: write-release-announcements description: Use this skill to write the release notes and the announcement texts for an upcoming release, from the pull requests merged since the previous tag. Produces releases//release-notes.md plus LinkedIn and Discord texts. Invokes when asked to "write release announcements", "write the release notes" or similar, and is invoked by the prepare-release skill. Does NOT bump versions, create tags or publish anything. --- # Write Release Announcements Turns the pull requests merged since the last release into the four texts a release needs. ## Usage write release announcements Runs standalone whenever the texts need to be written or rewritten, and is invoked by `prepare-release` as the step before the version is bumped. ## Where the texts go, and why the order matters Everything lands in `releases//` -- for example `releases/14.2.0/`: | File | Becomes | |---|---| | `release-notes.md` | the GitHub release body (`pack.yml` passes it to `gh release create --notes-file`); in ExRam.Gremlinq, `publishBlogPost.yml` then also copies it verbatim into the blog on docs.gremlinq.net | | `linkedin.md` | a manual LinkedIn post | | `discord-tinkerpop.md` | a manual post in the TinkerPop Discord | | `discord-dotnet.md` | a manual post in the .NET Discord | The LinkedIn and Discord channels described here, and the worked examples in [the channel style guide](references/channel-style.md), are ExRam.Gremlinq's own -- written for that library's audience and communities. A repository that shares this skill but does not run its own LinkedIn/Discord announcements should confirm with the user which of the four texts, if any, are wanted before writing all of them. **These files must be committed before `nbgv prepare-release` runs.** That command creates the branch the release tag is put on, and the current branch is then rebased *onto* it -- so the tag points at the earlier state. In ExRam.Gremlinq, `pack.yml` and `openAnnouncementChecklist.yml` both read these files out of the tag; a repository without those workflows still benefits from the same ordering, since a tag whose release-notes.md is missing falls back to a worse, auto-generated body. Commit them late and they will not be there. That is why this skill commits its own output rather than leaving it staged. ## Workflow 1. Run `scripts/collect-prs.sh`. It resolves the previous release tag, the target version (via `nbgv`) and the pull requests merged since, and prints JSON. Pass a tag as an argument to collect for a different range. 2. **Read the `excluded` list out loud to the user.** The script drops anything labelled `skip-changelog` and the release-preparation pull requests. That is a floor, not curation -- see below. 3. Propose a grouping of the remaining pull requests into Features / Fixes / Performance / Maintenance, together with anything you think should be dropped, and get agreement before writing. Propose dropping every pull request with `tests_only` set: its lead says "Tests only", so it changes nothing a reader of the notes would notice. Check the lead, since the flag only matches the words. 4. Write the texts into `releases//` following [the channel style guide](references/channel-style.md). `release_kind` in the JSON says whether this is a `patch`, `minor` or `major` release: a patch gets `release-notes.md` only, unless the user asks for the rest. 5. Run `scripts/check-lengths.sh releases/` and fix what it reports. Missing release notes fail it; the three channel texts are optional, so a patch release passes with only the notes. A Discord text over 2000 characters is rejected by Discord outright. 6. Commit: `git add releases/ && git commit -m "Add release notes and announcements for "`. When only `release-notes.md` was written, say so: `"Add release notes for "`. ## Curation is not automatic The `skip-changelog` label removes the obvious chores. It does not decide what is worth announcing, and the history shows why: the hand-written notes for 14.1.1 covered 7 of the 13 pull requests in range. Kept were a CI change (Trusted Publishing) and a test-reporting change; dropped were dependency bumps and Actions maintenance. There is no rule that separates those -- it is a judgement about what a reader of the changelog would care about. So: propose, show what you would drop and why, and let the user decide. Do not quietly truncate, and do not pad the notes with everything in range either. The range for 14.1.2 shows what the label misses. Eight pull requests there only added contract tests ("Say that a bulk set may shout its type name", "Tests only."), and none carried `skip-changelog`. They described behaviour the library already had, so a release note for them would have announced a change that never happened. `tests_only` catches that case; reading the leads still catches the rest. ## Writing the entries Each pull request in the JSON carries a **`lead`** field: the text above its first `##` heading, with the pull request template's HTML comment already stripped. That is the change's changelog entry. Use `lead`, not `body` -- authors routinely leave the template comment in, and it sits above that heading, so reading `body` by hand would carry "Replace this comment with one to three sentences..." into the published notes and from there, verbatim, into the blog post. If a `lead` runs longer than about two sentences, summarise it -- the notes are scanned, not read. If a `lead` is empty or useless, the entry cannot be reconstructed honestly from the title alone. Say so, and either read the diff for that one change or ask. Do not paraphrase the title back as if it were a description; that is the failure mode the `open-pull-request` skill exists to prevent, and it should not be reintroduced here. ## Key requirements - Version comes from `nbgv get-version`, never from a guess or from `version.json` read by hand. - Every release-note entry links to its pull request as `([#2417](url))`. - **No author names.** Nearly every pull request here is the maintainer's own. - Do not create tags, do not bump `version.json`, do not push. That is `prepare-release`. - Do not post anything anywhere. Every text is posted by hand, and whether it is posted at all is the maintainer's decision. ## References - [Channel Style](references/channel-style.md) - what each of the four texts is for, its limits, and a worked example of each