Repository navigation
Commit d114d4c
fix(pm): C5 reports a widening tell instead of refusing on it (#19495)
Fixes #19490
## What this changes
`scripts/pm/check-clause2-carriers.mjs`'s **C5** limb turned a widening
**tell** into a hard refusal: a card declaring `Clause-②: no` whose diff
carried tells made `--pair N` exit 4, and PD #14 makes `--pair` at 0 a
conjunct of the landing predicate. C5 is now **report-only** — the row
still prints, with its `file:line` evidence and its remedy text, and it
no longer contributes to a non-zero exit.
**⛔ C5 and nothing else.** C6 stays a hard refusal, as do C2, C3, C8, C9
and the UNJUDGED exit-2 path.
## The ruling this stands on
Maintainer ruling of 2026-09-21, recorded at
#18917 (comment).
Quoted verbatim, untranslated, because paraphrasing a ruling rewrites
it:
> 阻碍我的pr落地,导致 agent 开发满就是负面因素,哪怕挡住几个bug,但是出现bug也是可以重新修改的。
The operative distinction that record draws, and which the code now
carries in its own words (`pairReportRows`'s docblock):
> A limb that judges 「is this diff widening a contract」 — which
`check-widening-tells` itself says it cannot prove (its header: 「a tell,
never a proof」, 「cannot tell an array element from a call argument」) —
does not hold a hard gate. A limb that asks 「does a review of record
exist」 does.
## The mechanism
- `pairReportRows(pair, repo)` is new and is **where the demotion is**:
the C5 row is built there instead of being pushed into `rows`, and
`rows.length === 0` is the exit-0 condition. A row that is not in that
list cannot refuse.
- `renderPair` is split into the pure **`pairRendering`** (every line,
its stream, and the exit, computed before a byte is printed) and a
printer. That split is what lets the self-test pin what a run **prints**
beside what it **exits** — "the row still prints" and "the exit is 0"
are two claims, and a case pinning only the second stays green over a
row that has gone silent.
- The C5 row's text gains `C5_REPORT_ONLY_DISPOSITION`: it says the row
is report-only, that the at-tier contract review is what rules on the
tell, and ⛔ that report-only is **not** a clearance and **not** a
verdict that the tell is false. `REFUSAL_SENTENCE`'s remedy is carried
verbatim, unchanged.
- The report row prints under `⚑`, never `✗` — a refusal marker beside
an exit of 0 is the exit register's banned "0-with-a-message", inverted.
- `greenPairLine` gains a `wideningReported` branch, so an exit-0 line
on a pair that carries tells says so instead of going quiet. Without it
a 0 would read as "narrow", which is the silence this file exists
against.
- The report and its `file:line` list print on **every** exit this path
can answer — including the exit-2 gap path, where the tell would
otherwise be lost.
## Measured, before and after
Exit codes captured BEFORE any pipe.
| run | before | after |
| --- | --- | --- |
| `--pair 19314` | **exit 4**, sole adverse row `✗ C5`, 7 tells | **exit
0**, row `⚑ C5`, the same 7 tells |
| `--pair 19438` | **exit 4**, row `✗ C6` | **exit 4**, row `✗ C6` —
untouched |
Both halves of requirement 1 verified: 19314 reaches 0 **because C5 no
longer refuses**, not because any tell disappeared. `grep -c 'T2
packages/spec/src/stack.zod.ts'` reads **7** on the before log and **7**
on the after log — `stack.zod.ts:3412` through `:3418`, each with its
`T2` explanation and its `+` source line, in both.
The after run's exit-0 line says so itself, rather than reading clean:
> 1 parent 9dd9319 commit d114d4c
1 file changed
Lines changed: 260 additions & 32 deletions
0 commit comments