Reported by
An external Codev adopter's architect, via cross-workspace afx send, 2026-05-26. Verified independently against packages/codev/src/ HEAD.
The bug
getMaxIterations() is exported from packages/codev/src/commands/porch/protocol.ts:403, parses per-phase max_iterations config from protocol.json (default 1), but has zero callers anywhere in packages/codev/src/. It's dead code. The state machine flows the value through state.iteration but never consults the configured cap, so porch unconditionally re-iterates on every REQUEST_CHANGES (and in practice on COMMENT-level findings too).
The dead-code comment at next.ts:617-618 makes the intent obvious:
// No rebuttal yet — emit "write rebuttal" task (do NOT increment iteration)
// This MUST come before the max_iterations safety valve so the builder
// gets a chance to write a rebuttal before force-advance kicks in.
The safety valve referenced by the comment does not exist after this block. Someone wrote the rebuttal-first path but never landed the force-advance path it was supposed to precede.
Live symptom
Reporter observed on a real in-flight SPIR project: a phase went through 5 review iterations of 3-way consult before the architect had to do a direct override on iter-6 — final verdicts had converged to APPROVE + COMMENT + COMMENT but porch demanded another iter. Other phases of the same project showed 2-iter and mid-iter-2-on-codex-hang patterns. Every SPIR protocol.json phase has max_iterations: 1. Builders ignore it because porch ignores it.
Root cause
$ grep -rn "getMaxIterations" packages/codev/src/
packages/codev/src/commands/porch/protocol.ts:403:export function getMaxIterations(protocol: Protocol, phaseId: string): number {
# ↑ definition only, no callers
See the follow-up comment on this issue
The reporter's first fix proposal (a per-N iteration cap with default 1 or 2) is retracted. A natural experiment over 21 review iterations on the reporter's project produced data showing the cap is the wrong abstraction — real bugs surface in deep iterations and a count-based cap would have shipped critical bugs.
The superseding policy (see the issue comment for the data and rationale): treat COMMENT and REQUEST_CHANGES asymmetrically. ADVANCE when no reviewer returned REQUEST_CHANGES (all APPROVE-or-COMMENT). RE-ITER when any reviewer returned REQUEST_CHANGES, no count limit in normal flow. Keep max_iterations only as a high safety ceiling (~8) for runaway prevention.
References
packages/codev/src/commands/porch/protocol.ts:403-406 — getMaxIterations definition
packages/codev/src/commands/porch/protocol.ts:228 — default-1 parsing
packages/codev/src/commands/porch/next.ts:617-618 — phantom-guarding comment
Reported by
An external Codev adopter's architect, via cross-workspace
afx send, 2026-05-26. Verified independently againstpackages/codev/src/HEAD.The bug
getMaxIterations()is exported frompackages/codev/src/commands/porch/protocol.ts:403, parses per-phasemax_iterationsconfig fromprotocol.json(default1), but has zero callers anywhere inpackages/codev/src/. It's dead code. The state machine flows the value throughstate.iterationbut never consults the configured cap, so porch unconditionally re-iterates on everyREQUEST_CHANGES(and in practice onCOMMENT-level findings too).The dead-code comment at
next.ts:617-618makes the intent obvious:The safety valve referenced by the comment does not exist after this block. Someone wrote the rebuttal-first path but never landed the force-advance path it was supposed to precede.
Live symptom
Reporter observed on a real in-flight SPIR project: a phase went through 5 review iterations of 3-way consult before the architect had to do a direct override on iter-6 — final verdicts had converged to APPROVE + COMMENT + COMMENT but porch demanded another iter. Other phases of the same project showed 2-iter and mid-iter-2-on-codex-hang patterns. Every SPIR
protocol.jsonphase hasmax_iterations: 1. Builders ignore it because porch ignores it.Root cause
See the follow-up comment on this issue
The reporter's first fix proposal (a per-N iteration cap with default
1or2) is retracted. A natural experiment over 21 review iterations on the reporter's project produced data showing the cap is the wrong abstraction — real bugs surface in deep iterations and a count-based cap would have shipped critical bugs.The superseding policy (see the issue comment for the data and rationale): treat
COMMENTandREQUEST_CHANGESasymmetrically. ADVANCE when no reviewer returnedREQUEST_CHANGES(allAPPROVE-or-COMMENT). RE-ITER when any reviewer returnedREQUEST_CHANGES, no count limit in normal flow. Keepmax_iterationsonly as a high safety ceiling (~8) for runaway prevention.References
packages/codev/src/commands/porch/protocol.ts:403-406—getMaxIterationsdefinitionpackages/codev/src/commands/porch/protocol.ts:228— default-1 parsingpackages/codev/src/commands/porch/next.ts:617-618— phantom-guarding comment