Found while diff-scoping check-changeset-no-major.mjs (#7005). Not fixed there — that PR closed the hole in its own file only, because each sibling's fixtures and messages are its own. Filed per Prime Directive #10. Unclaimed.
The observation
Both scripts/check-empty-changeset.mjs:229 and scripts/check-adr-0087-registration.mjs:1056 enumerate the diff with:
git diff --name-status --diff-filter=AM <from> <head> -- '.changeset/*.md'
Git's rename detection is on by default (diff.renames, since git 2.9), so a changeset that is renamed and modified in the same commit is reported with status R, which --diff-filter=AM drops. Neither gate sees the file at all.
Measured
git 2.43.0, temp repo, one changeset renamed and its frontmatter changed in one commit:
--- default (rename detection on) ---
R075 .changeset/old.md .changeset/new.md
--- with --diff-filter=AM ---
(empty)
Same fixture shape, run against check-changeset-no-major.mjs before its own fix: 0 violations where the correct answer is 1.
The consequence in each gate
Unlike #7005 this one is live today: both gates run on every PR right now, neither is behind a pre-mode exemption.
Impact, stated honestly
- No known instance. Changeset filenames are generated by
pnpm changeset and essentially never renamed by hand, so this is not something an author trips over by accident — it is a bypass that requires doing an unusual thing.
- It is not dormant drift either: the code path runs on every PR, and the bypass is one
git mv away for anyone who wants it.
- Both gates are advisory-shaped (they protect the release train, not a security boundary), which is the honest reason this is filed rather than hot-fixed.
The fix, already written once
#7005 / PR #7048 closes the same hole in the third member of the family, and the change is small enough to quote in full:
--diff-filter=AMR instead of AM;
R is R<score>\t<old path>\t<new path>, so the head path is field 3 and the branch-point path is field 2 — read the base side at the pre-rename name;
- everything else is unchanged, and a pure rename then compares equal and stays exempt (no false red for moving a stock file).
Both siblings already compare head-vs-base per file, so step 2 is the only real work. The fixture wants a control asserting git really reported R — a body long enough to score as a rename, otherwise the case silently degrades to add-plus-delete and the test passes for the wrong reason.
Related: #7005 (the same hole in the third parser, fixed), #7004 (the family's shared entry regex), #6129 (why all three are diff-scoped at all).
Found while diff-scoping
check-changeset-no-major.mjs(#7005). Not fixed there — that PR closed the hole in its own file only, because each sibling's fixtures and messages are its own. Filed per Prime Directive #10. Unclaimed.The observation
Both
scripts/check-empty-changeset.mjs:229andscripts/check-adr-0087-registration.mjs:1056enumerate the diff with:Git's rename detection is on by default (
diff.renames, since git 2.9), so a changeset that is renamed and modified in the same commit is reported with statusR, which--diff-filter=AMdrops. Neither gate sees the file at all.Measured
git 2.43.0, temp repo, one changeset renamed and its frontmatter changed in one commit:
Same fixture shape, run against
check-changeset-no-major.mjsbefore its own fix: 0 violations where the correct answer is 1.The consequence in each gate
check-empty-changeset.mjs— take a stock non-empty changeset, rename it and delete its frontmatter entries in the same commit. That is a brand-new empty declaration, which is the whole harm 空 frontmatter changeset 相对skip-changeset标签零收益、单向风险 —— 「禁止空 changeset 进 .changeset/」的决策证据(#5292 结案后无处存放) #5471 names (a real input tochangesets/action; an all-empty set stalls the release silently and greenly, 空 changeset 会静默卡死已 version 的发布:Release run 全绿,但 npm 和 Docker 什么都没发(17.0.0-rc.2 现在就卡着) #4898). Row 2 of that file's own table ("emptied in place") exists precisely to catch it underM, andRwalks around the row.check-adr-0087-registration.mjs— a declared-breaking changeset that arrives by rename carries no ADR-0087 disposition and is never asked for one ([finding] ADR-0087 台账没有「完备性」门禁:已发生的退役漏登记时全仓全绿,只有人工能发现(#6011 即如此) #6148).Unlike #7005 this one is live today: both gates run on every PR right now, neither is behind a pre-mode exemption.
Impact, stated honestly
pnpm changesetand essentially never renamed by hand, so this is not something an author trips over by accident — it is a bypass that requires doing an unusual thing.git mvaway for anyone who wants it.The fix, already written once
#7005 / PR #7048 closes the same hole in the third member of the family, and the change is small enough to quote in full:
--diff-filter=AMRinstead ofAM;RisR<score>\t<old path>\t<new path>, so the head path is field 3 and the branch-point path is field 2 — read the base side at the pre-rename name;Both siblings already compare head-vs-base per file, so step 2 is the only real work. The fixture wants a control asserting git really reported
R— a body long enough to score as a rename, otherwise the case silently degrades to add-plus-delete and the test passes for the wrong reason.Related: #7005 (the same hole in the third parser, fixed), #7004 (the family's shared entry regex), #6129 (why all three are diff-scoped at all).