feat(release_library): aggregate prerelease notes into graduated Lerna releases - #158
Merged
Conversation
…a releases `lerna publish --conventional-graduate` resolves conventional-changelog's `from` to the package's last PRERELEASE tag, so the commit range is empty and the GitHub Release body becomes `**Note:** Version bump only for package <pkg>` — every itemised change of the prerelease line is missing from the stable release. Confirmed on real graduations, incl. `@side-eng/test-monorepo-hello@10.0.0`, a major whose BREAKING CHANGES note is absent. `packages/<pkg>/CHANGELOG.md` is committed, so the detail survives; only the release body is empty. This adds a post-publish step that patches release BODIES only — no commit, no push, no bypass actor, no workflow re-trigger — keyed per package (all Lerna repos use `"version": "independent"`). The aggregation engine ships as `@side/graduation-notes`, the same package Adapter A resolves through `@side/semantic-config-base`, installed at release time. Until it is published the step warns and skips, so today's releases are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mathieudi
requested review from
alexsupp and
prescottprue
and removed request for
a team
July 30, 2026 16:25
Lets a repo point at a vendored engine instead of the registry install — needed to validate before @side/graduation-notes publishes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…before merge) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A graduation whose changelog range includes the squash commit publishes a one-entry body rather than the sentinel, losing the prerelease detail all the same. Detection now defers to the engine's nothing-new-vs-stable no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alexsupp
approved these changes
Jul 30, 2026
|
🎉 This PR is included in version 1.18.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adapter B of the graduation release-notes mechanism (design:
github-repo-alignment/12-graduation-notes-design.md§3). Adapter A covers the 11 semantic-release libraries via@side/semantic-config-base; this covers the Lerna path.The Lerna path is worse than semantic-release, not better.
--conventional-graduatestarts conventional-changelog's range at the last prerelease tag, so a graduation's range is empty and lerna writes**Note:** Version bump only for package <pkg>— the stable release body isn't collapsed, it's empty. That already shipped a real major with its BREAKING CHANGES note missing (@side-eng/test-monorepo-hello@10.0.0).What this does
A composite action run once after publish, gated on
main && IS_MONOREPO && ENABLE_GRADUATION_NOTES, using the App token the job already mints (contents:write— no permission change). It reads the tags at HEAD, finds the ones whose release body is the bump-only sentinel, aggregates that package's in-range prerelease releases via the shared engine, and PATCHes the release body.Release bodies only — no commit, no push, no bypass actor, no re-trigger loop. Lerna's
CHANGELOG.mdis a committed file, so the prerelease entries already survive the squash there; only the release body needed repair.It re-reads every release it patched and asserts the marker, the entry count and a byte-identical round-trip (design §4 guard 2), failing loudly otherwise. Idempotent twice over: the sentinel is gone after the first patch, and the engine's marker would no-op anyway.
Engine delivery
Installed from the registry at release time (
@side/graduation-notes@^1), deliberately not vendored — the design made the engine a package precisely so the two adapters can't drift. Until reside-eng/release-config#205 publishes it, the step warns and skips; verified live (npm returns E404 today, adapter exits 0, releases untouched). Same for a missing token, no tags at HEAD, or an API failure — the engine's policy is never to block a release on an unknown.Validation (local only, no mutations)
Mock releases API seeded from read-only dumps of all four Lerna repos, plus a throwaway git repo carrying the tags lerna would have created, run against the real adapter and real engine.
The design specified detection as "sentinel AND compare-from is a prerelease". That second condition produces false negatives on real losses and is now advisory only:
@side/fastify-pubsub@0.12.1carries the sentinel with a stable compare-from, yet0.12.1-alpha.0in that range published a real Bug Fix (#501). The broader rule — sentinel plus at least one in-range prerelease that isn't itself bump-only — is a strict superset and cannot false-positive, since a sentinel body states nothing and the sources are in-range ancestors of that exact package version by construction.Coverage
Covers 3 of 4 Lerna repos (platform-tools, lint-config, test-monorepo).
release-configis NOT covered — it runs an inlinerelease.ymlrather than this template. Cost of leaving it is currently zero (all its prerelease bodies are themselves bump-only, so there's nothing to aggregate), but migrating it onto the template is worth doing for its four other drifts, including the missing--conventional-graduate.🤖 Generated with Claude Code