You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Changelog fragments: stop concurrent PRs conflicting on CHANGELOG.md #3563
Every writer of CHANGELOG.md appends at one insertion point (## [Unreleased] / ### Fixed), so any two open PRs conflict as soon as one merges. In clabonte/generator, 385 of the 570 commits on main since 2026-10-01 touch the file.
A merge=union attribute makes local rebases clean, but GitHub's merge button ignores merge drivers. The PR still shows as conflicting, and a conflicted PR gets no CI run. Rebasing each item onto current main (FEAT-2026-0200) only helps the agent at the moment it opens the PR: two PRs open at once still conflict when either merges.
Why upstream
An adopter can't move fully to fragments locally, because the writers and the enforcement live here:
/fix-bug's changelog step
close-discipline.md §3
changelog.py's append_entry and stamp_release
assert_changelog_entry_for_contract_changes (close-k) and specfuse lint --closing, which require a feature close's entry to be in CHANGELOG.md
This was first raised as a comment on #3451, a closed roadmap PR, so it has no tracking item of its own. This issue is that item.
Proposal
One entry per file under changelog.d/, carrying its class (added / changed / fixed / breaking) and its trace (FEAT-… or #N). Naming the file after the trace means PRs for different issues can't collide.
/fix-bug and the close step write a fragment.
close-k accepts an entry in either a fragment or CHANGELOG.md.
The release step collates fragments into the version section through the existing write path, deletes them in the same commit, and fails if parse_changelog reports a finding on the result.
Opt-in per project, so an adopter that keeps a single file sees no change.
Adopter status
clabonte/generator is building an interim hybrid in clabonte/generator#3283: bug PRs write fragments through a local override, feature closes keep appending to CHANGELOG.md to satisfy close-k, and its own release script collates. Once this lands, the local override can go. Design detail and measurements: clabonte/generator#1973, #1958.
Done when
Two open PRs that each add a changelog entry don't conflict when one merges, whether they come from /fix-bug or a feature close.
A feature close whose entry exists only as a fragment passes close-k and specfuse lint --closing.
A released section contains every fragment, parses cleanly, and leaves no consumed fragment behind.
Implemented in #3566 as an opt-in changelog.mode: fragments in .specfuse/layout.yml; projects on the default single mode keep the same read and write path.
Writer: python3 -m specfuse.loop.changelog_fragments add, with collate and check alongside it.
close-k, specfuse lint --closing and the terminal auto-close guard share one reader, so an entry counts in a fragment or in CHANGELOG.md.
scripts/bump_version.py folds fragments before stamping and deletes them. A custom release script must call collate before it stamps; docs/lifecycles.md says so, but no test covers an adopter's script.
Not yet proven: GitHub's merge button itself. The oracle uses real git merge, so opening two real PRs that each add a fragment is the first post-merge check.
Control PR #3574 (direct CHANGELOG.md edit) merged
#3575 went CONFLICTING / DIRTY, and its merge was refused
Limits: merges used gh pr merge --squash, not a click on the button, against a throwaway base rather than main, so branch protection on main was not exercised. The base branch is deleted.
Decision: FEAT-LOOP-2026-0200's optional append-only re-apply is skipped.
Activity
clabonte commentedon Nov 3, 2026
Implemented in #3566 as an opt-in
changelog.mode: fragmentsin.specfuse/layout.yml; projects on the defaultsinglemode keep the same read and write path.python3 -m specfuse.loop.changelog_fragments add, withcollateandcheckalongside it.close-k,specfuse lint --closingand the terminal auto-close guard share one reader, so an entry counts in a fragment or inCHANGELOG.md.scripts/bump_version.pyfolds fragments before stamping and deletes them. A custom release script must callcollatebefore it stamps;docs/lifecycles.mdsays so, but no test covers an adopter's script.Not yet proven: GitHub's merge button itself. The oracle uses real
git merge, so opening two real PRs that each add a fragment is the first post-merge check.FEAT-LOOP-2026-0249: changelog fragments so concurrent PRs stop confl…
clabonte commentedon Nov 4, 2026
Post-merge check passed, on released 0.32.0 code. Two PRs that each add a fragment do not conflict on GitHub; direct
CHANGELOG.mdedits still do.MERGEABLECHANGELOG.mdedit) mergedCONFLICTING/DIRTY, and its merge was refusedLimits: merges used
gh pr merge --squash, not a click on the button, against a throwaway base rather thanmain, so branch protection onmainwas not exercised. The base branch is deleted.Decision: FEAT-LOOP-2026-0200's optional append-only re-apply is skipped.