Skip to content

Two pull requests can add the same ADR number and both stay green #123

Description

@macanderson

Problem

Two pull requests can each add a new ADR with the same number, and every check passes on both. The tree ends up with two ADRs claiming one number and nothing reports it.

This is not hypothetical — it happened today:

The filenames differ, so there is no path conflict. Git merges both cleanly, neither author sees a warning, and each PR's CI is green because each tree is individually valid. The damage only appears after the second merge, in a tree neither author ever tested — the same shared-cell shape that makes lockfile and baseline collisions escape pre-merge CI.

An ADR number is an address. Prose, code comments and other ADRs cite decisions as "ADR 0012", so two documents holding that number makes every citation ambiguous, permanently and silently.

Why a human is unlikely to catch it

The next free number is discovered by listing docs/adr/, which every concurrent author does before any of them lands. Each one correctly reads 0011 as the highest and correctly picks 0012. Nobody is being careless; the information needed to avoid the collision does not exist on any single branch.

What to build

A gate step that fails when two files in docs/adr/ share a leading number. It must run against the merge result, not the branch — a branch-local check cannot see the other PR, which is the whole problem. check-deleted-tests in this org is the existing example of a check that compares two trees on pull_request, and is the shape to copy.

Worth deciding at the same time:

  • Whether an ADR index (docs/adr/README.md, docs/GUIDE.md's ADR list, or a docs/manifest.json) should be required to carry an entry per ADR, so a missing entry fails rather than merging silently.
  • Whether to drop sequential numbers entirely in favour of date- or slug-addressed ADRs, which removes the shared cell instead of guarding it. This is the more durable answer and deserves an explicit rejection if it is not taken.

Files

  • docs/adr/ — the ADRs.
  • .github/workflows/ci.yml — where a pull_request-scoped guard job would go.
  • .github/scripts/ — where the repo keeps its guard scripts.

Verify

  1. Construct two branches off one base, each adding a differently-named docs/adr/0099-*.md.
  2. Merge the first. The guard must fail on the second — and must have been green on it before the first merge, since nothing was wrong then.
  3. A tree with unique ADR numbers passes.

Step 2 is the whole test. A guard that only checks the branch in isolation will pass both and prove nothing.

Done when

Two PRs cannot land ADRs sharing a number without a red check, and the guard has a self-test showing it actually fails on the colliding case.

Found while coordinating eight concurrent PRs against this repository; the collision above was caught by hand and the numbers reallocated, which is not a control that scales.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Next cycle

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions