The gap
The changeset gate in lint accepts any fragment matching:
^changelog\.d/[0-9]+-[^/]+\.md$
That requires some digits, not this PR's number. So both of these pass:
changelog.d/0000-<slug>.md — the placeholder, when an author has not filled in the number yet
changelog.d/<other-PR>-<slug>.md — a number belonging to a different PR
changelog.d/README.md says filenames are PR-keyed precisely so in-flight PRs cannot collide, and the failure is silent: fragments are only folded into release notes at tag time by scripts/cut_release_notes.sh, so a wrong number is invisible until a release is cut and then attributes the change to the wrong PR.
Evidence it happens
Four in one day (2026-08-28), all caught by eye during review rather than by any gate:
| PR |
shipped as |
should have been |
| #8944 |
0000-inline-array-pop-tier.md |
8944- |
| #8947 |
0000-inline-method-shape-probe.md |
8947- |
| #8291 |
0000-node-version-26.5.1.md |
8291- |
| #8977 |
8976-write-stub-two-way.md |
8977- |
The first three sat on main until #8973 renamed them; the fourth misattributed a write-stub change to #8976, an unrelated Array-subclass loop-guard fix that had merged an hour earlier.
Why the obvious fix is wrong
"The fragment must start with this PR's number" would be a false positive on legitimate cases:
So the check needs to be "the number is this PR's, or an existing PR number", or to warn rather than fail. The 0000 case specifically is unambiguous though: no PR is ever number 0, so rejecting ^changelog\.d/0+- is a safe tightening on its own and would have caught three of the four above.
Suggested minimum
Reject 0000- (and any all-zero prefix) outright; treat a number that is neither this PR's nor an existing PR as a warning. That closes the common case without breaking cleanups.
Found while auditing the PR queue; not fixed here because a naive tightening would have blocked #8973.
The gap
The changeset gate in
lintaccepts any fragment matching:That requires some digits, not this PR's number. So both of these pass:
changelog.d/0000-<slug>.md— the placeholder, when an author has not filled in the number yetchangelog.d/<other-PR>-<slug>.md— a number belonging to a different PRchangelog.d/README.mdsays filenames are PR-keyed precisely so in-flight PRs cannot collide, and the failure is silent: fragments are only folded into release notes at tag time byscripts/cut_release_notes.sh, so a wrong number is invisible until a release is cut and then attributes the change to the wrong PR.Evidence it happens
Four in one day (2026-08-28), all caught by eye during review rather than by any gate:
0000-inline-array-pop-tier.md8944-0000-inline-method-shape-probe.md8947-0000-node-version-26.5.1.md8291-8976-write-stub-two-way.md8977-The first three sat on
mainuntil #8973 renamed them; the fourth misattributed a write-stub change to #8976, an unrelated Array-subclass loop-guard fix that had merged an hour earlier.Why the obvious fix is wrong
"The fragment must start with this PR's number" would be a false positive on legitimate cases:
8944-,8947-and8291-— none of them its own number. A strict rule blocks exactly the PR that repairs the problem.So the check needs to be "the number is this PR's, or an existing PR number", or to warn rather than fail. The
0000case specifically is unambiguous though: no PR is ever number 0, so rejecting^changelog\.d/0+-is a safe tightening on its own and would have caught three of the four above.Suggested minimum
Reject
0000-(and any all-zero prefix) outright; treat a number that is neither this PR's nor an existing PR as a warning. That closes the common case without breaking cleanups.Found while auditing the PR queue; not fixed here because a naive tightening would have blocked #8973.