From b63a53bb94b8ce77c8e0378b5a13a303fec495cf Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 19 Aug 2026 03:10:58 +0000 Subject: [PATCH] docs(ci): state the launch-window major guard's arming CONDITION, not today's phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two comments described the guard as standing down "today" because `.changeset/pre.json` said `"mode": "pre"`. Measured on this branch, that premise is dead: `pre.json` is absent since the 17.0.0 GA (`2c2afa544` ran `changeset pre exit`, `24c1b91e4` removed the file), `readPre` collapses absent to `null`, and `judge` exempts only on `pre?.mode === 'pre'` — so the real scan reaches `enforce`. Verified by running it, not by reading it: a temporary changeset declaring `"@objectstack/spec": major` made `node scripts/check-changeset-no-major.mjs --base origin/main` exit 1 with the offenders report, and the probe commit was then rolled back. Neither comment is re-dated, because a dated reading goes stale again at the next `changeset pre enter`. Both now state the condition: - `pr-automation.yml`'s allow-major read says whether the guard can fail is a function of the release phase, that it flips with no edit there, and that a phase in which the guard cannot fail must never justify weakening the live label read — the exemption ends on the day that read matters most. - `check-changeset-no-major.mjs`'s `--self-test` coverage note is re-derived rather than re-worded. Its claim that the `enforce` verdict "has never been produced by a CI invocation" understated the gate in the direction that flatters it. The replacement distinguishes the file's TWO green ticks — `clean` is decided before pre-mode is consulted and means what it says in every phase, `exempt` names the window in its own text — and scopes the phase-dependence to the one thing it governs: whether the real scan can reach `enforce` at all. Comment-only: `git diff` contains no non-comment line. The fixture side of this was already made phase-independent (#8654); only the prose was left. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja --- .github/workflows/pr-automation.yml | 24 ++++++--- scripts/check-changeset-no-major.mjs | 73 ++++++++++++++++++++-------- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/.github/workflows/pr-automation.yml b/.github/workflows/pr-automation.yml index 9f5d64c392..437e297e94 100644 --- a/.github/workflows/pr-automation.yml +++ b/.github/workflows/pr-automation.yml @@ -780,14 +780,22 @@ jobs: # `rerun_failed_jobs` after labelling converges to green. Removing the # permanent red is the whole of what #5620 records. # - # DORMANT TODAY, and the dormancy is deliberately not load-bearing. - # `check-changeset-no-major.mjs` stands aside for the whole pre-release - # window (its RC EXEMPTION note) and `.changeset/pre.json` currently says - # `"mode": "pre"`, so the guard below cannot fail and the label is never - # needed. It re-arms by itself at `changeset pre exit` -- precisely the day - # whole-stack majors are under discussion and `allow-major` is most likely to - # be applied by hand, seconds after `gh pr create`. Nothing here leans on the - # dormancy, and it must not become a reason to weaken the read. + # WHETHER THE GUARD BELOW CAN FAIL AT ALL IS PHASE-DEPENDENT, and nothing + # in this read may lean on the phase either way. `check-changeset-no-major.mjs` + # stands aside for a diff that introduces a `major` for exactly as long as + # `.changeset/pre.json` parses with `"mode": "pre"` (its RC EXEMPTION note); + # in every other state -- absent, `"exit"`, unreadable -- it enforces, and + # the label read below is the only way past it. That flips with no edit + # here: `changeset pre enter` stands the guard down, `changeset pre exit` + # re-arms it. Read the phase from `.changeset/pre.json` or from the guard + # step's own output, never from this comment -- a comment that named + # today's phase is what this paragraph replaces. + # + # The re-arming edge is the one this read is built for: it is precisely the + # day whole-stack majors are under discussion and `allow-major` is most + # likely to be applied by hand, seconds after `gh pr create`. So a phase in + # which the guard cannot fail must never become a reason to weaken the read + # -- the exemption ends on the day the read matters most. - name: Re-read this PR's allow-major label live (the event payload can predate it) id: allow_major # Both skip-changeset reads, for the same reason the guard below names diff --git a/scripts/check-changeset-no-major.mjs b/scripts/check-changeset-no-major.mjs index f0608399dc..67019f6978 100644 --- a/scripts/check-changeset-no-major.mjs +++ b/scripts/check-changeset-no-major.mjs @@ -181,25 +181,60 @@ * rather than 0 (#4690): a gate that cannot read its input has verified nothing, * and exiting 0 there reads as "no violations" in every checks list. * - * ## What `--self-test` covers, and what it does NOT (#6923, still true) - * - * Read this before trusting a green tick from this file. The enforcing half it - * fixtures is still, on CI, unexecuted — and #7005 did not change that. - * - * COVERED — every decision this file makes: the frontmatter dialects, the - * pre-mode/exit-mode switch in BOTH directions, the diff scoping driven - * through real temp git repositories (including a real `refs/pull/N/merge` - * shape with a base branch that keeps moving), and the rendered text of the - * offenders report. - * - * NOT COVERED — the CI path. `.changeset/pre.json` says `"mode": "pre"`, so - * the real scan below still takes the exemption branch and exits 0 on every - * run; the `enforce` verdict has never been produced by a CI invocation of - * this script and still is not after #7005. What #6923 changed is that it is - * produced by fixtures on every PR, in a job with no label exemption - * (`check:changeset-gate-self-tests`, lint.yml's ESLint job — #6509/PR #6917). - * Fixtured is not the same as executed, and this note exists so the next - * reader does not read one as the other. + * ## What `--self-test` covers, and what a green tick from this file means (#6923) + * + * Read this before trusting a green tick. Both halves below are stated as a + * condition rather than as a reading of the release phase the repo happens to be + * in — the wording they replace was the latter, and `changeset pre exit` + * falsified it without touching a line of code (see RE-DERIVED at the end). + * + * COVERED, IN EVERY REPO PHASE — every decision this file makes: the + * frontmatter dialects, the pre-mode/exit-mode switch in BOTH directions, the + * diff scoping driven through real temp git repositories (including a real + * `refs/pull/N/merge` shape with a base branch that keeps moving), and the + * rendered text of the offenders report. None of it can move with the release + * train: the fixtures pass `judge()` its `pre` argument directly and build + * their own throwaway repos, and the assertions that did read the real + * `.changeset/` for its PHASE were removed at #8654 (see the reader block in + * `--self-test`). They run in a job with no label exemption — + * `check:changeset-gate-self-tests`, lint.yml's ESLint job (#6509/PR #6917) — + * so they execute on every PR. + * + * WHICH GREEN TICK — this file has two, they rule out different things, and + * the text is what distinguishes them: + * + * "introduces no `major` bump" the `clean` verdict. Decided BEFORE + * pre-mode is consulted — the verdict + * ORDER in `judge` is contract, and pinned + * by `--self-test` — so it means what it + * says in every phase. + * + * "in pre-release mode (tag: …)" the `exempt` verdict. A `major` WAS + * introduced and stood aside for the + * window. Reachable ONLY while + * `.changeset/pre.json` parses with + * `"mode": "pre"`. + * + * So the question a reader must answer is not "green or red" but WHICH green. + * What the phase governs is only whether the real scan can reach `enforce` at + * all: never inside a pre-release window, always outside one — absent, `"exit"` + * and unreadable all enforce. `--list` prints the mode it read; an ordinary run + * does not, which is exactly why the two ticks are worded apart. + * + * Fixtured is still not the same as executed, and keeping those two apart is + * what this note is for. The enforcing branch is reached by the real scan only + * on a PR that introduces a `major` outside a pre-release window — rare by + * construction, since the guard exists to make that PR rare — so `--self-test` + * remains the only thing that exercises it on a routine basis. + * + * RE-DERIVED, not re-worded. This paragraph used to read "`.changeset/pre.json` + * says `"mode": "pre"`, so the real scan below still takes the exemption branch + * and exits 0 on every run; the `enforce` verdict has never been produced by a + * CI invocation of this script". That was a measurement of the RC window the + * repo was in, not a property of this file, and it understated the gate in the + * direction that flatters it. It is replaced by the condition above rather than + * re-dated, because a dated reading of this paragraph goes stale again at the + * next `changeset pre enter`. * * ## The frontmatter dialects, measured against the real parser *