Found by the domain:cli execution PM seat (#6024) while acting on a real Check Changeset red. ⛔ Not graded and no pm: state — lane and kind only; priority is triage's.
The contradiction, inside one job, 119 ms apart
Job 102616882441 (run 34396338801), Check Changeset on PR #17228:
19:40:02.7148889Z Labels on PR #17228 right now: documentation
19:40:02.7149246Z size/m
19:40:02.7149451Z tests
19:40:02.7149645Z tooling
19:40:02.7149872Z needs:contract-review ← present
...
19:40:02.8332871Z · carrier: `needs:contract-review` is not on this PR (0 label(s) read)
The label is on the PR. The step immediately before — the allow-major guard, which notes in its own echo that it reads labels "live, not from the event payload" — enumerates five labels including it. scripts/check-changeset-no-major.mjs, run seconds later in the same job, reads zero.
Reproducible, not a one-off: the same PR failed identically in two separate workflow runs — jobs 102616882441 (run 34396338801) and 102616922544 (run 34396350963).
⇒ The likely difference is the one the sibling step names: reading labels live versus from the event payload. A PR whose labels were applied after the event was emitted reads as unlabelled. ⚠️ That is a hypothesis from the sibling's own echo, ⛔ not a measurement — I have not read the script.
⛔ Why this is more than a cosmetic wrong line
The false reading is not inert: the gate uses it to characterise the PR's state, and then routes the reader on that characterisation.
The two declarations disagree, inside one PR:
· carrier: `needs:contract-review` is not on this PR (0 label(s) read)
· declaration line: `Clause-②: yes`
They do not disagree — both say clause-② is on. And the gate's two remedies are explicitly "not interchangeable":
- The declaration is right and the level is wrong → raise the widened package to
minor.
- The level is right and the DECLARATION is wrong → correct it at the producer: the
needs:contract-review carrier is the review seat's to place and to clear, and the Clause-②: line is the claim's.
⇒ A manufactured disagreement is evidence for path 2, and path 2's repair is removing the carrier or the Clause-② line. On PR #17228 that would have stripped a maintainer-ruled contract-review requirement (2026-09-04, decision batch #27, on #15071) off the PR — to clear a red actually caused by a changeset graded patch instead of minor, which is path 1.
⚠️ The gate is right about the failure and wrong about the diagnosis, and the wrong half is the half that reads as a verdict on the declaration. Its closing warning — 「⛔ Do not add a tolerance here to route around a declaration that says something its author did not mean」 — is exactly the discipline the false line undermines, since it invites a reader to conclude the author did not mean it.
What would close it
check-changeset-no-major.mjs reads labels the way its sibling step does — live, not from the event payload — or, if it cannot, says "could not read labels" rather than "0 label(s) read" and ⛔ never presents an unread carrier as an absent one. An instrument that cannot see must not report a zero: a zero with no firing control is not a reading.
- The "two declarations disagree" branch fires only when it actually read both. With one side unreadable there is no disagreement to report, and ⛔ path 2 must not be offered on the strength of it.
- A control on the gate itself: a fixture PR carrying
needs:contract-review must make the carrier line report present. Without that leg, this defect is invisible to the gate's own tests — it is a wrong string, not a wrong exit code, and the exit code here was correct.
Not in scope
Refs
Found by the
domain:cliexecution PM seat (#6024) while acting on a realCheck Changesetred. ⛔ Not graded and nopm:state — lane and kind only; priority is triage's.The contradiction, inside one job, 119 ms apart
Job
102616882441(run34396338801),Check Changeseton PR #17228:The label is on the PR. The step immediately before — the
allow-majorguard, which notes in its own echo that it reads labels "live, not from the event payload" — enumerates five labels including it.scripts/check-changeset-no-major.mjs, run seconds later in the same job, reads zero.Reproducible, not a one-off: the same PR failed identically in two separate workflow runs — jobs
102616882441(run34396338801) and102616922544(run34396350963).⇒ The likely difference is the one the sibling step names: reading labels live versus from the event payload. A PR whose labels were applied after the event was emitted reads as unlabelled.⚠️ That is a hypothesis from the sibling's own echo, ⛔ not a measurement — I have not read the script.
⛔ Why this is more than a cosmetic wrong line
The false reading is not inert: the gate uses it to characterise the PR's state, and then routes the reader on that characterisation.
They do not disagree — both say clause-② is on. And the gate's two remedies are explicitly "not interchangeable":
⇒ A manufactured disagreement is evidence for path 2, and path 2's repair is removing the carrier or the
Clause-②line. On PR #17228 that would have stripped a maintainer-ruled contract-review requirement (2026-09-04, decision batch #27, on #15071) off the PR — to clear a red actually caused by a changeset gradedpatchinstead ofminor, which is path 1.What would close it
check-changeset-no-major.mjsreads labels the way its sibling step does — live, not from the event payload — or, if it cannot, says "could not read labels" rather than "0 label(s) read" and ⛔ never presents an unread carrier as an absent one. An instrument that cannot see must not report a zero: a zero with no firing control is not a reading.needs:contract-reviewmust make the carrier line report present. Without that leg, this defect is invisible to the gate's own tests — it is a wrong string, not a wrong exit code, and the exit code here was correct.Not in scope
patch-vs-minorverdict on PR fix(rest): a sandboxed crash outranks the declared-code arm and answers the sanitised 500 UNCLASSIFIED_FAULT (#15071) #17228 is correct and is being fixed there as path 1. This card is only about the carrier line and what it routes a reader toward.Refs
domain:devxfinding onpr-automation.yml's "WHICH LEVEL" prose, adjacent surface, ⛔ different defect.