porch prints All reviewers approved! for a project where one reviewer never ran. The summary a human reads when deciding to merge cannot distinguish a lane that approved from a lane that read nothing.
Mechanism
packages/codev/src/commands/porch/verdict.ts
// No valid VERDICT: line found but the consult ran — treat as COMMENT (non-blocking skip)
return "COMMENT";
parseVerdict recognises only APPROVE, REQUEST_CHANGES, COMMENT. Anything else falls through to COMMENT.
// COMMENT counts as approve (non-blocking feedback).
return reviews.every(r => r.verdict === "APPROVE" || r.verdict === "COMMENT");
So VERDICT: SKIPPED — the value our NOT-RUN convention deliberately writes — becomes COMMENT, and COMMENT becomes approval.
Note the default comment says "but the consult ran". That assumption is the bug: it was written for a reviewer that ran and produced unparseable output. A reviewer that never ran cannot be expressed in this vocabulary at all.
Second path to the same place
if (reviews.length === 0) return true; // No verification = auto-approve
Zero reviewers also approves. Two independent routes from "no review happened" to "approved".
Blast radius
Every project today that recorded a codex skip. Codex has been quota-exhausted since before 08:00 UTC and reset is 2026-08-27, so #2, #4, #11 and #12 all wrote a VERDICT: SKIPPED NOT-RUN file, and porch counted each as an approval. All four merged.
The merges themselves were sound: I approved each gate knowing the coverage was two of three, and each PR body states it explicitly. But that was the human channel working despite the tool, not because of it. Anyone reading porch summary got All reviewers approved!.
The NOT-RUN convention did its job. The parser undid it.
Fix
- Recognise
SKIPPED as a first-class verdict, distinct from COMMENT.
allApprove must NOT count SKIPPED. A skip is absence of evidence.
- Change the fall-through. An unrecognised verdict on a lane that produced output is
COMMENT; a lane that produced no output, errored, or was never invoked is SKIPPED. Those are different facts.
- Reconsider
reviews.length === 0 -> true. Auto-approving zero reviews is defensible for protocols with no consultation step, but it should be an explicit "consultation not configured", not a silent unanimous pass.
- The phase summary should state coverage:
2 of 3 lanes ran rather than All reviewers approved!.
Pattern
Sixth arrival today at the rule now in lessons-critical.md: a truncated or absent result must not be emitable as a complete one. Others: the render gate returning CLEAN on an unproven screen (#4), log extraction returning arbitrary lines as a diagnosis (#13), recently-merged timing out into an empty panel (#17), a timed-out check reporting as a test failure (#8), and this.
This one has the largest blast radius because it is the line a human reads before merging.
Found by the #12 builder during its own review phase, and it flagged the finding against its own gate rather than staying quiet.
porch prints
All reviewers approved!for a project where one reviewer never ran. The summary a human reads when deciding to merge cannot distinguish a lane that approved from a lane that read nothing.Mechanism
packages/codev/src/commands/porch/verdict.tsparseVerdictrecognises onlyAPPROVE,REQUEST_CHANGES,COMMENT. Anything else falls through toCOMMENT.So
VERDICT: SKIPPED— the value our NOT-RUN convention deliberately writes — becomesCOMMENT, andCOMMENTbecomes approval.Note the default comment says "but the consult ran". That assumption is the bug: it was written for a reviewer that ran and produced unparseable output. A reviewer that never ran cannot be expressed in this vocabulary at all.
Second path to the same place
Zero reviewers also approves. Two independent routes from "no review happened" to "approved".
Blast radius
Every project today that recorded a codex skip. Codex has been quota-exhausted since before 08:00 UTC and reset is 2026-08-27, so #2, #4, #11 and #12 all wrote a
VERDICT: SKIPPEDNOT-RUN file, and porch counted each as an approval. All four merged.The merges themselves were sound: I approved each gate knowing the coverage was two of three, and each PR body states it explicitly. But that was the human channel working despite the tool, not because of it. Anyone reading porch summary got
All reviewers approved!.The NOT-RUN convention did its job. The parser undid it.
Fix
SKIPPEDas a first-class verdict, distinct fromCOMMENT.allApprovemust NOT countSKIPPED. A skip is absence of evidence.COMMENT; a lane that produced no output, errored, or was never invoked isSKIPPED. Those are different facts.reviews.length === 0 -> true. Auto-approving zero reviews is defensible for protocols with no consultation step, but it should be an explicit "consultation not configured", not a silent unanimous pass.2 of 3 lanes ranrather thanAll reviewers approved!.Pattern
Sixth arrival today at the rule now in
lessons-critical.md: a truncated or absent result must not be emitable as a complete one. Others: the render gate returning CLEAN on an unproven screen (#4), log extraction returning arbitrary lines as a diagnosis (#13),recently-mergedtiming out into an empty panel (#17), a timed-out check reporting as a test failure (#8), and this.This one has the largest blast radius because it is the line a human reads before merging.
Found by the #12 builder during its own review phase, and it flagged the finding against its own gate rather than staying quiet.