Problem
docs/adr/ carries three duplicated identifiers: 0018, 0025, and 0028 each name two records. Nothing checks that a new ADR takes a free number, so the next collision lands the same way the first three did: two branches each compute "highest plus one" against the same base and both merge.
The architecture:record-decision skill's gotchas already state the posture for the existing pairs: report the duplicate, take highest plus one, never renumber (renumbering breaks every inbound link, and the duplicate is a fact about the repository's history). This issue is for the gate that stops a fourth pair, not for renumbering the three.
Proposed change
scripts/check-adr-numbers.sh --check: walk docs/adr/, extract the four-digit prefix of each record, and fail on any prefix carried by two or more files that is not in a baseline of the known pairs (0018, 0025, 0028). Print one number: file, file line per offender. Header states the rule and its reason.
scripts/check-adr-numbers.test.sh: co-located black-box suite on the fixture_tree.sh pattern: clean tree passes, a fresh duplicate fails and is named, a baselined pair passes, a baseline entry that no longer collides fails (stale baseline).
- A
lint job step pair beside the docs-naming pair (Run adr-number tests gated on run_shell, then Check ADR numbers are unique with id: adr_numbers, continue-on-error: true) and an adr-numbers=${{ steps.adr_numbers.outcome }} line in the aggregation block.
Context
Surfaced while writing ADR 0033 (docs/adr/0033-name-docs-files-lower-kebab-case-with-conventional-exceptions.md) in #4097, whose plan computed the number at write time and re-checked it after a base sync because no gate exists. The same PR adds scripts/check-docs-naming.sh and its CI pair, which is the shape this gate should copy.
Problem
docs/adr/carries three duplicated identifiers:0018,0025, and0028each name two records. Nothing checks that a new ADR takes a free number, so the next collision lands the same way the first three did: two branches each compute "highest plus one" against the same base and both merge.The
architecture:record-decisionskill's gotchas already state the posture for the existing pairs: report the duplicate, take highest plus one, never renumber (renumbering breaks every inbound link, and the duplicate is a fact about the repository's history). This issue is for the gate that stops a fourth pair, not for renumbering the three.Proposed change
scripts/check-adr-numbers.sh --check: walkdocs/adr/, extract the four-digit prefix of each record, and fail on any prefix carried by two or more files that is not in a baseline of the known pairs (0018,0025,0028). Print onenumber: file, fileline per offender. Header states the rule and its reason.scripts/check-adr-numbers.test.sh: co-located black-box suite on thefixture_tree.shpattern: clean tree passes, a fresh duplicate fails and is named, a baselined pair passes, a baseline entry that no longer collides fails (stale baseline).lintjob step pair beside the docs-naming pair (Run adr-number testsgated onrun_shell, thenCheck ADR numbers are uniquewithid: adr_numbers,continue-on-error: true) and anadr-numbers=${{ steps.adr_numbers.outcome }}line in the aggregation block.Context
Surfaced while writing ADR 0033 (
docs/adr/0033-name-docs-files-lower-kebab-case-with-conventional-exceptions.md) in #4097, whose plan computed the number at write time and re-checked it after a base sync because no gate exists. The same PR addsscripts/check-docs-naming.shand its CI pair, which is the shape this gate should copy.