| name | review-loop |
| description | Agent-driven peer review loop: structured findings, dispositions, circuit breakers, commit strategies, sandbox isolation, and plan-phase advisory mode. |
Review Loop Protocol
Agent-driven peer review loop for Build and Plan phases. The Build agent drives the loop; the reviewer is a separate, read-only LLM.
Loop Architecture
The review loop is agent-driven: the Build agent calls /review-submit, which invokes review-round-runner.sh as a synchronous subprocess. The runner executes one round and returns. The agent reads the result and decides the next action. There is no long-running orchestrator.
Driver-as-submitter mode: under the auto-build loop (auto-build-loop.md), scripts/auto-build-loop.sh plays the /review-submit role — it initializes .cct/review/state.json, invokes the runner with CCT_REVIEW_BASE_REF=<phase-base> so the whole multi-commit phase diff is reviewed (the default remains HEAD~1), commits review fixes itself, and injects commit_ref into fixed dispositions. The runner's contract (exit codes, file formats, breakers, sandbox) is unchanged.
Agent completes work → /review-submit → review-round-runner.sh (one round)
↓
EXIT 0 (PASS) → loop-summary.json → /phase-complete
EXIT 1 (FAIL) → findings-round-N.json → agent fixes → /review-submit
EXIT 2 (BREAKER) → breaker-tripped.json → agent stops → /review-decide
File Contract
All review-loop artifacts live under .cct/review/ during the active loop.
| File | Written By | Purpose |
|---|
state.json | /review-submit (init), runner (update) | Loop state: round, attempt, findings, breaker counters |
findings-round-N.json | Runner | Structured findings for round N |
resolution-round-N.json | Build agent | Builder's response to each finding |
breaker-tripped.json | Runner | Breaker context, awaiting /review-decide |
decision.json | /review-decide | Human's decision: approve, reject, or retry |
loop-summary.json | Runner (PASS) or agent (bypass) | Final record of all rounds |
On loop completion, the completing actor copies loop-summary.json and writes a build-review.md artifact to specs/<feature-id>/collaboration/.
Finding Schema
Each finding has a stable ID computed from SHA-256(file + category + normalized_description) truncated to 8 hex chars, prefixed with f-. Line numbers are excluded from the ID to remain stable across edits.
{
"id": "f-a1b2c3d4",
"severity": "blocking|warning|note",
"category": "correctness|security|style|performance|design|testing|documentation",
"file": "path/relative/to/project",
"line_hint": "semantic anchor (display only)",
"description": "clear description",
"suggested_fix": "actionable fix",
"first_seen_round": 1,
"disposition": null
}
Severity Levels
| Severity | Blocks PASS? | Builder must respond? |
|---|
blocking | Yes | Yes — must set disposition |
warning | No | Optional |
note | No | No — informational |
Disposition Values
The builder responds to each blocking finding with a disposition in resolution-round-N.json:
| Disposition | Meaning | Required fields |
|---|
fixed | Builder addressed the finding | commit_ref (SHA of fix commit) |
disputed | Builder disagrees | detail (explanation) |
deferred | Acknowledged, fix later | detail (optional) |
not-applicable | Finding doesn't apply | detail (optional) |
Circuit Breakers
All breakers escalate to human — there is no path to automatic acceptance of unresolved work.
| Breaker | Default | Env Var | Behavior |
|---|
| Max rounds | 5 | CCT_REVIEW_MAX_ROUNDS | Escalate to /review-decide |
| Wall-clock timeout | 900s | CCT_REVIEW_TIMEOUT_SEC | Escalate to /review-decide |
| Stale findings | 2 consecutive | CCT_REVIEW_STALE_THRESHOLD | Escalate to /review-decide |
| Provider unavailable | — | — | Escalate to /review-decide |
A stale finding is one that appears in N consecutive rounds with disposition fixed each time (builder thinks they fixed it, reviewer disagrees). Stale findings remain blocking — they do not auto-downgrade.
Human Decision Channel
When a breaker fires, the agent stops and the human runs /review-decide:
| Decision | Effect |
|---|
approve | Agent writes loop-summary.json with bypass: true. Proceeds to /phase-complete. |
reject | Agent logs rejection. No merge. |
retry | Reset breaker state. Round numbering continues monotonically (next round is N+1, not 1). |
Commit Lifecycle
Commit-Strategy Modes
| Mode | Flag | Behavior |
|---|
single | --review-commits single | Amend same commit each round |
per-round | --review-commits per-round | New commit per fix round: fix(review): round N — <summary> |
squash | --review-commits squash (default) | Per-round during loop; git reset --soft to pre-review commit on PASS |
Rules
- The review system does NOT auto-commit. The Build agent commits following
phase-workflow.md commit-gate rules.
- Before each
/review-submit, the worktree must be clean (all fixes committed).
- Squash mode presents the final commit for user approval before creating it.
- If
git reset --soft fails, fall back to a merge commit — never force-reset or lose work.
Plan-Phase Review
Product decision: Plan review is advisory and single-round.
- Run exactly one
/review-submit round
- A FAIL verdict is logged but does not gate Build entry
- No fix loop, no circuit breakers, no commit strategies
- Artifact written as
plan-consult.md with mode: consult
- Stop hook does not block plan-phase session stop based on review outcome
This is deliberately narrower than Build-phase review. Plan artifacts are already approved by the human via the Plan Approval Gate before Build begins.
Read-Only Enforcement
The reviewer runs in a snapshot copy of the working tree:
- Working directory:
cp -R to temp dir
- No
.git directory (reviewer cannot create commits)
SSH_AUTH_SOCK and GPG_AGENT_INFO unset
CCT_READ_ONLY=true set in environment
- Post-review validation: runner compares real repo HEAD and status before/after. If changed, round is marked INVALID and findings are discarded.