Skip to content

porch: max_iterations enforcement is dead code (getMaxIterations has zero callers) #870

@waleedkadous

Description

@waleedkadous

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-406getMaxIterations definition
  • packages/codev/src/commands/porch/protocol.ts:228 — default-1 parsing
  • packages/codev/src/commands/porch/next.ts:617-618 — phantom-guarding comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions