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
fix(ci): run the changeset family's --self-test in lint.yml, out of reach of skip-changeset (#6509) (#6917)
* fix(ci): run the changeset family's --self-test in lint.yml, out of reach of skip-changeset (#6509)
`check-empty-changeset.mjs` and `check-adr-0087-registration.mjs` were called
from exactly one place in the repository: pr-automation.yml's `changeset-check`
job, which a PR carrying `skip-changeset` is exempted from wholesale. A PR that
edits a CI-internal script is the textbook case for that label, so a PR editing
these two checkers was routinely the PR that skipped their own fixtures.
The self-test halves are pure static checks with no merge-base dependency, so
they move to lint.yml's ESLint job as one unconditional step. The real scans
stay in `changeset-check`, which is where $MERGE_BASE exists — running them in
a job with no branch point would be #6129 in the false-RED direction. The
`chunks.length === 5` job invariant is untouched.
`check-empty-changeset.mjs`'s consumer block gains 11 assertions pinning the new
wiring: the step exists exactly once, carries no `if:`, lint.yml reads no label
and carries no paths filter, the gates are invoked only through the self-test-only
pnpm script, and that script covers both checkers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn
* TEMP ABLATION (reverted in the next commit) — reverse-verification for #6509
Weakens the live allow-major matcher in pr-automation.yml from `grep -qxF` to
`grep -qF`, i.e. reintroduces exactly the #5620-class defect that PR #6876's
consumer assertions were added to catch — and which never executed once on
#6876's own CI, because that PR carried skip-changeset.
This PR carries skip-changeset too. Predicted direction: `Check Changeset`
skips its self-test steps again, and the NEW lint.yml step goes RED.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn
* Revert the temporary ablation — restore the whole-line allow-major matcher (#6509)
Restores `grep -qxF 'allow-major'` in pr-automation.yml, undoing 56f7018.
That commit existed only to prove on CI that the new lint.yml step goes RED on a
PR carrying skip-changeset; run 31294323300 is the evidence and this returns the
branch to the intended state. pr-automation.yml is now byte-identical to
origin/main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8q5J1MQyocgtNspb15fSn
---------
Co-authored-by: Claude <noreply@anthropic.com>
`consumer: the ESLint job of lint.yml must run \`pnpm check:changeset-gate-self-tests\` exactly once (found ${wiredSteps.length}) -- that step is the only place these two checkers' fixtures are executed on a PR carrying \`skip-changeset\` (#6509)`,
964
+
);
965
+
// The load-bearing half. A conditioned step is the defect again with one
966
+
// more hop: whatever the condition reads, it is a way for a PR to arrange
967
+
// that this self-test does not run on it.
968
+
assert(
969
+
wiredSteps.every((s)=>!/^\s*if:/m.test(s)),
970
+
'consumer: the changeset-family self-test step in lint.yml must carry NO `if:` -- an exemptable self-test is #6509 itself, and the exemption it must not have is the one that hid PR #6876\'s five assertions',
971
+
);
972
+
// Same property, one level up: a label read anywhere in this workflow
973
+
// would mean some step of it can be waived by the author of the PR under
974
+
// test.
975
+
assert(
976
+
!/skip-changeset/.test(uncommented(lintYaml)),
977
+
'consumer: lint.yml must not read the `skip-changeset` label anywhere -- the whole point of running the self-tests here is that this workflow has no PR-level exemption',
978
+
);
979
+
// And one level up again: "runs on every PR" is what "unconditional"
980
+
// means in practice. A `paths:` filter is a condition written in the
981
+
// trigger instead of in an `if:`, and it would silently restore the gap
'consumer: lint.yml must keep its `pull_request:` trigger -- a self-test that does not run on pull requests is not wired at all (#6509)',
987
+
);
988
+
assert(
989
+
!/\bpaths(-ignore)?\s*:/.test(onBlock),
990
+
'consumer: lint.yml must carry no `paths:`/`paths-ignore:` filter -- a path-filtered trigger is an exemption written one level up, and this self-test may not have one (#6509)',
991
+
);
992
+
993
+
// The other half of the split: the merge-base-dependent scans stay out of
994
+
// this job. `check:empty-changeset` / `check:adr-0087-registration` chain
995
+
// the REAL scan, whose verdict is a function of the PR's diff; this job
996
+
// has no branch point, so running one here reads stock and reports main's
997
+
// drift against the author -- #6129 in the false-RED direction.
`consumer: lint.yml must invoke these gates ONLY through \`pnpm check:changeset-gate-self-tests\` (found ${strayScans.length} direct invocation(s)) -- the real scans need $MERGE_BASE and this job has no branch point, which is #6129 in the false-RED direction`,
1004
+
);
1005
+
1006
+
// What that pnpm script actually is. The step above is a name; this is the
1007
+
// thing the name resolves to, and it is where "self-test halves only" and
1008
+
// "BOTH of them" are actually true or false.
1009
+
constpkgPath=join(REPO_ROOT,'package.json');
1010
+
constpkgPresent=existsSync(pkgPath);
1011
+
assert(pkgPresent,'consumer: the repository root package.json must exist -- it carries the script lint.yml runs');
'consumer: `check:changeset-gate-self-tests` must run `check-empty-changeset.mjs --self-test` -- the step in lint.yml is only as real as the script it resolves to',
'consumer: `check:changeset-gate-self-tests` must run `check-adr-0087-registration.mjs --self-test` too -- both checkers live in the same exempted job and both were unwired by it (#6509). `check-changeset-no-major.mjs` is deliberately absent: it has no `--self-test` to run.',
'consumer: every invocation in `check:changeset-gate-self-tests` must carry `--self-test` -- chaining a real scan into the lint job is the #6129 direction this split exists to avoid',
1029
+
);
1030
+
}
1031
+
908
1032
// ── Parser unit rows ─────────────────────────────────────────────────────
909
1033
assert(isEmptyDeclaration('---\n---\n\nbody\n'),'parser: the canonical empty shape is empty');
910
1034
assert(isEmptyDeclaration('\n---\n\n---\n\nbody\n'),'parser: blank lines around/inside the fence stay empty');
0 commit comments