Skip to content

Changelog lines and claims in one file per pull request, so parallel and ported pull requests don't conflict #525

Open

Description

@CaffeinatedCoder

Problem

Almost every pull request adds to the same few places: a line in the version's section of CHANGELOG.md, the same line again in one or more of the four package changelogs, and claims in test/oracles/claims.json. When several pull requests are open at once, each merge can leave the rest conflicting. A conflicting pull request stops the merge train (it only runs update-branch on BEHIND and merges on CLEAN), so someone has to merge the base in by hand, resolve it, push and wait for the whole CI again. On 2026-10-25 a scripted resolution committed conflict markers into four changelogs, which #402 then had to fix.

Ports conflict more. A changelog line lands under ## [11.0.0-preview.N] on main and under ## [10.x.0] on release/10.x, so a cherry-pick of a commit that carries one almost never applies as it is.

Measured on 2026-10-29, since v10.0.0 (2026-10-19), by replaying each merge with git merge-tree --write-tree:

  • Base merged into a PR branch: 45 times. 36 were clean, needed only because both main and release/** require a branch to be up to date (strict: true, kept on purpose on 2026-10-25). 9 conflicted:

    File Conflicts
    CHANGELOG.md 5
    src/CodoMetis.ValueRanges.EFCore.PostgreSQL/CHANGELOG.md 5
    test/oracles/claims.json 4
    src/CodoMetis.ValueRanges.EFCore.PostgreSQL.NodaTime/CHANGELOG.md 4
    docs/efcore.md 4
    DeclaredNullabilityTests.cs 1
  • Cherry-picks into release/10.x: 57, each replayed onto its port's parent (--merge-base=<source>^ <port>^ <source>). 33 conflicted. 8 of those conflicted only in changelogs or claims.json, and so would have applied cleanly. CHANGELOG.md conflicted in 23, the EF Core changelog in 15, the EF NodaTime one in 9, the core one in 6 and the NodaTime one in 4. The rest are real overlaps: docs/migration.md (9), oracles.json and candidates.json (4 each), and source files.

  • Claims. claims.json is 4,374 lines: 256 claims from 11 documents, sorted by id. An id's letter names a topic, not a document (E is used in docs/efcore.md, docs/getting-started.md and both NodaTime READMEs). 15 prefixes are taken twice, such as E46 in docs/ranges.md and docs/efcore.md (both added 2026-10-18 on parallel branches). Nothing fails, because the slugs differ and only the whole id has to be unique.

Strict protection stays, so this doesn't remove the re-runs. What it removes is the hand resolution. With no textual conflict, every BEHIND pull request is brought up to date by update-branch, and the train runs to the end unattended.

Proposal

Changelog, one fragment per pull request. Each pull request adds changes/<issue>.md. There's no version in the path, so the same commit cherry-picks onto either line without a conflict. The fragment says which packages the change affects (the package/* label names) and which subsection it goes under (Breaking, Changed answers, Added, Fixed, …), then holds the line or lines. The root changelog and each named package's changelog are built from the same fragment, so the line is written once instead of copied by hand.

The fragments are joined into each changelog's ## [<version>] section by the "date the changelogs" pull request (like #519), and deleted in it. So a released version's section stays the one source, and the release workflow and the links at the tag don't change. The joiner also writes the text that is generated today by hand: the "Some calls answer differently" line above ### Changed answers, and "No change in this package." for a package with no fragment. Decide the line order inside a subsection (issue number, or a field in the fragment) and write it down in CONTRIBUTING.

Readers to change or confirm:

  • Directory.Build.targets (ChangelogReleaseNotes, Packages: show the version's changelog section as NuGet release notes #346). Packing an undated version, which every preview on main and every CI run does, either reads the fragments or requires the joined section. Release notes must stay byte for byte what they are now.
  • .github/scripts/pr-hygiene.sh: a silent pull request into a minor needs its ### Changed answers row. That becomes a fragment with that subsection, linking the issue.
  • .github/workflows/release.yml, ChangelogConventionTests, ReleaseWiringConventionTests, ReleaseVersionTests and the packaging metadata tests.
  • test/consumer-smoke-test.sh, which checks that each nuspec's release notes open with the version's section.
  • CONTRIBUTING (#changelog, the release steps), AGENTS.md (the Pack paragraph), .claude/skills/plain-docs/SKILL.md.

Claims, one file per document. Replace test/oracles/claims.json with test/oracles/claims/<document>.json, such as claims/docs-efcore.json or claims/src-CodoMetis.ValueRanges.NodaTime-README.json. Each file holds the claims whose source is that document, sorted by id, and ids stay unique across all of them. Two pull requests that add promises to different documents then touch different files. Readers: OracleCatalogue.cs, tools/oracles.cs, OracleCatalogueTests (unique and sorted), OracleSurfaceCoverageTests, OraclePostgresSurfaceTests, ApiSurface.cs, docs/oracles.md, test/oracles/README.md, test/oracles/axes.md, docs/architecture.md, docs/testing.md, CONTRIBUTING, AGENTS.md and the plain-docs skill.

Decide whether an id's prefix should mean anything. If a letter belongs to one file, a test can refuse a prefix taken twice, and the 15 existing duplicates get renamed. If not, say so in docs/oracles.md.

Out of scope: docs/migration.md, docs/efcore.md, oracles.json and candidates.json also conflict, but there two pull requests are editing the same text.

Lands on main first and is cherry-picked into release/10.x, so both lines read the same layout.

Done when

  • Two pull requests branched from the same commit, each with a changelog fragment and claims for different documents, merge one after the other with only update-branch, with no hand resolution.
  • A commit with a fragment cherry-picks from main onto release/10.x with no conflict.
  • Packing a version built from fragments gives the same release notes as today's hand-written section. Compare against the 10.4.0 nuspecs.
  • pr-hygiene.sh's silent rule, oracles.cs status and oracles.cs seeds read the new layout. Each reader's test is seeded and caught.
  • CONTRIBUTING, AGENTS.md and docs/oracles.md describe the layout, on both lines.

Same change as CaffeinatedCoder/CodoMetis.TypeKit#143, adapted: here there are five changelogs, two lines that port to each other, and strict up-to-date protection.

Activity

  1. added this to the 11.0.0 milestone on Oct 29, 2026
  2. added
    choreBuild, CI, packaging, refactoring with no behaviour change
    area/releaseVersioning, changelogs, release wiring
    on Oct 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/releaseVersioning, changelogs, release wiringchoreBuild, CI, packaging, refactoring with no behaviour change

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions