Repository navigation
Commit 5d0ee8f
fix(scripts): derive check-changeset-no-major's level headline from the parsed declaration (#19163)
Fixes #19008
Clause-②: no
## What was wrong, as measured (not as assumed)
`judgeLevel` reaches the `not-declared` lane on `value === 'no' && arm
!== 'narrowing'`. **Past that lane the verdict is decided by `refusable`
and `offenders.length` alone — `declaration.value` plays no further
part.** So a `Clause-②: no (narrowing)` declaration flows straight into
`enforce`, `discharged` and `clean`, and every one of those headlines
stated `yes` as a literal, while the `readings` rows two lines below
printed the true value and arm.
That made the arm docblock's own rule false in the one line a reader
quotes:
> a PR that declares `no (narrowing)` must not be reported as having
declared `yes`
### The enumeration (the card named one branch; the tree has four
literals in three lanes)
Every render branch reachable with `value === 'no' && arm ===
'narrowing'`, driven through the real `declarationFromPullRequest` /
`judgeLevel` / `renderLevel` at `8b8258d91`:
| reachable branch | channel | what it said before | verdict / exit |
|---|---|---|---|
| `clean` | stdout | "declares clause-② \`yes\`" 🔴 | `clean` / 0 |
| `discharged` | stdout | "declares clause-② \`yes\` … the declared
**widening** is accounted for" 🔴 (two claims, both false) | `discharged`
/ 0 |
| `enforce` | **stderr** | "This PR declares clause-② **YES**" 🔴 |
`enforce` / 1 |
| `enforce` | **check-run annotation title** | "clause-② declares
**YES** while no moved package is graded \`minor\`" 🔴 — ⭐ **a fourth
literal the card did not name, and the only one that crosses out of the
step log** | `enforce` / 1 |
| `unreadable-diff` | stderr | makes no claim about the declaration —
correct as written | `unreadable-diff` / 1 |
And the two lanes the dispatch left open, now measured rather than
assumed: **`not-measured-material` and `not-measured-moot` are NOT
reachable under a narrowing declaration.** Both sit behind
`declaration?.value === null || undefined`, and a narrowing declaration
has `value === 'no'`; the enumeration run confirms every `no
(narrowing)` row lands in `enforce` / `discharged` / `clean` /
`unreadable-diff` and never in a `not-measured-*`. `payload-unreadable`
and `no-pull-request` are likewise unreachable: `payload: false` is
written by one place, which returns `value: null` beside it.
## The fix
1. **The parsed declaration travels on the verdict** as `declared: {
value, arm }`, beside `carrier` and for the same stated reason:
`renderLevel` must not assert a declaration it did not read.
2. **One exported speller, `declaredClause2`, renders all five
headlines** — the four that lied plus `not-declared`, which was already
correct and stays byte-identical on every input that reaches it. ⛔ No
literal is left in a headline, so the rule above is now structural
rather than per-branch.
3. **The arm is carried through**, so the headline is *the declaration
as written* and a reader can grep the PR body for the string the gate
printed (`yes`, `yes (widening)`, `no`, `no (narrowing)`).
4. **The `discharged` sentence derives its noun too** — a declared
narrowing is no longer "accounted for" as a *widening*.
5. `value: null` spells `NOT MEASURED` rather than guessing. The three
carried lanes cannot be reached with a null value, so a result arriving
without one is a caller that dropped the axis, and #4690's direction is
that such a run says so.
6. The verdict table in the `judgeLevel` docblock stopped describing
`clean` / `discharged` / `enforce` as "declared \`yes\`" — that was the
same misstatement in the documentation half, in the same file.
⛔ **No `verdict` and no `exitCode` moves.** This is a reporting repair;
that is pinned, not asserted — see below.
## Both directions, pinned
New self-test battery `#19008: the level headline is the parsed
declaration, not a literal`, floor **30** cases (roster floor 19 → 20).
Three lanes × both directions, with the lane identity asserted first so
a headline pin cannot read a row it does not name:
- each lane reaches its own verdict on **both** a `yes` and a `no
(narrowing)` declaration;
- each lane's exit code is **unchanged in both directions** (the
grading-is-untouched pin);
- on a narrowing: the true declaration appears, and `clause-② \`yes\`` /
`clause-② YES` appears **nowhere** — stdout, stderr and the annotation
title alike;
- ⛔ **controls:** a bare `yes` still renders `yes` on all three lanes
(that is every other PR in this repo), and `yes (widening)` renders its
arm;
- the annotation title is pinned separately, still exactly one, still
one line;
- a bare `no` still stands the axis down at exit 0 on the very tree the
narrowing refuses, and still prints the `no` it always printed;
- `declaredClause2` unit rows, including both `NOT MEASURED` spellings.
### Reverse verification (ablation), at `4e3cbe10`
One-line mutation of `declaredClause2` back to the literal this card
removes, with the mutation proved on disk before the run and the restore
proved by blob hash:
```
HEAD blob = 4d72a0f
ON-DISK PROOF: removed-text count 1 -> 0 ; injected-text count 0 -> 1
mutated blob = aea6bdf92c0249d83e0714f7513f82322701e004 (differs from HEAD)
VERDICT ablation-exit=1 (12 failures)
restored blob = 4d72a0f
RESTORE PROVED: blob hash matches HEAD and `git diff HEAD` is empty
VERDICT restore-leg-exit=0
```
⭐ The **shape** of that red is the evidence, not the count: the 12 that
fired are all reporting pins (the three lanes' narrowing rows, the three
`yes (widening)` controls, the annotation title, the `no` lane, the
speller). The `LANE IDENTITY` and `GRADING IS UNTOUCHED` pins stayed
**green** through the mutation, and so did the bare-`yes` controls —
which is exactly the claim this PR makes: the literal was reporting, and
only reporting.
## Acceptance notes
Observed while enumerating, ⛔ **not** changed here and ⛔ not filed as
cards:
- The `enforce` refusal's *remedy prose* still cites only the "purely
additive widening takes AT LEAST \`minor\`" ruling and still says "raise
the widened package to \`minor\`". Under a declared **narrowing** the
reading that actually drove the verdict is the arm's launch-window
breaking rule (the `readings` line says so: "a BREAKING change; during
the launch window it ships \`minor\`"), and the package was narrowed,
not widened. That is a *different* sentence from the headline this card
governs, and correcting it is a judgement call about what the remedy
should say — ⛔ not the mechanical derivation this card asked for.
Reported to the PM with dedupe words rather than folded in here.
- `readClause2Line` accepts `yes (narrowing)` (only `no` + `widening` is
treated as a contradiction). The new speller renders it faithfully;
whether that combination should be declarable at all is a parser
question, not a reporting one.
## Scope
- One file: `scripts/check-changeset-no-major.mjs`, its `--self-test`
included. No published surface, no runtime file, no workflow file.
- **`skip-changeset`**: nothing published moves. `scripts/**` ships in
no package's `files[]`, so this PR releases nothing.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 8f42b17 commit 5d0ee8f
1 file changed
Lines changed: 190 additions & 19 deletions
0 commit comments