independent-review-loop
SkillAI & modelsIndependent review loop run before delivery in code work. An independent reviewer (Codex, or fresh Codex-style subagents) reads the whole PR against the approved outcome; the lead fixes in-scope functional findings and re-reviews until none remain. Its differentiator is independence and exhaustiveness — a reviewer that fails differently from the authors, run to clean — distinct from the recursive-refinement ladder. Invoked by the team lead from the unified pre-ship gate (code-mode and /swarm:refine) after the ship steps.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the independent-review-loop skill
What this skill tells your AI
The instructions your AI receives, as published by dheerg/swarms in skills/independent-review-loop/SKILL.md and read by ahel’s review.
Operational spec for the team lead. This skill runs an independent review pass at delivery: a reviewer that fails differently from the author reads the whole PR against the approved outcome, the lead fixes what is in scope, and the loop repeats until the reviewer finds no more in-scope functional issues. It is the automated form of "ship, then loop the PR through Codex until it stops finding edge cases" — with the lead acting as the operator who keeps findings on-scope.
What distinguishes this from the team's own review and the recursive-refinement ladder is independence and exhaustiveness: the reviewer is a different model (Codex) or a fresh-context agent that fails differently from the authors, and it runs to exhaustion — until no in-scope functional finding remains — rather than the bounded rung ladder. (The ladder hunts bugs too, as it drives the work to the full scope of the outcome; the new axis here is the independent eye run to clean, not bug-hunting per se.) It is a distinct pass; it never replaces the ladder.
Only the lead runs this. Reviewers (Codex, or fresh subagents) are read-only; the lead is the sole writer, same as every other phase.
When this runs
Invoked from the unified pre-ship gate in code-mode's Refine/Deliver (and /swarm:refine) — the gate the lead presents once the team reaches 9/10+, offering (thoroughness-descending): recursive refinement + independent review / recursive refinement only / independent review loop only / ship as is. This skill runs for the two options that include the independent loop — recursive refinement + independent review (after the recursive ladder completes) and independent review loop only (on its own); recursive refinement only and ship as is do not invoke it. Deliver invokes this skill when the independent-review-loop pending run-state task recorded at the gate is open — it branches on that recorded task, not on recall of the pick.
It runs after the ship steps complete (PR-then-loop on the common path): when a PR was created, the loop reviews the PR's diff and pushes each round's fixes to it; on a commit-only / push-only ship with no PR, it reviews the pushed branch against its base. The diff base is resolvable from the PR when one exists (see the loop's base resolution below for the no-PR fallback).
Engine choice (Codex or Swarm fallback)
Persisted preference first. Before asking, read .claude/swarm-review.md — the per-project reviewer config. It mirrors the mechanism of .claude/swarm-ship.md (ask once, read thereafter) but is a separate file: reviewer config is not ship config. If it names a recognized engine (Codex or Swarm fallback), use it and skip both questions below — print a one-line plain-text notice so the choice is visible and overridable ("Using saved reviewer: — edit .claude/swarm-review.md to change"), no modal. If the file is present but its engine value is missing, garbled, or unrecognized, treat it as no saved preference and fall through to the questions below — a typo must never wedge the choice. A saved preference only selects the engine; it never asserts availability. Still run the runtime check (command -v codex + the first call's auth result). If the saved engine is unavailable on this machine, auto-degrade to the Swarm fallback without a modal — print a one-line notice ("Saved reviewer Codex unavailable here — using the Swarm fallback for this run") and do not rewrite the file. A saved preference must never become a blocking modal on a machine that lacks the tool — that is the portability guarantee (and it differs from an interactive Codex pick that's absent, which may ask). Swarm prompts are portable across machines, so a peer missing Codex neither hangs on a teammate's saved pref nor has it silently overwritten.
When no preference is saved, select the reviewer engine with AskUserQuestion (header "Reviewer"). Detect Codex once: command -v codex (exit 0 = present). Offer both engines every time — command -v codex only sets the order/default and a neutral note. No affordability language, ever. ("No loop" is not offered here — that is the gate's "ship as is.")
- Codex present → 1) "Codex" 2) "Swarm fallback".
- Codex absent → 1) "Swarm fallback" 2) "Codex (requires Codex CLI)".
Descriptions (neutral): Codex = "An independent model (Codex) reviews the whole PR; fails differently from the code's author." Swarm fallback = "A fresh Claude reviewer in a Codex style — sharp, but shares the author's model blind spots; always available, no Codex needed."
Then ask the scope of that choice (only when no preference was saved) with a second AskUserQuestion (header "Save reviewer"): "Use this reviewer just this session, or always for this project?" — "This session" (default — nothing is written) / "Always" (persist it). Only "Always" writes .claude/swarm-review.md, with a single ## Engine field naming the chosen engine:
# Reviewer Definition
## Engine
[Codex | Swarm fallback]
Persist the engine only — not availability, and not the rate-limit wait policy (the within-cap auto-wait below is default behavior, not a saved knob; the file is structured so a future field can be added without rework).
If the user picks Codex while it is absent/unauthed, surface Codex's own message + how to enable (codex login, or install per Codex's docs) once, then offer the Swarm fallback or stopping — never hang. While this gate is live and teammates exist, apply the launch.md live-team gate rules (ask teammates to hold; re-ask once if the modal is preempted).
The loop (identical control flow on both engines, except the non-convergence backstop)
Resolve the diff base once. The review is simply the branch's committed diff against the branch it targets — main, or the repo's equivalent default/target branch. A GitHub PR is not required: Codex does a PR-style review of any branch, and the PR (when the ship definition uses one) is only the push target, not a precondition. Pick the base as the PR base if a PR exists (gh pr view --json baseRefName just names the target branch), otherwise the repo's default branch (git symbolic-ref refs/remotes/origin/HEAD --short, stripped of origin/); compare against the up-to-date base — the fetched remote-tracking ref (e.g. origin/main), not a possibly-stale local copy. The base must be the point the work diverged from, so the diff covers this run's changes; if the work was committed directly onto the base branch (so <base> and HEAD coincide), use the pre-work commit instead. If the base can't be resolved, ask the user to provide or confirm the base (or to explicitly opt out of the loop) — never silently skip the review the user selected, and never error. And if the resolved git diff <base>...HEAD comes back empty, that is NOT a clean review — nothing was reviewed — so surface it to the user to correct the base rather than terminating clean. Then each round:
-
Review the WHOLE PR diff, every round — the full
git diff <base>...HEAD, never just the delta since the last fix. New findings hide behind old fixes; only a whole-PR pass surfaces them. -
Collect findings in Codex's native shape (see Output format).
-
Relay the round to the user — verbatim, non-blocking, before the fixes. One contiguous plain-text block, one-way (emit-and-continue — never AskUserQuestion, never a wait for a reply; the loop does not stop), emitted once the triage in step 4 has determined the disposition and before any of the round's fix edits: a one-line header
Round N — independent review, then the reviewer's output reproduced in full, verbatim — raw and unmediated, so the user sees the reviewer's voice before the driver's framing (on the Swarm fallback's multiple reviewers: each reviewer's output verbatim, labeled by its lens), then a one-line intent-phrased disposition —Fixing <n> in-scope; <m> filed out-of-scope, orClean — no in-scope findings; loop terminating, orReviewer returned no usable output — retrying. The disposition is forward-phrased ("Fixing…", never "Fixed…") — the fixes have not run when it renders. A round that ends in an escalation or degrade emits no leading block at all — the same-turn modal would collapse it; everything decision-relevant rides in the self-sufficient escalation modal (see Backstop and the Codex path), which quotes the reviewer's critical line(s) where a valid review ran and the verbatim error on a degrade. The disposition narrates the termination guard's determination (below); it never triggers or substitutes for it: writeClean …only when the guard actually cleared — the reviewer's own affirmative-clean line (relayed verbatim in this block; the lead never synthesizes "clean") or every finding triaged out-of-scope — and a FAILED review (empty/malformed/non-review) gets the retry line and no termination. This fires every non-escalation round a review actually ran, clean rounds included; an escalation round routes to the modal instead and holds the round's other in-scope fixes — every in-scope finding except the escalated one, where a specific finding is escalated — until the operator decision: the escalated finding follows that decision, never an auto-land, and on a continue the held fixes land, with a normal leading-block relay, on the resumed round, so the operator decides against a stable diff; a rate-limit park ran no review, so it emits nothing (on resume the round runs and the relay fires normally). It is distinct from the end-of-loop out-of-scope pile in step 5 — raw reviewer voice + this round's disposition here, the lead's cumulative triage ledger there; do not collapse them.IMPORTANT — the relay leads the round's rendering. Emit the relay as one contiguous leading text block ahead of any of the round's fix edits, in the same turn — never deferred to the turn's end, never split around the edits, never a back-reference ("relayed above"). Streamed text between tool calls renders live in a plain working turn; what collapses it is a same-turn AskUserQuestion — which is why the modal-ending escalation rounds carry their substance inside the modal itself (see Backstop and the Codex path) rather than in a leading block. As a thin durability net, recap each round's one-line disposition (never the verbatim reviewer output) in the final text block of a turn that ran review rounds. Run each round's review as a background invocation whose completion opens the next turn — one round per turn — so a later round's escalation modal can never share a turn with, and collapse, an earlier round's streamed relay; between-round boundaries are safe (the round's fixes are committed before the next review launches — nothing yields uncommitted state), unlike the rejected mid-round split between a round's relay and its fixes.
-
The lead triages each finding against the approved outcome — the lead is the codified "operator once in a while":
- Bears on the outcome and is a real functional defect → fix it (lead writes the change) — except on an escalation round, where this round's fixes are held per step 3. Fix the class, not the site: a finding names one instance — before fixing, state the rule it violates and grep for sibling sites; fix the reachable siblings in the same round (a sweep that finds only the named site is a valid result, but it must be a result, not an assumption). When the same class recurs across rounds — new siblings, same rule — it is an invariant, not a bug; fork on this same triage, never pausing the loop: in-scope (the outcome requires the invariant everywhere) → stop scattering point-fixes, centralize the rule at one shared entry point, and surface the diff growth after per step 5's scope-growth discipline; exceeds the outcome → point-fix the named site now and file the enumerated remainder in the out-of-scope pile as a consequential item per step 5, recommending the chokepoint centralization as its resolution.
- Out of scope (new feature, adjacent refactor, gold-plating, pre-existing issue, style) OR genuinely ambiguous → do not fix; add to the out-of-scope pile. When unsure, surface — never silently drop and never silently fix.
-
Surface the out-of-scope pile to the user (plain text) at the end of the loop, or immediately if a pile item is consequential (a real defect you're declining because it exceeds the approved outcome) — that is the rare operator escalation the user asked for. The diff may legitimately grow toward outcome-completeness (e.g., fixing an off-diff caller the outcome requires); that growth is bounded by the outcome, grounded in
git diff --stat <base>...HEAD, and surfaced — not silent scope expansion. -
Verify each fix landed, then commit this round's fixes (push per the ship definition), then re-run. Before committing, confirm each fix actually applied and changed the intended behavior — a scripted or multi-site replace that matched nothing is a silent no-op, not a fix, and a build exit code proves compilation, not behavior ("never claim compliance you didn't execute," applied to the lead's own edits). The review reads the committed
git diff <base>...HEAD(the reviewer runs git locally), so the lead MUST commit each round's fixes (e.g.,review: round N — <summary>) before re-running — otherwise the reviewer re-reads stale committed state, the loop never converges, and every just-fixed finding reappears unchanged and is misread as oscillation. Pushing is governed by the approved ship definition, not the loop: if the ship definition pushes (a PR or push workflow), push each round — to the PR if one exists, else the branch — to keep the remote reflecting the work; if it is commit-only, do NOT push (committing is enough for the review, and pushing would silently change the user's approved delivery or fail on a branch with no upstream). After committing (and pushing where the ship definition calls for it), re-run the review on the whole diff.
Termination (the stop criterion is identical on both paths; the safety net behind it is engine-conditional — see Backstop) — stop when, after the lead's triage of a valid review, no in-scope functional findings remain. Two cases satisfy this and both terminate: (a) the review is an explicit, format-conformant affirmative "no findings" result; or (b) the review returned findings but the lead triaged them all as out-of-scope/ambiguous — terminate and surface the out-of-scope pile. Do NOT re-run a round whose only findings are out-of-scope (re-running an already-on-scope-clean state just churns pointlessly). What must NOT be read as termination is a non-review: empty output, a truncated or off-format response, a clarifying question, or a non-zero exit is a FAILED review, not a clean one — retry once, then degrade (Codex: take the error path / offer the Swarm fallback; Swarm: re-spawn the reviewer). Never read silence or a non-answer as "done": a false "done" silently ships bugs while reporting success, defeating the loop's whole purpose. On Codex — which has no round cap — this validity guard is load-bearing for termination itself: there is no numeric backstop behind the Codex off-ramp set named in Backstop (the canonical list lives there). (This is a validity check — was this a real, well-formed review? — combined with the lead's in/out-of-scope triage; not a field-by-field parser.) Both Codex and the swarm-native reviewers emit the same finding format, so the terminator never depends on an engine-specific field; on the Codex path an overall_correctness: patch is correct verdict, when present, is a convenience corroborator only, not the stop signal. Expect this to take many rounds — 8–10 rounds of genuinely different findings is normal and runs fully autonomously — but the stop condition is the clean review itself, not a round count: once a valid review returns no in-scope findings (case (a) or (b) above), terminate; do NOT re-run an already-clean review hoping to surface more. "Don't stop early" means don't declare done without a clean review (a small-but-nonempty round is not a clean review) — it never means re-running a review that already came back clean.
Backstop — the non-convergence guard is engine-conditional. On the Swarm fallback, a hard cap of 15 rounds (a fixed constant, not a config knob) guards against a non-converging loop. The cap counts fallback-reviewed rounds only: on a mid-loop switch to the fallback (a degrade, or an escalation resolved as fallback), its count starts at zero — an uncapped Codex prefix never arrives with the cap pre-exhausted — while the loop's round numbering continues unbroken. Reaching it does NOT silently abort: escalate to the user (continue / stop here / take over). On Codex there is no round cap — the loop runs until clean, and its non-convergence protection is the remaining off-ramp set: affirmative-clean termination (or all-out-of-scope triage), the oscillation escalation, the repeated-invalid-review escalation, and the rate-limit park. On both engines, repeated invalid reviews (several consecutive failed/malformed reviews after retry/degrade) escalate the same way rather than silently riding on. An oscillating finding — the same finding, matched by its title + file:line (never by [P#], which is severity and is shared across unrelated findings), resurfacing after ≥2 fix attempts — is a disagreement, not a grind: stop auto-fixing it and surface it to the user with both positions for an operator decision. (A recurring class of new siblings is not oscillation — broaden the fix per step 4, never the oscillation key.) Escalation modals are self-sufficient: a same-turn AskUserQuestion collapses the turn's streamed text, so each escalation carries everything decision-relevant inside the modal's own content — and where a valid review ran, that content quotes the reviewer's own critical line(s) verbatim, never only the lead's paraphrase (the step-3 auditability, carried into the modal): oscillation quotes the resurfacing finding alongside the lead's position; the round-cap escalation (Swarm fallback only) includes what the reviewer is still finding, in its own words; and both name the held-fix count from step 3 — "also holding N in-scope fixes", N counting the held others and excluding the escalated finding — so a stop is chosen knowing what dies with it (a degrade modal names no held count: a failed review has no in-scope findings to hold). (Unlike a standing-team review dispute, this loop's reviewer is Codex or an ephemeral one-shot — not an addressable teammate — so swarm:resolve-dispute, which messages the reviewer, does not apply here; the user is the arbiter. This is the spirit of the "break review loops with evidence" rule, routed to the operator.)
Whenever the loop returns control to Deliver having terminated (it will not resume), Deliver closes the independent-review-loop pending task. That is EVERY terminal exit, not a select list: a clean review; all findings triaged out-of-scope; a rate-limit escalation resolved as stop; or the user choosing stop or take-over at ANY escalation — the Swarm fallback's 15-round backstop, an oscillation, repeated invalid/failed reviews, a Codex auth/fatal degrade the user ends, or an unresolvable/empty base the user opts out of. What does NOT close it is a non-terminal pause that will resume: a timed park, an escalation still awaiting the user's decision, or a fallback / wait-anyway / continue. An open task after a terminal return would re-churn the loop via the pulse; closing it during a pause would lose the loop. (The test is mechanical: will the loop run again? Pause → keep open. Done → close.) The terminal handshake never shares a turn with a round relay: when the loop terminates, the turn ends with the final round's relay, the out-of-scope pile, and every in-session delivery artifact the ship type owes (the no-PR Decision digest; /swarm:refine's in-session summary) as its final text block — Deliver's in-session content completes here, so only the terminal handshake (pulse-delete, then the Terminal gate) runs on the next wake, which the pulse's own terminal backstop already implements for both ship classes once the in-session artifact has landed; a same-turn Terminal modal would collapse the loop's final relay, the exact loss this relay exists to prevent. (A terminal-turn boundary is safe where a mid-round one is not: the loop will not resume, so nothing races the pulse.)
Auditability — why step 3 relays raw, before the fixes: the lead is both loop-driver and fixer, so the user — not the self-interested driver — must be the backstop on "are we actually done," and a false Clean disposition is visibly contradicted by the reviewer's own text sitting directly above it. The obligation itself lives in step 3; this note is rationale only.
The steer (one string, both engines — this is the over-reach guard)
Send the reviewer exactly this (substituting the approved outcome verbatim and the resolved base):
The outcome this change is meant to achieve: «approved outcome, verbatim»
Review the ENTIRE change on this branch relative to
«base»— the full PR diff (git diff «base»...HEAD), not just the latest edits. Report only material, FUNCTIONAL findings that bear on that outcome: incorrect logic, defects, broken or unhandled edge cases, and regressions in code this change touches.Do NOT report: style, naming, or formatting; low-value cleanup; pre-existing issues this change did not introduce; speculation you cannot tie to a concrete failing input or condition; or choices the author clearly made intentionally. A finding must be discrete, actionable, and something the author would fix if they knew.
Prioritize each finding [P0]–[P3]. No code suggestions — for each finding, explain in prose what breaks, the exact conditions that trigger it, and the consequence. Format: open with a 1–3 sentence summary, then a line
Full review comments:, then one bullet per finding as- [P#] <imperative title> — <file>:<line-range>followed by the indented prose paragraph. If you find no in-scope functional issues, state that explicitly (e.g., "No in-scope functional findings.") — do not return an empty or merely terse response.
The closing affirmative-clean instruction is load-bearing: it is what lets the termination guard above distinguish a genuinely clean review from a malformed/empty/aborted one. Without it a clean reviewer may return nothing, the guard reads that absence as a FAILED review, and the loop never terminates — on the Swarm fallback burning to its 15-round cap and firing a spurious "didn't converge" escalation on work that was actually clean; on uncapped Codex there is no round cap to eventually catch it, and the failure surfaces only through the repeated-invalid-review escalation.
Telling the reviewer the outcome and asking only for functional findings is the steer that keeps the loop on-scope — the input-side guard, paired with the lead's triage on the output side.
Codex path
Codex is the preferred reviewer because it is a genuinely different model and fails differently from the code's author. Run it via Bash (the lead can run shell; /codex:* slash commands are not model-callable, so do not rely on them):
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 85
- Forks
- 7
- Last commit
- Jul 2026
Advanced
- Catalog kind
- skill
- Gateway key
independent-review-loop- Source
- github.com/dheerg/swarms