Skip to content

chore(scripts): script per-server release notes with a reporter Thanks section (release:notes) - #5064

Merged
cliffhall merged 2 commits into
v2/mainfrom
v2/chore/5056-release-notes
Oct 7, 2026
Merged

cliffhall merged 2 commits into
v2/mainfrom
v2/chore/5056-release-notes

Conversation

@cliffhall

@cliffhall cliffhall commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Closes #5056

Description

Adds scripts/release-notes.mjs (npm run release:notes), a tested helper that writes a milestone release's notes per server. It replaces the shell recipe in the release skill's step 5a. It is modeled on the Inspector's helper (modelcontextprotocol/inspector#2591) and adapted to a release that publishes seven packages at seven versions.

The notes it produces:

## Packages                         every package at the merge SHA, its version, published / unchanged (registry-checked)
## <package> <version>              one per PUBLISHED package
### What's Changed                  the PRs that change files under its src/<dir>/ (a rename's source counts too)
## Repository                       every PR no published section took (CI, docs, skills, an unchanged server)
## New Contributors / Full Changelog   GitHub's trailer, verbatim
Release ledger: [<merge branch>](<ledger url>)
## Known issues                     only from --known-issue
## Thanks for helping us improve    one release-wide list, laid out as the Inspector's release notes are
  • PR list: releases/generate-notes, from the latest published Release (asked of GitHub, never sorted out of the mixed tag list) to --merge-sha.
  • Bucketing: a PR goes under each published package whose src/<dir>/ it changes. Anything left over goes under Repository, so no generated PR is dropped. The generated body is parsed strictly: a line it doesn't recognize throws rather than being skipped, and the ## New Contributors lines (which also end in /pull/N) are never mistaken for PR entries.
  • Thanks: one ## Thanks for helping us improve section for the whole release, in the same layout as the Inspector's release notes (* @user (#1, #2), most issues first, then by name). It credits the author of every issue the listed PRs close (closingIssuesReferences, paginated, plus Closes/Fixes/Resolves #N outside code, quotes and comments). Maintainers (admin/maintain/write), bots, deleted accounts, PR numbers and other repos' issues are excluded. The section is left out when nobody remains.
  • Packages table: manifests are read at --merge-sha through one GraphQL tree query, so no checkout of the merge commit is needed. Each version is checked against npm or PyPI. An answer other than 200 or 404 aborts the run (the old recipe's UNKNOWN).
  • Existing tag: every run refuses a milestone tag that already exists, because GitHub would then build the notes and the Release from that tag's commit, not --merge-sha.
  • Modes: the default is a preview that prints the notes and creates nothing. --draft creates a draft Release tagged --milestone at --merge-sha. It refuses a SHA that is not on main (compare API) and a tag that already has a Release or draft. There is no --publish. Publishing starts release.yml, so it stays the maintainer's act (step 5b now publishes the draft).
  • Fail fast: a non-zero gh exit, GraphQL errors, a missing node, an unknown permission value, an unrecognized generated line or a registry failure throws before anything is created.

Deviations from the issue, at the maintainer's direction

  • One Thanks section, not one per server. Item 5 asked for a Thanks subsection in each package section. On review, the maintainer asked for the Inspector's layout instead: a single release-wide list.
  • Outside-PR credit (item 6) stays a documented manual step. An earlier commit added a machine-readable Credit: @login line; the maintainer judged it unnecessary, so it is removed. The release skill's step 5a again says to add those people to the draft's Thanks by hand, and the harvest docs keep their v2/main wording.

Server Details

  • Server: none (repository-wide)
  • Changes: release tooling (scripts/), the release skill's step 5, RELEASING.md step 5, the AGENTS.md tree

Motivation and Context

The current Release's notes list only package names: no PRs, no issues, no contributors. Step 5a improved on that with one flat, untested shell list for the whole repo. A reader of a release wants to know what changed in the server they use. Under the issue-only contribution model, the people to credit are the issue reporters, per server. See #5056.

How Has This Been Tested?

No server behavior changes, so the evidence is unit tests plus a live preview rather than client runs.

Unit tests: scripts/release-notes.test.mjs, 38 tests in npm run test:scripts, at about 99.6% line coverage of the script (only the CLI entry lines are not exercised). They cover:

  • bucketing for one server, several servers and none, including a file renamed out of a server's directory
  • sections only for published packages
  • the Repository catch-all, with an unchanged server's PR falling through to it
  • the release-wide Thanks: its exact layout and position, each reporter listed once across servers, and every exclusion
  • keyword parsing and masking
  • pagination of closing refs
  • the generated-body parser rejecting unknown lines
  • a failed lookup at each of ten stages, a 503 from a registry, an unknown permission, GraphQL errors and missing nodes, each aborting with nothing created
  • --draft's exact gh release create call (SHA target, milestone tag, no --latest), and the refusals (SHA not on main, existing Release or draft, existing tag in any mode)

Acceptance probe: a preview of v2.0.0 (#4968). The v2.0.0 merge PR hasn't merged yet, so this preview targets the current origin/v2/main head (b5cea9df), from the latest published Release 2026.8.31:

npm run --silent release:notes -- --milestone v2.0.0 --merge-sha b5cea9df5f123fd8d408d37fe60616c13edfeb8c \
  --merge-branch v2/chore/4968-release-v2.0.0 --ledger-url https://example.invalid/ledger

Result: exit 0 in about 2.5 minutes, nothing created. 87 generated PRs were bucketed into five published sections (server-everything, -filesystem, -memory and -sequential-thinking at 1.0.0, and mcp-server-fetch 0.6.3) plus Repository. mcp-server-git and mcp-server-time read as unchanged because their versions are already on PyPI, so they get no section and their PRs fall under Repository.

Against the milestone, I took the v2.0.0 milestone's closed issues (83) whose authors have read permission (24 issues, 20 people) and compared them with the issue numbers in the preview's Thanks section (28 people, 33 issues):

Gate (npm run local:gate, under Node 22.23.3 on macOS): every stage passes except one pre-existing, macOS-only test, src/time test_get_current_time_errors[arguments5-expected5]. That test expects europe/warsaw to be rejected, but on macOS's case-insensitive filesystem zoneinfo resolves it. It fails identically on v2/main without this branch, and it is tracked in #5060 (v2.1.0). It fails validate:py and coverage:py for time; fetch and git pass both. The stages the && chain skipped after it were run on their own: verify:skills:cli and smoke exit 0. This PR touches no server. CI (Linux) is the authoritative run.

The script tests also ran under the local Node 26: they passed, as did every other stage up to a separate Node 26–only EISDIR-message mismatch in a memory test (not this branch; Node 22 passes it).

Breaking Changes

None. No server, published package or client configuration changes.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality): release tooling
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation (not applicable: no protocol surface is touched)
  • My changes follow MCP security best practices (not applicable to MCP; the helper never publishes, and it refuses a SHA not on main)
  • I have updated the server's README accordingly (not applicable: no server changes; RELEASING.md, AGENTS.md and the skills are updated)
  • I have added a changeset (npm run changeset) if this changes what a TypeScript server publishes (not applicable: nothing published changes)
  • I have tested this with an LLM client (not applicable: no client-observable surface; evidence is the tests and the live preview above)
  • My code follows the repository's style guidelines
  • New and existing tests pass locally (except the pre-existing macOS-only time: test_get_current_time_errors fails on macOS (case-insensitive tz database) #5060; see above)
  • I have added appropriate error handling
  • I have documented all environment variables and configuration options (the CLI flags are in the script header and the release skill's step 5a)

Additional context

  • The README and the project-structure skill describe scripts/ generically and list no individual script, so the per-script listing updated is the AGENTS.md tree.
  • The release skill is name-only, and no skill description changed, so skills:eval does not apply.
  • The helper uses only gh and fetch, with no git, so it gives the same result from any checkout.

🤖 Generated with Claude Code

…s section (release:notes)

Add scripts/release-notes.mjs (npm run release:notes), modeled on the
Inspector's helper and adapted to a seven-package release. It builds the
Packages table at the merge SHA (registry-checked), one section per
published package with its PRs and the community reporters it thanks, a
Repository catch-all so no generated PR is dropped, GitHub's trailer, the
ledger line and any known issues. Outside-PR authors are credited from a
maintainer-written `Credit: @login` line. Preview by default; --draft
creates the draft Release at the merge SHA; publishing stays manual.

The release skill's step 5a uses the helper in place of the shell
recipe, and 5b publishes the draft. The harvest instructions adopt the
Credit line.

Closes #5056

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@changeset-bot

changeset-bot Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d3bd74d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Existing milestone tags can redirect a release away from the verified merge SHA.

3 open findings
What changed in this PR

Adds per-server release notes tooling for the repository’s multi-package releases, replacing the manual shell recipe.

Changes:

  • Generates package sections, PR lists, and community reporter credits.
  • Supports previews and draft Releases without publishing.
  • Documents maintainer-authored Credit: lines and the updated release process.
File Description
scripts/​release-notes.test.mjs Tests note generation, credits, and draft safeguards.
scripts/​release-notes.mjs Implements release notes and draft creation.
RELEASING.md Updates the release process.
package.json Adds release:notes.
docs/​contribution-model.md Documents machine-readable credits.
AGENTS.md Lists the new release helper.
.claude/​skills/​release/​SKILL.md Replaces the shell recipe with the helper.
.claude/​skills/​issue-triage/​SKILL.md Adds the credit-line convention.

🧠 Review effort: Balanced


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/release-notes.mjs
Comment thread scripts/release-notes.mjs Outdated
Comment thread scripts/release-notes.mjs Outdated
…drop Credit lines

Thanks is now a single `## Thanks for helping us improve` list at the end
of the notes, laid out as the Inspector's release notes are, rather than a
subsection per server. The maintainer-written `Credit: @login` convention
is removed: outside-PR authors are added to the draft's Thanks by hand, as
before, and the harvest docs are back to their v2/main wording.

From Copilot's review: refuse a milestone tag that already exists (GitHub
would use its commit, not --merge-sha), and count a rename's source path
when bucketing a PR by server.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1 (3 findings), answered in each thread:

  • Existing milestone tag: fixed in d3bd74d. Every run refuses an exact existing refs/tags/<milestone> before generating anything.
  • Renamed files: fixed in d3bd74d. A rename's previous_filename now counts toward the server bucket.
  • Login casing in Thanks: no longer applies. It came from Credit: lines, which d3bd74d removes at the maintainer's direction, so logins now come only from issue authors' canonical spelling.

The same commit also moves Thanks to one release-wide section in the Inspector's layout (maintainer request); the PR body explains that change. Requesting round 2.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Markdown masking can consume valid closing references and silently omit reporter credits.

0 open findings

3 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Medium severity HTML comments incorrectly mask references inside code

scripts/​release-notes.mjs:408

HTML comments are removed before code is recognized. For example, a body containing The delimiter is followed by the inline code span <!--, then a prose Closes #1 line, returns no issue numbers. An opener inside fenced code has the same effect: it consumes the remaining body as an unclosed comment. If the closing reference exists only in the body, the reporter disappears from Thanks. Use context-aware masking so comment delimiters inside code remain literal, and add regression cases for both inline and fenced code.

Medium severity Inline-code regex accepts partial backtick delimiters

scripts/​release-notes.mjs:415

The inline-code regex accepts part of a longer backtick run as a closing delimiter. For a body containing a two-backtick span with a three-backtick run inside it, followed by a prose Closes #1 line and another inline span, subsequent matches consume the closing reference. This silently omits reporter credit when the reference exists only in the body. Require complete opening and closing backtick runs of equal length, and add a regression test for this sequence.

🧠 Review effort: Balanced

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 2: 0 open findings, and all 3 from round 1 are marked resolved. The review body adds two "previously missed" notes with no inline thread, answered here:

  • HTML-comment masking runs before code masking (proseOf, a <!-- inside an inline or fenced code span swallows a later Closes #N): declined as out of scope.
  • The inline-code regex can close on part of a longer backtick run: declined for the same reason.

Both need pathological Markdown in a PR body. Both matter only for a closing reference that exists in the body alone, but every PR in this repo is linked explicitly with addCloseIssueReferences, and closingIssuesReferences is the primary source. proseOf is ported unchanged from the Inspector's shipped helper (modelcontextprotocol/inspector#2591). Making it exact needs a real Markdown tokenizer, which is hardening #5056 did not ask for, and not worth an issue on its own.

The loop stops here: this round holds only declined findings, and nothing was pushed in response to it.

@cliffhall
cliffhall merged commit 7e751ec into v2/main Oct 7, 2026
37 of 38 checks passed
@cliffhall
cliffhall deleted the v2/chore/5056-release-notes branch October 7, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Script per-server release notes with PRs and a reporter Thanks section (release:notes)

2 participants