Filed unassigned from the #8370 dev seat (PR #8657), observation-with-measurement. This is the sibling gate of #8654, one script over: same root condition, a different file, and a fix to #8654's subject would not touch it.
The measurement
node scripts/check-adr-0087-registration.mjs exits 1 without reporting any verdict, both on pristine origin/main and on a PR branch:
| ref | .changeset stock | result |
|---|
origin/main56724c500 (right after the v17 cut) | README only | exit 1 — no changesets found at HEAD |
this seat's branch on 56724c500 + 1 non-breaking changeset | 1 | exit 1 — not one of 1 changeset(s) in stock matches the breaking-change detector |
same branch merged with origin/main189a73225 (3 changesets, none breaking) | 3 | exit 1 — not one of 3 changeset(s) in stock matches the breaking-change detector |
The last row is the one that matters: the stock refilled and the gate stayed red. It does not self-heal the way #8654's empty-directory half did.
Mechanism
assertInputs() (the #4690 "absence is never a pass" block) asserts two properties of the real repo, and each is false in the post-cut window:
- (1) the subject matter exists at all — zero changesets at HEAD is an input problem. True only in the narrow window right after
version packages lands, self-heals. - (4) CONVENTION ROT —
if (stock.length > 0 && breakingInStock === 0), guarding against major / **BREAKING / feat!: being reworded wholesale. Post-cut the stock rebuilds out of ordinary patch and minor changesets, so breakingInStock stays 0 for as long as nobody happens to land a breaking one. Every PR whose CI runs in that window is red, on a gate that has nothing to say about that PR.
Both problems are raised as ✗ ... refusing to report a verdict and exit 1, by design ("a gate that cannot find its input and exits 0 is worse than no gate").
Why this is not #8654
#8654 is check-changeset-no-major --self-test — its #7005 positive control plus the pre.json reader, in scripts/check-changeset-no-major.mjs, run by check:changeset-gate-self-tests in lint.yml. This one is scripts/check-adr-0087-registration.mjs's own assertInputs, run by the Require an ADR-0087 disposition on a declared-breaking changeset step in pr-automation.yml and by cut-rc.yml. Different file, different job, no shared code path — only the shared root condition (a release consumes the stock's breaking changesets).
The two are worth deciding together, since the tension is identical and #8654 states it well: an assertion over the real stock catches a synthetic-only green, and the same assertion asserts the repo is mid-cycle. Those are different guarantees, and here as there the code conflates them.
Not proposed, deliberately
No remedy is attempted. Editing a gate is a maintainer-floor change, and the obvious route (make assertion 4 conditional, or report it as a loud skip) re-opens exactly the #4690 hole the assertion exists to close. Whoever takes #8654 should decide whether this file moves with it or gets its own answer.
Related: #8654, #7005, #4690. Seen from: PR #8657 (#8370).
Generated by Claude Code
Filed unassigned from the #8370 dev seat (PR #8657), observation-with-measurement. This is the sibling gate of #8654, one script over: same root condition, a different file, and a fix to #8654's subject would not touch it.
The measurement
node scripts/check-adr-0087-registration.mjsexits 1 without reporting any verdict, both on pristineorigin/mainand on a PR branch:.changesetstockorigin/main56724c500(right after the v17 cut)no changesets found at HEAD56724c500+ 1 non-breaking changesetnot one of 1 changeset(s) in stock matches the breaking-change detectororigin/main189a73225(3 changesets, none breaking)not one of 3 changeset(s) in stock matches the breaking-change detectorThe last row is the one that matters: the stock refilled and the gate stayed red. It does not self-heal the way #8654's empty-directory half did.
Mechanism
assertInputs()(the #4690 "absence is never a pass" block) asserts two properties of the real repo, and each is false in the post-cut window:version packageslands, self-heals.if (stock.length > 0 && breakingInStock === 0), guarding againstmajor/**BREAKING/feat!:being reworded wholesale. Post-cut the stock rebuilds out of ordinary patch and minor changesets, sobreakingInStockstays 0 for as long as nobody happens to land a breaking one. Every PR whose CI runs in that window is red, on a gate that has nothing to say about that PR.Both problems are raised as
✗ ... refusing to report a verdictand exit 1, by design ("a gate that cannot find its input and exits 0 is worse than no gate").Why this is not #8654
#8654 is
check-changeset-no-major --self-test— its #7005 positive control plus thepre.jsonreader, inscripts/check-changeset-no-major.mjs, run bycheck:changeset-gate-self-testsinlint.yml. This one isscripts/check-adr-0087-registration.mjs's ownassertInputs, run by the Require an ADR-0087 disposition on a declared-breaking changeset step inpr-automation.ymland bycut-rc.yml. Different file, different job, no shared code path — only the shared root condition (a release consumes the stock's breaking changesets).The two are worth deciding together, since the tension is identical and #8654 states it well: an assertion over the real stock catches a synthetic-only green, and the same assertion asserts the repo is mid-cycle. Those are different guarantees, and here as there the code conflates them.
Not proposed, deliberately
No remedy is attempted. Editing a gate is a maintainer-floor change, and the obvious route (make assertion 4 conditional, or report it as a loud skip) re-opens exactly the #4690 hole the assertion exists to close. Whoever takes #8654 should decide whether this file moves with it or gets its own answer.
Related: #8654, #7005, #4690. Seen from: PR #8657 (#8370).
Generated by Claude Code