changelog: the -dev re-arm should seed Unreleased with all three headings — and the non-empty guard must then count entries, not bytes #97
Replies: 1 comment
|
Triage outcome: accepted — minted as #98, with one premise corrected and all three open questions closed here rather than passed to a builder. The premise correction, because it changes the shape of the workThis discussion says the seeding belongs to the The So the two halves land in different places: the seeding is doctrine (README, The three open questions, answered
One thing added, because the proposal opens a hole of its ownCounting entries instead of bytes catches a wholly empty release. It does not catch the stamp that carries an unused empty heading into the published body — What is not in #98
Credit where it is due: the analysis and the eight-PR batch evidence are heavy-duty/rig#86's author's. One live confirmation to add to it — box's Closing as resolved; the work is #98, |
Uh oh!
There was an error while loading. Please reload this page.
The proposal
## Unreleasedshould always carry### Added,### Changedand### Fixed, even when empty — and the-devre-arm should be what seeds them.Why it lands here now, not in the product repos
rig#86 was filed on 2026-07-20, when each repo owned its own release flow. ceremony#13's conversion moved the re-arm upstream:
.github/workflows/release.ymlin this repo is what bumpsX.Y.Z→X.Y.(Z+1)-devand pushes it ("the release re-arms main itself"). A product repo cannot implement the seeding half of this proposal any more — the commit that would carry it is authored here.The same is true of the guard the proposal breaks. Release notes are extracted between
## <ver>and the next##; that extractor is nowlib/changelog.shin this repo, and it is here that a non-empty check would have to learn the difference between bytes and entries.It was filed in box, rig and cast — three twins of one flow. One issue here replaces all three.
The problem
Every open PR writes under
## Unreleased, which starts with no subsection headings. The first PR needing### Fixedcreates it; any other PR needing### Fixedcreates it too, at the same anchor in the same empty region. Git sees two sides inserting at one anchor and calls it a conflict.The conflict rate is therefore a function of how many PRs are open, not of whether they are related. Two changes touching entirely different subsystems still collide in
CHANGELOG.md. It compounds: each merge re-conflicts every PR still open, so a batch of N PRs costs on the order of N² rebases rather than N.Evidence from a real batch (rig, seven entries across eight PRs — 1
Added, 3Changed, 3Fixed): nearly every rebase hitCHANGELOG.md, and not one conflict was a genuine disagreement about content. Two shapes:### Changed, the other### Fixed. They compose perfectly; the conflict exists only because both had to create their heading. Seeding eliminates these entirely.### Fixedbullets at one anchor. Seeding does not fix these.The worst case was shape 1 with an ordering twist: a PR adding
### Addedconflicted against### Fixed, and resolving it correctly meant hoisting the new section, because the file ordersAdded → Changed → Fixed. Concatenating would have produced out-of-order release notes that nothing would have caught. Pre-seeded, that judgment call does not exist.The shape
A PR then only ever appends a bullet under an existing heading — never creates a heading, chooses a position, or reorders.
Seeding on the
-devside is what makes it safe: the template only ever exists in## Unreleased, so a stamped section never contains an empty heading and empty headings never ship into a published release body.The guard this breaks
The release flow asserts the section it is about to publish is non-empty. Three empty headings are textually non-empty, so a genuinely empty release would sail past.
The check must test for at least one real
-entry, not for bytes. Landing the seeding without this is a net loss — it trades a noisy, self-announcing conflict for a silent hole in a release gate. Whoever picks this up should treat the two as one change.Open questions for this repo
The reason this is a discussion and not a minted issue — three things the product-repo framing never had to answer:
Added/Changed/Fixedis what the three repos use today. Keep-a-Changelog also definesDeprecated,Removed,Security. Seeding all six would make the empty-heading noise worse; seeding three hardcodes a subset in shared machinery.mainmigrate? Consumers are already sitting on a## Unreleasedwith no headings; the seeding only takes effect at the next re-arm, so there is a window where the guard change is live and the template is not.Limits, stated honestly
changelog.d/, one file per PR, assembled at release time), which makes conflicts structurally impossible. Bigger change, deserves its own discussion if the cheap fix proves insufficient.Refs: heavy-duty/rig#86 (origin, closed as wrong-repo), ceremony#4 (
lib/changelog.sh), ceremony#13 (the conversion that moved the re-arm here)All reactions