Repository navigation
Commit 10da5c4
feat(pm): H4 reads the prose blocker and prints its upstream's state (#17494)
* feat(pm): H4 reads the prose blocker and prints its upstream's state
`pm:blocked` is a PAIR — the label plus the body line `Blocked-by: #N` —
and the unlock scan fires from that line and nothing else. Seats state the
block in prose instead, which a human reads perfectly and no machine reads
at all, so when the upstream closes nothing returns the card. Three cards
were measured in that shape, each past a satisfied unlock, one of them
`priority:p1` + `security` and stalled four days after its own ruling.
H4 already caught the missing line; it could not say the half that costs —
that the stated condition has already been met. H19 cannot reach this class
either, because it resolves the target of a `Blocked-by:` line and the whole
defect is that there is no such line.
So H4 now scans the anchored prose of a card it is already firing on, reads
the candidate upstreams out of it, and prints each candidate's current state.
The row reads "line missing, and the upstream already closed" when it is.
- `PROSE_BLOCKER_ANCHORS` is a MEASURED list: the three specimens plus every
H4-firing card on the 2026-09-10 board. Bare `blocking` is deliberately out
(it is the label being discussed, not a block being stated).
- Report-only, exit codes unchanged. A candidate is never a verdict: the row
names candidates and their state, writes no label, and keeps H4's remedy.
- Cost rides H19's per-target cache: only a card already firing H4 is scanned,
at most `H4_PROSE_CANDIDATE_CAP` (5) candidates per card are resolved, an
open local candidate is answered free from the listing already in hand, and
the clause declares anything the cap held back.
- The row is emitted in the blocker-liveness loop rather than the main loop,
because that is where the resolver lives; the gathering stays where it was,
ahead of the total-shortfall rethrow. Same population, same answer.
Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK
Co-authored-by: Claude <noreply@anthropic.com>
* test(pm): the prose cases must FAIL, not throw
The ablation leg that empties the anchor set made `proseBlockerRefs(...)[0].repo`
throw, aborting the whole self-test and hiding every case after it — the exact
shape the `blockedByTargets` cases already warn about, and the shape a real
regression in this reader produces. Read through `.map(...).join()` instead, so
an empty reading is a failed assertion with a name.
Claude-Session: https://claude.ai/code/session_01YKEjmbYNvYWJvWGSWx26zK
Co-authored-by: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 634f23d commit 10da5c4
1 file changed
Lines changed: 383 additions & 7 deletions
0 commit comments