v0.76.2 published with no release notes. Its body is the Full Changelog link and nothing else, so the one user-facing change in the release, the self-updater fix in #1844, is not mentioned anywhere.
The regroup job is green. It gave up before the pass that exists to handle this case.
What the job did
Regroup release notes in run 34825544376. Its entire release-notes: output, then exit 0:
release-notes: tag=v0.76.2 base=v0.76.1 workdir=/home/runner/work/_temp/release-notes
release-notes: no PR entries in the published body; nothing to regroup
That is scripts/release-notes-generate.sh:37, which returns early when GitHub's generated body carries no * .../pull/N entry. The link pass runs at line 50, thirteen lines later, and its own comment states what it is for:
The link pass repairs what GitHub's generated list cannot express
So the guard fires exactly when GitHub's list is most wrong, and the repair never runs.
Why GitHub credited nothing
v0.76.1..v0.76.2 holds four commits. Three are release scaffolding. The fourth is:
259fb57345e2c9116d7ac41c644ad39946bb92ca
fix(autoupdate): install composed product-v2 bundles in the self-updater (#1844)
#1844 was merged into main as d23b9b094b3ecf5ba44a58d158e3686b3d0bc6e5 and reached the release by cherry-pick. Same change, two commits:
$ git show 259fb5734 | git patch-id --stable -> c027235232b5
$ git show d23b9b094 | git patch-id --stable -> c027235232b5
$ git branch -r --contains 259fb5734
upstream/hotfix/v0.76.2-autoupdate
GitHub credits a pull request through its merge commit, which is not in the range, so the generated body came out empty of entries. This is the same shape as #1831, except that here the count goes to zero, which is precisely the value the guard reads as "nothing to do".
The chain works, it was just never reached
I replayed the repo's own scripts, unmodified, against the live release, starting after the guard:
$ gh release view v0.76.2 --json body -q .body > body.github.md
$ cat body.github.md
**Full Changelog**: https://github.com/Mesh-LLM/mesh-llm/compare/v0.76.1...v0.76.2
$ python3 scripts/release-notes-link.py --body body.github.md \
--range v0.76.1..v0.76.2 --repo Mesh-LLM/mesh-llm --repo-root . \
--out-body body.md --out-links links.json
linked 0 commit(s) to a pull request through the API; recovered 1 entry(ies) GitHub did not credit (0 published)
$ python3 scripts/release-notes-classify.py --body body.md --range v0.76.1..v0.76.2 \
--version 0.76.2 --date 2026-09-14 --repo-root . --links links.json --out plan.json
classified 1/1 entries deterministically (0 in 'Other changes')
$ python3 scripts/release-notes-regroup.py --body body.md --plan plan.json --out notes.md
ok: regrouped 1 entries
notes.md:
## [0.76.2] - 2026-09-14
### Fixed
* Install composed product-v2 bundles in the self-updater by @i386 in https://github.com/Mesh-LLM/mesh-llm/pull/1844
**Full Changelog**: https://github.com/Mesh-LLM/mesh-llm/compare/v0.76.1...v0.76.2
Both invariants hold on that run. The never-drop gate, comm -23 between the published and the augmented entry sets, is empty, trivially so since nothing was published. The final gate, the pull requests in the rendered body against the pull requests in the plan, matches exactly.
The link cost nothing, incidentally: the cherry-picked subject keeps its (#1844) suffix, which resolve_pull_requests treats as authoritative, so the recovery took zero API calls on the link itself.
When this fires
Any release whose range reaches GitHub through cherry-picks rather than merge commits, which is the normal shape of a hotfix branch cut from the previous tag. A hotfix carrying one or two fixes is exactly the release where every entry matters and exactly the one where the count is most likely to hit zero.
Suggested fix
Make the early exit conditional on there being nothing to recover either, rather than on the published body alone. Concretely, move the guard below the link pass and test the augmented body, so the current behaviour is preserved for a genuinely empty range while a body GitHub failed to populate still gets repaired.
A guard placed there also turns silence into a signal: a range with (#N) subjects and an empty body after the link pass is a contradiction worth failing on, and the script already states that principle for itself a few lines above, "A failure here is a defect in our own tooling, so it fails the job loudly and publishes nothing".
I am happy to send the patch with unit tests if that helps, or to leave it to whoever owns this pass.
Not verified
I have not run scripts/tests/test_release_notes.py; this box has no yaml, which predates any of this. I replayed the pipeline on Windows 11 with Python 3.13.0 rather than on the ubuntu-24.04 runner, and the three scripts above are platform-independent, but I have not reproduced the job itself. The Publish crates.io packages failure in the same run is separate and I have not looked into it.
v0.76.2 published with no release notes. Its body is the Full Changelog link and nothing else, so the one user-facing change in the release, the self-updater fix in #1844, is not mentioned anywhere.
The regroup job is green. It gave up before the pass that exists to handle this case.
What the job did
Regroup release notesin run 34825544376. Its entirerelease-notes:output, then exit 0:That is
scripts/release-notes-generate.sh:37, which returns early when GitHub's generated body carries no* .../pull/Nentry. The link pass runs at line 50, thirteen lines later, and its own comment states what it is for:So the guard fires exactly when GitHub's list is most wrong, and the repair never runs.
Why GitHub credited nothing
v0.76.1..v0.76.2holds four commits. Three are release scaffolding. The fourth is:#1844was merged intomainasd23b9b094b3ecf5ba44a58d158e3686b3d0bc6e5and reached the release by cherry-pick. Same change, two commits:GitHub credits a pull request through its merge commit, which is not in the range, so the generated body came out empty of entries. This is the same shape as #1831, except that here the count goes to zero, which is precisely the value the guard reads as "nothing to do".
The chain works, it was just never reached
I replayed the repo's own scripts, unmodified, against the live release, starting after the guard:
notes.md:Both invariants hold on that run. The never-drop gate,
comm -23between the published and the augmented entry sets, is empty, trivially so since nothing was published. The final gate, the pull requests in the rendered body against the pull requests in the plan, matches exactly.The link cost nothing, incidentally: the cherry-picked subject keeps its
(#1844)suffix, whichresolve_pull_requeststreats as authoritative, so the recovery took zero API calls on the link itself.When this fires
Any release whose range reaches GitHub through cherry-picks rather than merge commits, which is the normal shape of a hotfix branch cut from the previous tag. A hotfix carrying one or two fixes is exactly the release where every entry matters and exactly the one where the count is most likely to hit zero.
Suggested fix
Make the early exit conditional on there being nothing to recover either, rather than on the published body alone. Concretely, move the guard below the link pass and test the augmented body, so the current behaviour is preserved for a genuinely empty range while a body GitHub failed to populate still gets repaired.
A guard placed there also turns silence into a signal: a range with
(#N)subjects and an empty body after the link pass is a contradiction worth failing on, and the script already states that principle for itself a few lines above, "A failure here is a defect in our own tooling, so it fails the job loudly and publishes nothing".I am happy to send the patch with unit tests if that helps, or to leave it to whoever owns this pass.
Not verified
I have not run
scripts/tests/test_release_notes.py; this box has noyaml, which predates any of this. I replayed the pipeline on Windows 11 with Python 3.13.0 rather than on theubuntu-24.04runner, and the three scripts above are platform-independent, but I have not reproduced the job itself. ThePublish crates.io packagesfailure in the same run is separate and I have not looked into it.