You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
check-changeset-no-major.mjs judges the whole .changeset stock, not the PR's diff — at changeset pre exit it reds every unrelated PR (171 files measured) #7005
Measured while adding --self-test fixtures to that script (#6923). Not fixed there — #6923's dispatch explicitly ruled that changing what the enforcing half does, or the release mode it keys off, is a triage decision and not a fix to improvise inside a fixtures card. Filed per Prime Directive #10. Unclaimed.
The observation
check-changeset-no-major.mjs reads the entire .changeset directory (readdirSync, no branch point) and fails if any pending changeset declares major. Its sibling check-empty-changeset.mjs is diff-scoped by design — it judges only what the PR introduces, computed from merge-base(base, head), and the whole #6129 note in that file argues why. This one is stock-scoped, so its verdict is a function of what main carries, not of what the author wrote.
That is invisible today, because the guard stands aside for the whole pre-release window. It stops being invisible at changeset pre exit.
The measurement
On origin/main @ d3e53f2d8:
total .changeset/*.md (excl README): 1552
offender FILES declaring major: 171
total major package entries: 222
pre.mode: pre | pre.tag: rc | pre.changesets recorded: 1279
The 171 are still on disk because pre-mode changeset version does not delete consumed changesets — it records them in pre.json.changesets (1279 of them) so the final release can re-apply them. They are deleted only by the post-exitchangeset version.
So the sequence is:
changeset pre exit rewrites pre.json to "mode": "exit" (@changesets/pre@2.0.2, changesets-pre.cjs.js:117) and that commit lands on main.
From that moment, every PR's pull_request merge ref carries mode: exitand the 171 major-declaring changesets.
pr-automation.yml's Guard against accidental major bumps (launch window) step runs node scripts/check-changeset-no-major.mjs on every PR, exempted only by skip-changeset or allow-major.
Every unlabelled PR therefore goes red, listing 171 files it never touched, and the only route out is allow-major — a label whose stated meaning is "a whole-stack major is genuinely intended here", which is false for a PR fixing a typo.
The window closes when the Version PR merges and deletes them.
The Changesets release PR itself is fine: changeset-check exempts changeset-release/main at the job level. The exposure is ordinary PRs open across that window.
#6129 is "judge the author for what main gained while their PR was open". check-empty-changeset.mjs was fixed for exactly that and carries a long note about it. check-changeset-no-major.mjs has the same property by a different route — not a frozen base ref, but no base ref at all — and it has never been noticed because the guard has been dormant for the whole pre-release window.
Note the direction is not obviously wrong in every case: at pre exit, a whole-stack major is intended, and the guard's escape hatch exists for that. What is wrong is that the cost falls on authors of unrelated PRs rather than on the release, and that allow-major then means something different from what its own error message says.
Impact, stated honestly
No known instance, and none possible today — the guard exits 0 on every run while pre-mode holds.
It is not dormant drift either: it is a scheduled event. The imminent v17 window is the trigger, and it is the first time this half has ever executed.
Bounded in time (until the Version PR merges), unbounded in breadth (every open PR in that window).
Candidate dispositions (no conclusion presumed)
Scope it to the diff, like its sibling: judge only changesets the PR added or modified against merge-base. Keeps the guard's purpose (an author must not introduce a major) and removes the collateral entirely. Costs the script a git dependency it currently does not have.
Sequence it in the release runbook: run changeset pre exit and the final changeset version close enough together that no PR observes the window. Cheapest, but it is a convention protecting against a gate, held by hand, on the busiest release day.
Accept it and document the allow-major-for-everyone window in the exit checklist.
Leaning 1/2 — they are the same fix and the sibling already carries the merge-base machinery and the argument for it. But this decides what the guard means (does it police the author, or the tree?), which is a contract call rather than a code call, so it is left to triage.
Related: #6923 (the fixtures card that measured this), #6509 / PR #6917 (the family's wiring), #6129 (the direction).
Measured while adding
--self-testfixtures to that script (#6923). Not fixed there — #6923's dispatch explicitly ruled that changing what the enforcing half does, or the release mode it keys off, is a triage decision and not a fix to improvise inside a fixtures card. Filed per Prime Directive #10. Unclaimed.The observation
check-changeset-no-major.mjsreads the entire.changesetdirectory (readdirSync, no branch point) and fails if any pending changeset declaresmajor. Its siblingcheck-empty-changeset.mjsis diff-scoped by design — it judges only what the PR introduces, computed frommerge-base(base, head), and the whole #6129 note in that file argues why. This one is stock-scoped, so its verdict is a function of what main carries, not of what the author wrote.That is invisible today, because the guard stands aside for the whole pre-release window. It stops being invisible at
changeset pre exit.The measurement
On
origin/main@d3e53f2d8:The 171 are still on disk because pre-mode
changeset versiondoes not delete consumed changesets — it records them inpre.json.changesets(1279 of them) so the final release can re-apply them. They are deleted only by the post-exitchangeset version.So the sequence is:
changeset pre exitrewritespre.jsonto"mode": "exit"(@changesets/pre@2.0.2,changesets-pre.cjs.js:117) and that commit lands on main.pull_requestmerge ref carriesmode: exitand the 171 major-declaring changesets.pr-automation.yml'sGuard against accidental major bumps (launch window)step runsnode scripts/check-changeset-no-major.mjson every PR, exempted only byskip-changesetorallow-major.allow-major— a label whose stated meaning is "a whole-stack major is genuinely intended here", which is false for a PR fixing a typo.The Changesets release PR itself is fine:
changeset-checkexemptschangeset-release/mainat the job level. The exposure is ordinary PRs open across that window.Why this is #6129 in this script's own shape
#6129 is "judge the author for what main gained while their PR was open".
check-empty-changeset.mjswas fixed for exactly that and carries a long note about it.check-changeset-no-major.mjshas the same property by a different route — not a frozen base ref, but no base ref at all — and it has never been noticed because the guard has been dormant for the whole pre-release window.Note the direction is not obviously wrong in every case: at
pre exit, a whole-stack major is intended, and the guard's escape hatch exists for that. What is wrong is that the cost falls on authors of unrelated PRs rather than on the release, and thatallow-majorthen means something different from what its own error message says.Impact, stated honestly
Candidate dispositions (no conclusion presumed)
merge-base. Keeps the guard's purpose (an author must not introduce a major) and removes the collateral entirely. Costs the script a git dependency it currently does not have.skip-changeset标签零收益、单向风险 —— 「禁止空 changeset 进 .changeset/」的决策证据(#5292 结案后无处存放) #5471 used to exempt the stock without a hardcoded roster.changeset pre exitand the finalchangeset versionclose enough together that no PR observes the window. Cheapest, but it is a convention protecting against a gate, held by hand, on the busiest release day.allow-major-for-everyone window in the exit checklist.Leaning 1/2 — they are the same fix and the sibling already carries the merge-base machinery and the argument for it. But this decides what the guard means (does it police the author, or the tree?), which is a contract call rather than a code call, so it is left to triage.
Related: #6923 (the fixtures card that measured this), #6509 / PR #6917 (the family's wiring), #6129 (the direction).