Skip to content

check-changeset-no-major can read its base side off .changeset/README.md on an R row (dormant) #7107

Description

@os-project-manager

Found while implementing #7045 (PR #7106), which applies PR #7048's AMR pattern to the two sibling gates. Filed per Prime Directive #10, not fixed there — #7106 owns the two siblings, this is the third member's own file. Unclaimed.

The observation

scripts/check-changeset-no-major.mjs reads the branch-point side of an R row at the pre-rename path, correctly:

const basePath = fields[1];
...
const baseText = status === 'A' ? null : showOrNull(from, basePath, cwd);
const already = baseText === null ? [] : majorPackagesIn(baseText);
const added = majors.filter((pkg) => !already.includes(pkg));

basePath is never checked against isChangesetFile(). Git's rename detection pairs by content, not by name, and the pathspec is .changeset/*.md — which .changeset/README.md matches. So an R row can arrive as

R100    .changeset/README.md    .changeset/anything.md

Measured on git 2.43.0, in a temp repo, with the same pathspec the gate uses:

--- name-status, pathspec .changeset/*.md ---
R100    .changeset/README.md    .changeset/renamed-from-docs.md
A       .changeset/renamed-from-readme.md

(two identically-bodied files, paired across unrelated names.)

When that happens, already is computed from README's content. isChangesetFile() excludes README at the HEAD side — the file's own self-test has a case pinning that a major-shaped .changeset/README.md is documentation, never a changeset — but the exclusion does not reach the BASE side of a rename. A major that README appears to declare would be subtracted from the head file's majors and the head changeset would be reported as exempt rather than introduced.

Why this is filed as an observation, not a defect

Dormant. It needs .changeset/README.md to itself parse as declaring a major, and the real file is # Changesets boilerplate with no frontmatter fence at all, so majorPackagesIn() returns [], already is empty and nothing is subtracted. Nobody hits this today, and there is no way to hit it without first committing a major-shaped README — which the gate's own fixtures already treat as documentation.

It is recorded because the shape is real and because the two siblings no longer have it: PR #7106 added isChangesetFile(basePath) to both check-empty-changeset.mjs and check-adr-0087-registration.mjs, each with a fixture (RED 5 / R16). The family now reads one row three ways, which is the drift #7004 exists to prevent.

The fix, if it is ever wanted

One condition and one fixture, mirroring #7106:

const baseText = status === 'A' || !isChangesetFile(basePath) ? null : showOrNull(from, basePath, cwd);

plus a self-test case whose base side is .changeset/README.md, carrying the same rename control the other R cases in that file already carry (a body long enough that git scores R rather than degrading to add-plus-delete).

Related: #7045 / PR #7106 (the two siblings), #7005 / PR #7048 (this file's AMR change), #7004 (the family's shared entry regex).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions