Skip to content

fix(scripts): the foreign-changeset refusal prescribes restoring a release note the same PR made false — name the second class (ruling D on #17712) #18160

Description

@claude

The foreign-changeset gate landed on main at 0ffb4963e (PR #18146, card #17712) and is enforcing now. Its refusal is correct to fire; its remedy sentence is wrong for a measured second class, and every hit from here on misroutes that author.

Ruling — D, maintainer, 2026-09-14, verbatim

D — 只改补救语(推荐)
门禁强度一分不减,只把拒绝时那句话改对:点名「你的改动可能让这条待发布说明变成假的」,并指向已有的人工确认路径。不新增标签、不放宽接受集,一次 revert 可回退。四棱同向 D。

Recorded on #17712 (the anchor) together with the A/B/C/D analysis it answers. ⛔ Not re-adjudicable here: B (an exemption label) and C (changeset-only diffs pass) were both refused.

The defect

scanForeign() in scripts/check-empty-changeset.mjs refuses every foreign M/D with exactly one remedy:

Remedy: rename yours; restore theirs from base.

That is right for the collision the gate was built for (two PRs racing on a random changeset name). It is wrong for a PR that changed behaviour a pending release note describes and corrected that note in the same stroke — following it restores a note the same PR has just made false.

The measurement that produced the ruling

30-day window on origin/main, --first-parent, provably complete (node scripts/pm/git-history.mjs log --days=30 --path=.changeset → exit 0; floor 2026-08-08). Population 429 changeset-touching commits; control --diff-filter=A = 431. Each candidate replayed through the merged gate in a detached worktree at the historical commit with --base <commit>^:

population n verdict
non-Version-Packages commits with a foreign M/D 9 exit 1 — all nine
…carried skip-changeset (whole job exempt) 4 exempt in practice
…added their own changeset ⇒ cannot take the exemption 5 hard red, wrong remedy
control: changeset-adding commits, no foreign row 3 exit 0 — control fires

The five: #18011 · #17845 · #17709 · #16750 · #16636.

Load-bearing instance — ed7243d52 lands boolean support for sum/avg/min/max and rewrites .changeset/aggregate-field-type-compatibility.md, which had stated booleans were refused. Under the gate it is refused and told to restore the false note.

Scope — text only

  • ✅ The refusal body and the ::error file=… annotation in scanForeign().
  • No logic change. --diff-filter=MD, --no-renames, the merge-base derivation, the changeset-release/main exemption and the job-level skip-changeset exemption all stay exactly as they are.
  • ⛔ No new label (B refused). ⛔ No diff-shape allowance (C refused). ⛔ No accept-set or public-surface movement ⇒ Clause-②: no.

Acceptance

  1. The refusal names both classes: the collision (rename yours; restore theirs from base) and the deliberate correction (your change may have made this pending note false), and routes the second to the confirmation path the report already describes, rather than prescribing the restore.
  2. The ::error annotation carries the same two-class text — ⛔ the annotation and the body must not drift, and a self-test case must pin them equal.
  3. ed7243d52's real diff is a fixture: the gate still exits 1 on it (strength unchanged — ⛔ this card does not make it pass) and the text it prints names the second class. Drive it from a real commit in a throwaway worktree; the gate reads commits, not the working tree.
  4. node scripts/check-empty-changeset.mjs --self-test stays green, and the new case is registered in SELF_TEST_BATTERIES with the roster size floor moved — a battery that never ran must stay distinguishable from one that passed.
  5. ⛔ No net change to the number of PRs the gate refuses. State that in the PR body with a reading, not an assertion.

Dedup words

foreign changeset · check-empty-changeset · scanForeign remedy · restore theirs from base · release note made false


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions