fix(ci): let the two changeset gates see a RENAMED changeset (#7045) - #7106
Merged
os-project-manager merged 1 commit intoAug 9, 2026
Merged
Conversation
`check-empty-changeset.mjs` and `check-adr-0087-registration.mjs` both enumerated the diff with `--diff-filter=AM`. Git's rename detection is on by default, so a changeset renamed and modified in one commit reports as `R` and both filters dropped the row entirely: an emptied changeset, or a newly declared-breaking one with no ADR-0087 disposition, walked past its gate unseen. `--diff-filter=AMR`, reading the branch-point side at the PRE-RENAME path (field 2 of an `R` row, not field 3) — the pattern `check-changeset-no-major.mjs` landed first in PR #7048 for the third member of the family. A pure rename compares equal and stays exempt, so moving a stock changeset still costs nothing. One thing the reference did not need: the base-side read is guarded by `isChangesetFile(basePath)`. Git pairs renames by CONTENT, so an `R` row can arrive as `.changeset/README.md -> .changeset/x.md`, and README declares nothing by definition — inheriting an exemption from it would have been free. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-project-manager
marked this pull request as ready for review
August 9, 2026 17:09
os-project-manager
deleted the
claude/issue-7045-diff-filter-amr-siblings
branch
August 9, 2026 17:20
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #7045
scripts/check-empty-changeset.mjsandscripts/check-adr-0087-registration.mjsboth enumerated the diff with
--diff-filter=AM. Git's rename detection is on bydefault (
diff.renames, since git 2.9), so a changeset renamed and modified inone commit reports as
R, andAMdropped the row entirely — neither gate saw thefile at all.
check-changeset-no-major.mjsclosed the same hole in the third member of thisfamily in PR #7048 (#7005). This is that pattern applied to the two siblings, with
each gate keeping its own fixtures and its own message text.
The bypass, end to end
One temp repo, one commit that renames a declaring changeset while emptying its
frontmatter. Same repo, same commit, two versions of the same script:
What changed
--diff-filter=AMRin bothscan()s.Rrow isR< score >TAB old-path TAB new-path, so the head path is field3 and the branch-point path is field 2. The base side is read at the
pre-rename name; the status is compared one character wide, because the
Rletter carries a similarity score.
path. Moving a stock changeset is not a violation and never becomes one.
One thing the reference implementation did not need
The base-side read is guarded by
isChangesetFile(basePath). Git pairs renames bycontent, not by name, so an
Rrow can legitimately arrive as.changeset/README.md -> .changeset/anything.md— measured on git 2.43.0, where twoidentically-bodied files paired across unrelated names and the pathspec kept the
pairing inside
.changeset/. README is documentation that declares nothing bydefinition, so reading "already empty at base" / "already breaking at base" off it
would have handed the exemption out for free on a head file that really is a
brand-new changeset. Both gates now refuse to inherit anything from a non-changeset
base path; each carries a fixture for it (RED 5 / R16).
What deliberately did NOT change
.github/workflows/pr-automation.ymlkeeps--diff-filter=Afor its changesetcount, and
check-empty-changeset.mjs's consumer block keeps asserting exactlythat spelling. That
Ais route detection — "did this PR write a changeset, or doesit owe a
skip-changesetlabel" — not a violation scan. A rename adds no changeset,so counting
Rthere would hand the label exemption to a PR that wrote nothing,which is the loosening direction.
Afails closed for that count;AMRfails closedfor the scans. Same family, opposite obligations — now written down next to the
assertion so the next reader does not "fix" it to match.
Fixtures, and the control that keeps them honest
Rename detection is a similarity score: too short a body and git degrades the
move to add-plus-delete, the
Rpath is never exercised, and the case passes greenwhile testing nothing. Every new case therefore asserts the real
Rrow first, byregex against raw
git diff --name-statusoutput, before it asserts any verdict.check-empty-changeset.mjs— 91 → 105 assertions:at the head path,
kind: 'renamed-empty', carrying the branch-point path.This one can only be green through the
Rpath: had the rename degraded, the newpath would arrive as
A+ empty, which is RED 1.ok.Rrow whose base side isREADME.mdinherits nothing.check-adr-0087-registration.mjs— 130 → 142 assertions:skippedat its new path rather than silently absent.Rrow whose base side isREADME.mdis still judged.The self-test's
build()helper gainedbaseFilesandnull-means-delete infiles, because expressing a rename needs both halves of the pair.Reverse verification
Direction predicted before running, three ablations per gate, all red as predicted:
check-empty-changesetcheck-adr-0087-registrationAMR→AMisChangesetFile(basePath)Worth noting which case pins which claim. Reverting
AMRdoes not break the"read field 2" claim — under
AMthere is noRrow to misread. What pins field 2is the pure-rename case in each gate (GREEN 6 / G10): read the base at the head
path and it resolves to nothing, so a legitimate move is reported as a brand-new
violation. The RED cases alone would have stayed green through that mistake.
Sample output,
AMR→AMon the ADR gate:Verification
pnpm check:type-check-debtwas not run locally: it refuses to measure without thewhole package closure built, and this diff contains no TypeScript at all — two
.mjsfiles underscripts/. The CI job is the check for it.No changeset
scripts/belongs to the private root workspace (@objectstack/spec-monorepo,"private": true) and ships to no registry, so this PR releases nothing and takesskip-changeset— the same route PR #7048 took for the identical file class.Generated by Claude Code