Review loop for staged changes. Detects content type, prepares a review request for Codex (Terminal-relay manual default, opt-in Auto-terminal codex-exec subprocess, or IDE plugin) or an explicitly selected Agy, Copilot, or headless Claude backend, categorizes feedback, revises, and iterates. Works for code, papers, proposals, or any text-based output.
Installs into .claude/skills of the current project.
Are you the author of Implement Review?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/yzhao062-implement-review)
---
name: implement-review
description: Review loop for staged changes. Detects content type, prepares a review request for Codex (Terminal-relay manual default, opt-in Auto-terminal codex-exec subprocess, or IDE plugin) or an explicitly selected Agy, Copilot, or headless Claude backend, categorizes feedback, revises, and iterates. Works for code, papers, proposals, or any text-based output.
---
# Implement-Review
## Overview
A review loop for staged changes. Claude Code detects the content type, sends the changes to one or more reviewers, categorizes the feedback, revises, and iterates. Codex is the primary reviewer via three channels: terminal relay (manual copy-paste, default on every platform), Auto-terminal (opt-in subprocess dispatch), or IDE plugin. The Auto-terminal channel also supports explicit cross-vendor reviewer backends for GitHub Copilot CLI, Claude Code, and Gemini through Antigravity CLI. Other reviewers are driven ad-hoc by the user through their own UI and only need to honor the `Review-<AgentName>.md` save contract defined in Phase 1c. Bare `/implement-review auto` is unchanged and still selects Codex.
## When to plan-review first
**Any complex task benefits from a plan review BEFORE execution**, not only writing or code. Plan-first catches architectural holes while they are still cheap to fix. The scope includes: system design, refactors, paper outlines, proposal structure, data-pipeline redesigns, multi-stage debugging strategies, teaching / curriculum planning, release-process changes, migration plans, and anything else where the shape of the work precedes and constrains the execution.
**Plan-review is a Phase 0 before the staged-change loop below.** If the user asks for a plan review, or if the task clearly meets the signals below, do not apply the staged-change prerequisite in Phase 1 yet. Tell the reviewer to read the plan file directly (or paste the plan contents via the terminal path when the reviewer cannot access the file) and critique the design, not `git diff --cached`. After the plan has no High findings and no new design blockers, execute the work and resume the normal staged-output review flow at Prerequisites / Phase 1.
### When to plan-first
Signals that the round-trip pays off:
- **Blast radius is large** -- multiple files, cross-cutting concerns, shared state, multiple stakeholders, or the organizing structure of a deliverable.
- **Irreversible once executed** -- publishes, submissions, deployments, immutable packages, paper submissions, external commitments.
- **History shows a pattern** -- "got the structure wrong, redo next cycle" has happened on this track before.
- **Uncertainty in the approach** -- the user is weighing alternatives and wants the design validated, not the execution reviewed.
- **Context is unfamiliar** -- new codebase, domain, audience, agency, collaborator workflow, or external constraint set, where a wrong assumption can shape the rest of the work.
### When to skip plan-first
- Change is small, local, reversible.
- The design is already worked out and only execution feedback is wanted.
- Plan and execution would be the same artifact (three-line bug fix, one-sentence footnote).
### Process
1. Write the plan to a scratch file `PLAN-<identifier>.md` in the most natural location for the task (repo root for code, paper-repo root for Overleaf-style docs, a local scratch directory beside the deliverable for tasks that do not live in git). If the plan lands inside a git worktree, add it to `.git/info/exclude` so `git add -A` does not accidentally stage it; outside git, keep it as a clearly named scratch file outside the final deliverable and delete it after review.
2. Content varies by task but at minimum include: purpose, non-goals, structure, regression or failure analysis, validation plan, open questions. Keep it terse -- 1 to 3 pages.
3. Send the plan through a plan-review prompt (not the staged-change template). Make clear this is a pre-execution design review and that the plan file path or pasted contents are what the reviewer should read; instruct the reviewer to critique the design rather than to run `git diff --cached`. A reviewer with direct repository access reads the named files once. A snapshot backend (Antigravity) sees only the exported index, so a gitignored plan, an untracked evidence note, or an untracked local override is pasted into the request once, each under a `--- SUPPLIED INPUT: <path> ---` heading that names its source path; the reviewer reads the supplied copy and never the original worktree. Use the normal "Save your complete review to Review-<AgentName>.md" save-contract from Phase 1c.
4. **Reviewer must answer the scope-challenge questions** (see "Adversarial scope challenge" below) before any in-scope correctness review.
5. Iterate until the review has no High findings and no new design blockers.
6. Then execute (code, draft, revise, deploy).
7. Run the normal review cycle on the staged output. It is typically smaller because the architecture was already validated.
8. After the work ships or is submitted, delete the PLAN file.
### Adversarial scope challenge (mandatory in plan-review)
Plan-review **must** be adversarial about plan purpose and shape, **not** about low-probability edge cases. The single biggest plan-review failure mode in this maintainer's history was not "the plan had a bug" — it was "the plan's scope was over-conservative, deferring user value across an extra release cycle worth of process tax." Examples:
- Conservative scope (`v0.5.7 = ref bump only; defer compact to v0.6.0`) → the user's real projects would have stayed on large `AGENTS.md` files for another release cycle. The apparent product ask was a one-line bundled manifest flip, but existing-consumer delivery also required bundled-default drift detection in aa plus real-project upgrade tests. The scope challenge should force reviewers to price both parts: user value now, and the smallest code path that actually delivers it.
- Conservative deferrals also defer the validation that proves the next-step works in real consumers, so the next release inherits the same uncertainty plus a longer review chain.
The plan-review prompt must instruct the reviewer to take an explicit position on:
1. **Why this exact scope?** What user pain (or user opportunity) does THIS scope close? Could a strictly smaller scope close most of it? Could a marginally larger scope close all of it for low marginal cost?
2. **Are deferrals justified?** Every "out of scope / deferred to vNext" carries process tax: another full plan-review + implement + execution-review + CI + publish cycle. Quantify the deferral. If the deferred axis is 1 line of code and the user pain is real, the deferral is probably wrong.
3. **Is this plan the simplest path?** Are there simpler approaches the plan didn't consider — including doing nothing if the user pain is hypothetical?
Plan-review must **not** default to "no blocker, ready to implement" on the first round when scope is multi-axis or when the plan defers a user-facing axis. The reviewer must either raise a scope challenge OR explicitly state why the scope is calibrated. Skipping the explicit position on these three questions counts as an incomplete review.
Anti-patterns the reviewer must NOT pursue:
- **Edge-case fishing**: "what if user has unusual config Y?" — only matters when realistic probability × impact > review cost.
- **Process ritual**: "should this also do Z?" without grounding in user pain or opportunity.
- **Adversarial-for-its-own-sake**: rejecting plans whose scope IS calibrated, or proposing scope expansion that adds cost without proportional user benefit.
The Phase 1c prompt template `Scope-challenge focus:` line below carries this contract into every plan-review send.
### Illustrative examples (not exhaustive; the category is less important than the pattern)
- System / code: hook or infra design, cross-cutting refactor, state-file schema, cross-platform behavior, release runbook revisions.
- Research output: paper outline with specific aims, contribution claims before methods is written, figure-placement vs argument flow, reviewer response strategy, experiment design across multiple methods, ablation plan.
- Proposal: full outline (aims alignment with merit-review criteria), budget-narrative coupling, broader-impacts framing.
- Operational: migration plan, incident-response playbook, data-pipeline redesign.
- Administrative / teaching: course syllabus structure, lab policy document, committee process design.
The point is not which category -- it is whether the shape of the work precedes and constrains the execution.
### Empirical note
In the agent-config 0.1.9 release cycle, two plan-review rounds caught a High-severity design flaw before implementation. The later execution-review rounds were limited to documentation and test polish, avoiding a likely post-ship hotfix.
## Codex Channels
Three paths to Codex are supported. Default is Terminal-relay (manual copy-paste). Auto-terminal (`codex exec` subprocess) is opt-in via the trigger rules in Path selection below. Plugin is user-initiated. The skill picks the channel based on those rules.
### Terminal path (default)
The user has a Codex interactive terminal window open alongside Claude Code. Claude Code prepares a copy-pasteable review prompt (summary, diff, lens, round number) and presents it as a fenced text block. The user copies it into the Codex terminal, then relays the feedback back to Claude Code.
### Auto-terminal path (opt-in, codex exec subprocess)
When the user opts in via the trigger rules in Path selection below, Claude Code dispatches Codex via `codex exec --sandbox danger-full-access` as a background subprocess (Bash on POSIX, a transient `<state-dir>/run-codex.cmd` helper invoked through `cmd /c` on Windows; see the Script contract invariants subsection for the rationale). The dispatch script writes the prompt to a per-dispatch temp file under `%TEMP%` / `$TMPDIR`, feeds it on stdin (`codex exec -`), and emits a state-dir path to stdout for Phase 2 to consume. Codex writes its review to `Review-Codex.md` per the save contract; the same Phase 1d auto-watch fires on file appearance.
**Trust model and the sandbox flag**: Auto-terminal aligns its trust model with Terminal-relay by passing `--sandbox danger-full-access` to `codex exec`. In Terminal-relay the user is typing into their own Codex window with full fs / network / shell access; in Auto-terminal Claude Code starts the same Codex on the user's behalf, so granting the same access keeps the two channels behaviorally equivalent. The sandbox flag is also a hard requirement for the channel to work on Windows: Codex 0.130.0's default `workspace-write` sandbox runner hits `CreateProcessAsUserW failed: 1312` when Codex spawns its own git / grep / pwsh subprocesses, which makes the review come back as "could not access files" and surfaces through Phase 2.0 Check 7 / 8 / Substance-2 as a tool-sandbox failure. Scope discipline (review-only, save findings to `Review-Codex.md`, no commits / pushes / branch operations) is enforced at the prompt level, identical to how Terminal-relay enforces it. For CI / shared / multi-tenant environments where this trust posture is too broad, set `CODEX_DISPATCH_SANDBOX` to a stricter mode (`workspace-write` / `read-only`); the dispatch script honors the env var.
**Backend permission parity**: Reviewer selection changes the model family, not whether the reviewer can validate its claims. Every Auto-terminal backend runs unattended with its backend's broad execution switch: Codex `danger-full-access`, Agy `accept-edits` plus `--dangerously-skip-permissions`, Claude `bypassPermissions` plus `--tools default`, and Copilot `--allow-all`. Each may run tests, experiments, benchmarks, shell commands, and necessary network verification. The review prompt, not a crippled tool allow-list, keeps commit, push, publish, external-system mutation, and destructive cleanup out of scope. Agy and Claude perform potentially write-producing verification in disposable staged snapshots when export succeeds, so ordinary experiment artifacts do not touch the source checkout.
The primary Auto-terminal dispatch preserves the byte-identical prompt invariant with Terminal-relay: the assembled prompt bytes are the same regardless of channel. The extra `-c developer_instructions=...` value is a Codex CLI configuration argument, separate from the review prompt on stdin, so it does not change those bytes. The dispatch script must NOT call `codex exec review`, `codex exec review --uncommitted`, or any other Codex `exec` subcommand, because those carry Codex's own built-in review prompt template which would compete with the skill's lens-aware prompts. The embedded-diff retry described below is the explicit exception: it is used only after a health-checked Auto-terminal run shows that Codex's own shell tool cannot inspect the repo, usually because `CODEX_DISPATCH_SANDBOX` has been narrowed in a CI / sandbox-strict environment, but also for the same class of failure if a future Codex version regresses under the relaxed default sandbox.
The Auto-terminal path requires `codex` on PATH. Probe before dispatch; if absent, warn and take the next probed entry of the Phase 1c failover queue, reaching Terminal-relay only when none is available. Phase 1c's backend-probe step is the single authority for a backend that is absent, as its failover paragraph is for one that failed. A non-zero `codex exec` exit, a timeout, or a stdin-pipe failure (rare on Windows with PowerShell quirks or Bitdefender input-stream interception) is a confirmed failure; route it through the failover queue in Phase 1c, which is the single authority for that destination. Do NOT add a truncated positional-argument fallback, which would break the prompt invariant. Sticky downgrade applies session-wide once that queue is empty: subsequent rounds default to Terminal-relay until the user re-opts-in explicitly.
**Auto-terminal has no incremental-checkpoint guarantee.** The save contract requires a complete per-round review published atomically, but it does not require intermediate checkpoints. The Codex dispatcher snapshots the prior review mtime and propagates `codex exec`'s exit code, and it does not validate that a fresh review was published. A child that exits 0 without saving therefore leaves `<state-dir>/tail` and no fresh review, which Phase 2 health checks 1 through 3 reject as missing, stale, or wrong-round. That rejection costs a full Codex run.
**Configuration boundary: the dispatched Codex inherits only the `model` of the user's `config.toml`**. Under the default MCP isolation, the dispatch script passes `--ignore-user-config` and re-passes `-c model=<M>` and `-c model_reasoning_effort=$CODEX_DISPATCH_REASONING`. `M` is `CODEX_DISPATCH_MODEL` when set. Otherwise it is the top-level `model` in `$CODEX_HOME/config.toml` (default `~/.codex`), so a model switch there reaches reviews without a repository edit. Failing both, it is the Codex model `AGENTS.md` names. A config value counts only when it is quoted and made of `A-Za-z0-9._:/-`. Under a top-level `model_provider` other than `openai`, the config model is set aside too: isolation drops that provider, and its model names would reach the default one. Such a setup needs `CODEX_DISPATCH_MODEL` or `CODEX_DISPATCH_ISOLATE_MCP=off`. `<state-dir>/review-model` records the model and which source won. Reasoning defaults to `xhigh`. Independently of that isolation choice, every dispatch passes a separate `-c developer_instructions=...` argument telling the child reviewer to skip bootstrap and shared configuration refresh commands and to use the configuration already on disk. The child reads the injected `AGENTS.md` chain (Codex discovers one instruction file per directory: `AGENTS.override.md`, then `AGENTS.md`; it does not discover `AGENTS.local.md` or `agents/codex.md` on its own), and Codex injects at most `project_doc_max_bytes` of it, a 32 KiB default that a composed consumer file exceeds. Every dispatch therefore also passes `-c project_doc_max_bytes=262144`, outside the isolation branch, so the whole file reaches the reviewer with isolation on or off; a value set in `~/.codex/config.toml` alone would not survive `--ignore-user-config`. All project instructions in that file, including the agent-style banned-word list and any NSF rules, remain in force. Everything else in `~/.codex/config.toml` is dropped for the dispatched run: `model_reasoning_effort`, `service_tier`, `[features].fast_mode`, a profile's settings, and any custom provider settings. The MCP-isolation fix drops these settings on purpose, which is easy to miss. Under default isolation, the reasoning setting in `config.toml` does not reach reviews. Set `CODEX_DISPATCH_MODEL` or `CODEX_DISPATCH_REASONING` to change a review. To hand the dispatched run the full user config instead, set `CODEX_DISPATCH_ISOLATE_MCP=off`, which is the documented opt-out and also restores the user's MCP servers and plugins.
**Reasoning effort is a one-way ratchet**: `xhigh` is the floor, not a target. The dispatching agent may raise a single round to `max` by passing `CODEX_DISPATCH_REASONING=max` on that dispatch when it judges the change under review hard enough to earn deeper reasoning: changes that span mirrored copies of one file, touch caching or state lifetime, or would fail silently rather than loudly. It must never pass a value below `xhigh`. Raising the tier is not free: it roughly doubles wall-clock, so raise the dispatch timeout in the same step, or the round is killed before it writes anything. The asymmetry is deliberate. An agent choosing how hard to review its own work creates a self-grading loop, and its confidence is highest exactly where a quiet error hides. Its discretion therefore runs upward only, so the worst case is a round that was not deepened rather than one that was weakened.
**Depth against another round**: when the budget would buy either one deeper round or two at the floor, prefer another round while the artifact is still changing. The later round reads the revised state and carries the prior findings with it, so its budget is spent on what the last round actually produced. Raise the tier for the closing round, or when the change is subtle enough that a first read is unlikely to reach it. The relative yield of the two has not been measured, so treat this as a default rather than a finding.
**Python interpreter resolution and the verification contract**: Before it creates dispatch state or spends a Codex round, `dispatch-codex` finds a working Python interpreter in this order: `ANYWHERE_AGENTS_PYTHON`; project `.venv` or `venv`; the activated `VIRTUAL_ENV`; active and discovered conda or Miniforge roots and environments; `py -3`; then every `python3` and `python` entry on PATH. A configured but broken `ANYWHERE_AGENTS_PYTHON` is a hard preflight failure. Every candidate must run an isolated probe, return an absolute `sys.executable`, pass the nonempty-file and non-`WindowsApps` checks, and execute successfully again through the resolved path. The dispatcher writes the absolute path to `<state-dir>/python-interpreter` and includes it in the child `developer_instructions`; the reviewer must use that exact path for Python commands. Resolving nothing at all is a degradation rather than a dispatch failure: the dispatcher warns on stderr, omits the marker file so its absence is unambiguous, tells the reviewer that no interpreter was pre-resolved, and proceeds. The probe exists to keep a reviewer from claiming verification it never ran, and check 10 enforces that independently by refusing a `PASS` or `BLOCK` verdict without a `VERIFIED` status. Reviews also run on repositories whose verification is not Python at all, so a missing interpreter must not make those repositories unreviewable.
**PowerShell `7` (`pwsh`) resolution**: The probe enumerates `PATH` only because Codex's tool router shells out to `pwsh` by name. It rejects a candidate when its path contains `WindowsApps` or when the file has zero length. This is a structural check rather than an execution probe because a Microsoft Store App Execution Alias succeeds from an interactive shell and fails only under the dispatched spawn context, where it dies with NTSTATUS `0xC0000142` (`-1073741502`) in about `30 ms` and is retried indefinitely. The dispatcher writes a resolved absolute path to `<state-dir>/pwsh-interpreter` and names it in the child `developer_instructions`. Finding nothing is a soft degradation that tells the reviewer to prefer bash, Windows PowerShell `5.1` (`powershell.exe`), or plain git, as it does for a missing Python interpreter. The Copilot and Claude reviewer backends pass no developer instructions today, so they carry neither the Python nor the `pwsh` probe; extension to those backends is deferred.
Every saved review must include one standalone `Verification status: VERIFIED` or `Verification status: UNVERIFIED` line. `VERIFIED` is permitted only after at least one relevant verification command completed, and Verification notes must list the exact commands and outcomes. A run with no completed verification uses `Verification status: UNVERIFIED` together with `Commit verdict: UNVERIFIED`; it cannot issue `PASS` or `BLOCK`. Health check 10 rejects an `UNVERIFIED` status or verdict, a `PASS` or `BLOCK` verdict without `VERIFIED`, conflicting markers, and `VERIFIED` paired with notes that explicitly report no execution. A caller can therefore distinguish an execution-backed block from an unsupported one: `PASS check-10 verdict=BLOCK verification=VERIFIED` plus concrete command outcomes is admissible; `FAIL check-10 verdict=BLOCK verification=MISSING` or `FAIL check-10 verdict=BLOCK verification=UNVERIFIED` is a hard intake failure. Review formats that give no labeled verdict remain backward compatible in the health check. For a `Commit verdict` or `Plan verdict` label, the health check takes the value from anywhere after the label or from the first nonempty line within the next four. A bare `Verdict` label, including a heading, counts only when its value is the first word, and the next line is read only when the label stands alone.
**Embedded-diff retry for Codex tool-sandbox failure (sandbox-strict environments only)**: With the default `--sandbox danger-full-access` Codex can inspect the repo directly and this retry path should never fire. The retry exists as a defensive fallback for two cases: (1) `CODEX_DISPATCH_SANDBOX` has been narrowed to `workspace-write` or `read-only` in a CI / shared environment, and Codex hits `windows sandbox: runner error: CreateProcessAsUserW failed: 1312` or a similar internal sandbox error; (2) some future Codex version regresses the same class of failure under the relaxed sandbox. Symptom: dispatch exits 0 and `Review-Codex.md` is fresh, but the review body says Codex could not run `git diff --cached` or inspect files; Phase 2.0 flags this through Check 7, Check 8, or Substance heuristics. Procedure: retry Auto-terminal once with an embedded-diff prompt before downgrading to Terminal-relay. Generate the diff from Claude Code's shell by running the exact diff command chosen in Phase 1b, and include it after the normal save contract, summary, lens, focus, and prior findings. Mark the round line with this content: `Round N (RETRY: codex-exec sandbox blocked tool access; diff is embedded inline below.)` Then add this sentence: Do NOT run `git diff` or any shell command -- use the embedded diff as the source of truth. The retry may be larger than the primary prompt; keep it limited to the Phase 1b in-scope paths and do not include unrelated staged or working-tree content. Run the same dispatch, auto-watch, and health-check sequence on the retry. **Important quality caveat**: the retry sees only the embedded diff hunks, not the surrounding file context. Treat retry findings as **diff-scoped** — they are reliable for hunk-self-contained issues (test coverage gaps, naming, in-hunk regressions) but prone to false positives for claims that depend on non-diff code (e.g., a "this is missing X" finding when X actually exists elsewhere in the file). Because of this elevated false-positive rate, **Medium findings from a retry-channel review are subject to mandatory Phase 2.5 verification before being applied** (see Phase 2.5 trigger). If the retry produces a usable review, proceed with that review under the Medium-verify discipline; if it also fails or only records blocked verification, treat that as a confirmed failure and route it through the Phase 1c failover queue.
**Script presence is also required**: if any of the `dispatch-codex` / `health-check` / `stall-watch` scripts for the current platform is missing (e.g., Phase B has not landed yet in a given checkout), this path is documented-only — Phase 1c automatically downgrades to Terminal-relay **without** setting sticky downgrade, because the absence is design state, not a runtime failure. See the "Auto-terminal path (opt-in)" subsection in Phase 1c for the script-presence probe specifics.
**Self-review guard**: `dispatch-codex.{sh,ps1}` exits 2 with the message `dispatch-codex: refusing to dispatch (orchestrator=codex; self-review)` when `IMPLEMENT_REVIEW_ORCHESTRATOR` is `codex` (case-insensitive). It also exits 2 when that variable is unset or empty and `CODEX_THREAD_ID` or `CODEX_SESSION_ID` is set, which Codex does in its tool subprocesses. Any other orchestrator value proceeds. Phase 1c step 5 tells coordinators to set the variable on every dispatch; a launch that omits it reaches the fallback. The PowerShell check lives in the sibling `_codex_guard.ps1` for the same AV reason as `_claude_guard.ps1`. This guard exists because a Codex coordinator must never dispatch Codex. On a case-insensitive filesystem, the `dispatch-Codex.sh` named by a skill copy with Claude rewritten to Codex resolves to `dispatch-codex.sh`.
### Auto-terminal Copilot backend (cross-vendor reviewer, opt-in)
The Auto-terminal channel has a second dispatch backend that runs **GitHub Copilot CLI** as the reviewer instead of `codex exec`. It exists for the agent-fungibility case in `AGENTS.md` § "Agent Fungibility": when Claude Code is unavailable and Codex (or the user) is the primary implementer, Codex cannot review its own work and Claude is absent, so Copilot is the reviewer. It is **opt-in only** via the triggers in Path selection below; a bare `/implement-review auto` still selects Codex.
The backend is a drop-in. Apply the entire **Auto-terminal path (opt-in)** mechanics below — script-presence probe, prompt assembly, dispatch invocation, `STATE-DIR` capture, Phase 1d auto-watch, the Phase 2.0 Health checks, Substance heuristics, and Phase 1d coordination — with two substitutions and one probe swap:
- `dispatch-codex.{ps1,sh}` → `dispatch-copilot.{ps1,sh}`
- expected review file `Review-Codex.md` → `Review-GitHub-Copilot.md`
- the `codex` PATH probe → a `copilot` PATH probe (falling back to `gh copilot`)
Everything else is reviewer-agnostic: the `STATE-DIR` contract, the flags passed to `health-check` and to `await-review` (`--state-dir`, `--round`, `--review-file Review-GitHub-Copilot.md`), the stopped-task resolution rule, the silent-advance gating, and the downgrade / sticky-downgrade rules are identical. The save contract already normalizes `GitHub Copilot` → `Review-GitHub-Copilot.md` (Phase 1c), so Phase 2 intake needs no change.
Copilot-specific dispatch details, all handled inside `dispatch-copilot` (no caller change):
- **Deployed-script release**: Before argument parsing, preflight, or reviewer work, `dispatch-copilot` re-executes from a private temp copy. It uses the shared `IMPLEMENT_REVIEW_DISPATCH_REEXEC` sentinel and `IMPLEMENT_REVIEW_DISPATCH_SOURCE_DIR` source-directory handoff, removes the copy after exit, and propagates the child status. Preflight and the main run still resolve `stall-watch` from the deployed source directory.
- **Prompt delivery**: the assembled prompt (byte-identical to the Terminal-relay block) is written to a temp file and passed as `-p "@<prompt-file>"`; Copilot reads the file as its prompt. A long literal `-p` argument fails, so the file reference is mandatory -- this replaces Codex's `exec -` stdin feed.
- **Binary resolution**: prefer standalone `copilot`; fall back to `gh copilot` when `copilot` is not on PATH. Override with `COPILOT_BIN` / `GH_BIN`.
- **Trust posture**: `--allow-all` grants tools, filesystem paths, and URLs without confirmation, while `--no-ask-user` keeps the run unattended. This matches the verification capability of the other Auto-terminal reviewers, including shell experiments and current-fact checks. Scope discipline remains prompt-enforced: review and verify, but do not commit, push, publish, mutate external systems, or run destructive cleanup. Copilot writes `Review-GitHub-Copilot.md` itself per the save contract, exactly as Codex writes `Review-Codex.md`.
- **Pager**: `GIT_PAGER=cat` is set so Copilot's own `git diff` does not stall on a pager.
- **Auth preflight**: With the official CLI and the default `COPILOT_PREFLIGHT=auto`, each dispatch first sends a small live prompt that runs `git --version` and must return `COPILOT_PREFLIGHT_OK` before the full review starts. This checks the stored credential, network path, JSON stream, and git-shell permission. It costs one small Copilot request per dispatch because Copilot CLI 1.0.78 exposes no non-interactive auth-status command. `COPILOT_PREFLIGHT=force` applies the same check to a wrapper; `COPILOT_PREFLIGHT=off` skips it.
- **Non-interactive stream**: `--no-ask-user --no-auto-update --stream on --output-format json --no-color` keep the run unattended and expose live tool events to `stall-watch` and postflight. `--no-auto-update` prevents an in-flight CLI self-update.
- **Postflight**: For official CLI runs, and wrappers checked with `COPILOT_PREFLIGHT=force`, a tool permission denial or known authentication or shell-runner failure rejects the review with exit 70. A nominally successful run also exits 70 unless the expected review file exists, is at least 500 bytes, starts with the exact `<!-- Round N -->` marker, and has an mtime strictly newer than both dispatch start and any pre-dispatch copy.
Copilot inspects the repo directly, so there is no embedded-diff retry unique to Copilot; the same Phase 2.0 checks apply with the review-file name swapped.
### Auto-terminal Agy backend (Gemini through Antigravity CLI, opt-in)
The Agy backend gives the review loop an independent Google model family. It runs the official Antigravity CLI in headless mode. Use `agy` as the canonical reviewer token because it names the CLI and is fast to type; `gemini` and `antigravity` remain accepted aliases. When `IMPLEMENT_REVIEW_DEFAULT_CHANNEL=auto`, `/vet agy` is the complete invocation; no redundant `auto` token is required. With no reviewer token, the Auto-terminal default remains Codex.
Dispatch uses `<python> dispatch-gemini.py --prompt-file <path> --round <N> --expected-review-file Review-Antigravity.md`. The script is cross-platform and handles these details:
- **Binary resolution**: prefer `ANTIGRAVITY_BIN`, then `agy` on PATH. On Windows, also check `%LOCALAPPDATA%\agy\bin\agy.exe`, because an installer updates the user PATH registry but cannot update an already-running Claude Code process.
- **Default reviewer family**: default to the newest `gemini-*-flash-high` the signed-in account lists, at `high` effort. That is the highest effort the tested Antigravity CLI 1.2.0 exposes for this family. `DEFAULT_MODEL` (`gemini-3.8-flash-high`) is a family template: the preflight's `agy models` listing floats its version, so a new Flash release is picked up without an edit. A `MODEL-RESOLVE from=... to=...` line on stderr records the switch. With `ANTIGRAVITY_PREFLIGHT=off` there is no listing, and the template runs as written. A model named in `ANTIGRAVITY_DISPATCH_MODEL` runs verbatim; `ANTIGRAVITY_DISPATCH_EFFORT` overrides the effort. The Flash family matches Agy 1.2.0's default and one local same-prompt paper-review comparison in which it was more complete than `gemini-3.1-pro-high`. Treat that single comparison as routing evidence, not a general benchmark.
- **Preflight**: `ANTIGRAVITY_PREFLIGHT=auto` checks `agy --version`, `agy models`, authentication, and exact model availability before spending a review request. `force` has the same behavior for wrappers; `off` skips it. `ANTIGRAVITY_PREFLIGHT_TIMEOUT_SECONDS` defaults to 60.
- **Executable verification**: the dispatcher exports the Git index to `<state-dir>/staged-snapshot`, appends `git diff --cached --no-ext-diff` to a relay prompt, and runs Antigravity with `--mode accept-edits --dangerously-skip-permissions --add-dir <staged-snapshot>`. Agy's tool shell starts in its own scratch directory even when the process `cwd` points elsewhere, so the relay names the exact snapshot path as the place for source-file tools and verification commands and requires shell commands to change to it first. The relay also states that the embedded diff is the review input, so the reviewer does not obtain a second copy from the snapshot or `git diff`, and that unrelated `.gemini` and `.system_generated` material is skipped. Gemini can run tests, experiments, benchmarks, shell commands, and network verification, including tools that create normal generated artifacts inside the disposable snapshot. It must not recreate files from the embedded diff if snapshot access fails; that condition is a blocked verification. The prompt still forbids commit, push, publish, external-system mutation, and destructive cleanup. Gemini is never given the original worktree as an added directory: an unsuccessful diff capture, Git-root lookup, or snapshot export aborts the dispatch and records the reason in `git-diff.stderr`. The export runs under a per-command `core.longpaths=true`, because the state-dir prefix plus a long indexed path crosses the 260-character Windows limit and the export then fails with `Filename too long`. The whole index is exported on purpose: the snapshot has to stay a working copy the reviewer can run the repository's own verification inside, not only the changed files.
- **Supplied inputs**: the snapshot is the exported index, so a gitignored `PLAN-*.md`, an untracked evidence note, or an untracked local override never reaches it. The coordinator pastes each such input into the request once under a `--- SUPPLIED INPUT: <path> ---` heading (plan-review step 3); the relay tells the reviewer to read those copies and never the original worktree, and supplied policy stays distinct from repository material, which the relay treats as untrusted.
- **Long-prompt transport**: the relay goes over NDJSON stdin with `--input-format stream-json --output-format stream-json`; it never becomes a Windows command-line argument. `ANTIGRAVITY_DISPATCH_TIMEOUT_SECONDS` defaults to 2700 and is passed as `--print-timeout`.
- **Atomic publication**: Antigravity returns the complete review in its final result event. The dispatcher normalizes the current round marker, validates a substantive response, and atomically publishes the backend-stable `Review-Antigravity.md`. Stdout remains the single `STATE-DIR <abs-path>` line; model events and errors stay in `tail` and `tail.stderr-tmp`.
- **Result status**: after the CLI exits 0, an explicit, nonblank result status other than `SUCCESS` is rejected with exit 70, unless the recovery below applies. A missing or blank status keeps the legacy success behavior. Antigravity exits 0 when it stops on a quota limit, and that event still carries the model's opening narration, which would otherwise become the round's review. The recovery covers a response with review structure. That structure is this round's marker on its own line, followed by a standalone `Verification status:` line and a labeled verdict, the lines the health check reads. Lines inside closed triple-backtick blocks or single-line inline code do not count, the same exclusions the health check applies. A tilde fence or an unclosed fence is not recognized as quoted code. A server error such as `UNAVAILABLE (code 503)` can end a run after the review is written, and on 2026-09-15 four finished reviews were discarded that way. Such a response is published, and the dispatcher names the error on stderr and in `<state-dir>/backend-failure`. The structure makes a recovery candidate. It is not a complete Markdown parse, and it does not show the findings are finished. Check 8 therefore warns whenever `backend-failure` is present. Phase 2 reads the error reason and inspects the recovered review for placeholders, narration, and truncated findings before accepting its verdict.
- **Expected duration**: a review of a large diff often takes 15 to 25 minutes, because Agy builds and tests inside the snapshot and its tail can stay silent for minutes at a time. A `STALL` at 600 seconds, or `ALIVE` from `await-review`, is expected in that window. Keep waiting until the round deadline.
- **Residual-process guard**: the shared `stall-watch` starts with its working directory inside the state directory, not the reviewed repository. If the dispatcher disappears, the watcher uses its existing identity-checked parent/worker contract. Silence alone still never kills a reviewer.
- **Self-review guard**: direct calls refuse `IMPLEMENT_REVIEW_ORCHESTRATOR=agy`, `gemini`, or `antigravity`. Path selection must rewrite that case to Codex before dispatch.
The old `gemini` CLI is not this backend. Personal Google AI Pro and Ultra subscription access is routed through `agy`; using a Gemini API key or a Google Cloud project is a separate billing path.
### Auto-terminal Claude backend (cross-vendor reviewer, opt-in)
A second cross-vendor backend mirrors the Copilot one with the reviewer swapped: **Codex (or the user) implements, headless Claude Code (`claude -p`) reviews.** Use when Claude is preferred as the reviewer voice, the human orchestrator runs Codex as the primary implementer, or fungibility scenarios where Codex cannot self-review and Copilot is absent. The Claude backend is **opt-in only**: a bare `auto` / `cli` token still selects Codex.
**Self-review guard (mechanical).** Path selection treats the invoking runtime as authoritative. When this skill is running inside Claude Code, the orchestrator is `claude`; `/implement-review auto claude` is rewritten to Codex (or Copilot if Codex is unavailable on PATH) BEFORE any `dispatch-claude` script is launched, with one user-visible line: `Claude backend skipped: Claude Code cannot review its own implementation; using <backend> instead.`
The script-level guard in `dispatch-claude.{sh,ps1}` enforces the same refusal for direct callers that bypass SKILL.md path selection. The scripts exit 2 with the message `dispatch-claude: refusing to dispatch (orchestrator=claude; self-review)` when ANY of these holds:
- `IMPLEMENT_REVIEW_ORCHESTRATOR=claude` (case-insensitive), OR
- `IMPLEMENT_REVIEW_ORCHESTRATOR` is unset/empty AND `CLAUDECODE=1` (Claude Code's documented subprocess marker, set in Bash / PowerShell / hook / tmux / status-line subprocesses).
`IMPLEMENT_REVIEW_ORCHESTRATOR=codex` and `IMPLEMENT_REVIEW_ORCHESTRATOR=user` proceed regardless of `CLAUDECODE`. The `CLAUDECODE=1` fall-through exists so a direct script launch from a Claude Code session without setting `IMPLEMENT_REVIEW_ORCHESTRATOR` does not bypass the guard. The path-selection rewrite + script-level env exit together make the refusal mechanical at both layers; prose alone is not enforceable.
Dispatch detail mirrors the Copilot backend with a relay-save variant:
- **Probe**: `claude` on PATH (override via `CLAUDE_BIN`); no fallback binary.
- **Deployed-script release**: Before it creates dispatch state or starts a preflight, `dispatch-claude` re-executes from a private temp copy. `IMPLEMENT_REVIEW_DISPATCH_REEXEC` prevents a loop, and `IMPLEMENT_REVIEW_DISPATCH_SOURCE_DIR` retains sibling-script lookup at the deployed source directory. The wrapper removes the copy and propagates the child exit status.
- **No-model preflight**: `CLAUDE_PREFLIGHT=auto` (the default) runs `claude auth status --json` for the official CLI and skips opaque wrappers. API-key, gateway, Bedrock, Vertex, Foundry, and bare modes use `claude --version` as an availability probe because first-party login state does not describe those credentials. `force` applies the probe and strict postflight checks to a wrapper; `off` skips the probe. `CLAUDE_PREFLIGHT_TIMEOUT_SECONDS` sets the positive-integer timeout, default 60 seconds. A rejected preflight exits 70, or 124 on timeout, before a model request is sent.
- **Invocation**: `claude -p --permission-mode bypassPermissions --tools default --add-dir <staged-snapshot> --setting-sources project,local --effort max --strict-mcp-config --mcp-config <state-dir>/empty-mcp-config.json --output-format text`. This gives Claude the same unattended test, experiment, shell, write-tool, and web-verification capability as the other reviewers. The empty MCP config is still required for cross-agent review: a user-level Codex MCP server must not auto-start inside the Claude reviewer backend, or the reviewer can recurse into Codex and hang with empty output. Excluding user settings also keeps user-level hooks out of the headless review path. It also drops the effort set in their env block. Without another applicable effort setting, the reviewer would use the model's default, so the dispatcher passes `--effort max`. The flag requests `max` review effort; `CLAUDE_CODE_EFFORT_LEVEL` in the environment or an effort cap can change the applied level. `CLAUDE_DISPATCH_BARE=1` may opt into `--bare` only in environments that provide `ANTHROPIC_API_KEY` or an explicit `apiKeyHelper`; do not enable it by default because Claude Code 2.1.153 disables OAuth and keychain auth in bare mode. No `--sandbox` flag (Codex-only). The long prompt is fed via stdin (POSIX redirection; PowerShell `ProcessStartInfo.RedirectStandardInput`, avoiding ARG_MAX), but `dispatch-claude` first wraps it in a relay prompt. This embedded diff covers every staged path, because the dispatcher has no scope argument; use the Claude backend only when the entire staged set is in scope. The dispatcher exports the Git index to `<state-dir>/staged-snapshot` with `git -c core.longpaths=true checkout-index -a`, which keeps the export from failing on a path past the 260-character Windows limit, and launches Claude from that directory; if export fails or the directory is not a git worktree, it falls back to the original working directory and records the reason in `git-diff.stderr`. The relay permits tests and experiments, including generated-file writes inside the disposable snapshot, while forbidding commit, push, publish, external-system mutation, and destructive cleanup. The dispatcher writes Claude stdout to `Review-Claude-Code.md`; stderr remains in `<state-dir>/tail.stderr-tmp` for diagnostics. On save, the dispatcher trims any preface before `<!-- Round N -->`; if Claude omitted the marker, the dispatcher inserts it so Phase 2 intake has a stable first line. `--strict-mcp-config` plus the empty MCP config keeps user MCP recursion isolated without reducing built-in verification tools. **PowerShell side note**: `dispatch-claude.ps1` decomposes its self-review env-check into a sibling `_claude_guard.ps1` helper alongside it. Some Windows AV products (e.g., Bitdefender) score the combination of env-check + stderr-write + non-zero exit + cmdBody construction in a single `.ps1` as a malicious-orchestration signature and AMSI-block the file at parse time. Splitting the guard into a tiny helper file decouples the two patterns and clears the block on a typical Bitdefender install. `dispatch-claude.sh` keeps the guard inline because POSIX AV scanners do not have the same heuristic.
- **Live tails and transport rejection**: stdout and stderr flow into `<state-dir>/tail` and `<state-dir>/tail.stderr-tmp` while Claude is running, so `stall-watch` sees liveness before process exit. After a nominal exit 0 from an official or forced run, known authentication, API, stream, connection, timeout, rate-limit, and overload errors in stderr write `<state-dir>/transport-failure`, reject the review, and exit 70.
- **Atomic publication**: Claude stdout stays in the state directory until the process exits successfully. The dispatcher builds and validates a candidate beside the expected review file, then uses a same-directory rename or replacement. Auto-watch therefore sees the old review or the complete new review, never a partially written target.
- **Codex stream-death classifier is Codex-specific**: It requires a `chatgpt.com/backend-api/codex/responses` error followed by Codex's `tokens used` trailer. Claude's `--output-format text` stream has no equivalent terminal record, so that immediate classifier was not ported. Claude uses the stderr transport postflight after process exit. The shared watcher still records 600 seconds of silence and performs identity-checked reaping when the dispatcher exits.
- **Expected review file**: `Review-Claude-Code.md`. Phase 1c name normalization already maps `Claude Code` to `Review-Claude-Code.md`; Phase 2 intake needs no change.
- **No embedded-diff retry.** The primary Claude relay already includes the staged diff, so the Codex retry pattern for narrowed sandbox failures is not applicable.
State-dir, stdout contract (`STATE-DIR <abs-path>` single line), state files (`pre-mtime` / `timestamp` / `tail` / `tail.stderr-tmp`), and the `stall-watch` launch match the shared dispatch contract. For Claude, tail growth supplies liveness; the Codex-only terminal suffix is not a Claude completion record.
### Codex waiting for the Claude backend (App and Terminal)
**Scope:** This subsection applies only when Codex is the coordinator and Claude Code is the current reviewer. It overrides conflicting waiting instructions below only for that pair, in Codex App or Terminal. Other coordinator/reviewer pairs keep their existing dispatch, waiting, health-check, and failover behavior. In a mixed `/vet claude agy` queue, apply this subsection to the Claude slot only; handle the Agy slot under the existing rules. Under Codex, `/vet both` resolves to that queue.
Start `dispatch-claude` through a resumable exec tool and retain its handle and emitted `STATE-DIR`. Claude's text output can stay empty until the final response. Empty tails, `ALIVE`, and a pre-publication `STALL` therefore leave this review pending, even after 15 minutes. A tool yield or expired wait window does not establish reviewer failure. Do not conclude the review, drop the Claude slot, or cancel the dispatcher/reviewer merely to end the turn, including through `terminate: true`, Ctrl-C, or `kill`. Coordinator cancellation is not a backend failure that permits Single-source completion.
**Low-token waiting:** Prefer a confirmed native completion notification if the host provides one. Otherwise keep the turn open and wait directly on the retained Claude dispatcher session with empty-input `write_stdin`; it returns on success or failure, including failure without a report. Before the first wait, use `await-review.py --timeout 0` with this dispatch's state directory, round, and absolute `Review-Claude-Code.md` path to establish the original deadline. Request `yield_time_ms=180000` when supported and permitted by host instructions, capped by the remaining round budget and any smaller tool or host limit. Output or completion can return early, so this is a maximum wait window, not a fixed three-minute delay. If an outer execution cell yields earlier, retain and resume that cell without terminating it. On each completed tool wait, consume any dispatcher exit status first: a non-zero exit is a confirmed failure under the existing routing; exit 0 requires the existing report and health checks. While the dispatcher is still pending, make one brief `await-review.py --timeout 0` check for publication or the deadline, then continue the same dispatcher handle unless a documented checkpoint is reached. `ALIVE` never overrides an observed dispatcher exit. If the handle is lost, use Phase 1d's existing resolver recovery, with the original deadline. Do not wait exclusively on a long-running resolver: it cannot observe a dispatcher exit without publication. Do not add a fixed sleep, reread growing logs, or repeat unchanged progress messages. Quiet waiting generates no model tokens; returning to the model still costs tokens. Codex documents a default 300000 ms ceiling for empty terminal polls in the [configuration reference](https://learn.chatgpt.com/docs/config-file/config-reference#background_terminal_max_timeout); the actual exposed tool and host instructions control the usable window.
If the user selects scheduled background monitoring and this host exposes thread heartbeats, use the requested interval instead of concurrent active-turn polling. Confirm registration and save the repository, round, Claude state directory, original deadline, reviewer queue, and unconsumed tool handles before returning an explicitly pending status. Each wakeup uses `await-review.py --timeout 0` and consumes available dispatcher completion status. Stay quiet on unchanged `ALIVE` or `STALL`. Heartbeats are an optional host-specific handoff; do not assume Terminal provides them or promise automatic resumption without a registered mechanism. A background hook alone cannot provide that handoff: [its completion does not start a new turn](https://learn.chatgpt.com/docs/hooks#how-background-hooks-run).
`DONE` or resolver `REVIEW-READY` permits reading the current Claude report and running the existing Phase 2 checks. In sequential mode, start the next reviewer only after the Claude dispatcher returns or identity-checked evidence shows its worker exited; publication alone is not process-completion evidence. In parallel mode, launch the other reviewer after the memory check without waiting for Claude's dispatcher to exit. Consume any available dispatcher exit before marking the Claude slot complete, and route a non-zero exit through the existing failover queue. Preserve the existing health checks and warning checkpoints. A pre-publication silence warning is not permission to abandon the reviewer; assess any saved stall warning after publication under the existing health-check rules. A published report or exit 0 alone is not a final review conclusion.
Use the original 60-minute Phase 1d deadline unless the user specifies another budget, with no shorter harness kill timer. Pass a custom `--round-budget` on the first resolver call, before its immutable deadline is stamped. Preserve the deadline across tool yields and scheduled wakeups. At `TIMEOUT` or `REAP-UNKNOWN`, report the specific incomplete state and pause scheduled monitoring; do not kill the reviewer or invent a new deadline. A user-requested stop also pauses monitoring without treating silence as failure. Stop monitoring this Claude slot after its result is handed to the existing intake/queue flow; do not change how that flow runs the other reviewers.
### Plugin path (IDE sidebar)
Codex runs as an IDE plugin with direct access to the repo. The user tells Codex to review in the plugin sidebar (e.g., "review the staged changes"). Codex can see the repository and run the exact diff command chosen in Phase 1b, so no diff needs to be copy-pasted. Plain `git diff` shows the working tree against the index rather than the staged patch this skill reviews. The user relays Codex's feedback back to Claude Code.
### Path selection
1. **Default**: Terminal-relay (manual copy-paste) on every platform. Users who do not opt into another channel get the current Terminal-relay flow without modification.
- **User-scoped default override**: when the environment variable `IMPLEMENT_REVIEW_DEFAULT_CHANNEL` is set (case-insensitive) to `auto`, `cli`, or `auto-terminal`, which is the same synonym set the slash args accept, Auto-terminal becomes that user's default and no per-invocation opt-in token is needed. Read the variable once at Phase 1c entry. This is a per-user, per-machine setting; set it in the `env` block of `~/.claude/settings.json`. It is deliberately not a change to the shipped default: a consumer that does not set it sees today's behavior unchanged, channel line included. Follows the `IMPLEMENT_REVIEW_ORCHESTRATOR` precedent of a documented, case-insensitive env var read.
- When the variable is unset, empty, or holds an unrecognized value, the shipped Terminal-relay default applies and the variable is never treated as an opt-in, so a typo degrades to today's behavior rather than silently selecting a channel the user did not ask for. The three cases differ only in what is reported: **unset and empty are fully unchanged**, including the channel line, while an **unrecognized value keeps the same channel selection and the same dispatch side effects but adds one clause to the channel line** naming the value that was ignored. That added clause is the single intentional deviation from pre-W3 output, and it exists because a silently ignored typo would otherwise leave the user believing Auto-terminal is their default when it is not.
- The override changes only which channel is the *default*. Every rule in item 2 still applies on top of it: manual override tokens still win, the `codex` PATH probe and the dispatch-script presence check still run, and a failed probe still routes through the Phase 1c backend-probe step.
2. **Auto-terminal opt-in triggers**, evaluated in detection order at Phase 1c entry:
- Mid-flow override toward manual: "back to manual" / "next round manual" / "use terminal-relay" in the user's most recent message forces Terminal-relay.
- Mid-flow opt-in toward auto: "next round use cli" / "next round auto codex" forces Auto-terminal.
- Slash-arg opt-in: `/implement-review cli`, `/implement-review auto`, and `/implement-review auto-terminal` are equivalent synonyms; `auto` is the most natural keyword and is accepted.
When `.claude/commands/implement-review.md` forwards slash arguments through `Command arguments from the slash invocation: $ARGUMENTS`, evaluate that forwarded argument string the same way as the original slash invocation.
Manual override tokens still win before Auto-terminal opt-in tokens.
- Plain-phrase opt-in in the invoking message: case-insensitive substring match on any of `cli mode`, `use cli`, `auto codex`, `use codex exec`, AND no negation word (`do not`, `don't`, `no`, `not`, `avoid`, `manual`) within 4 words before the matched phrase.
- **Reviewer-backend selection (Agy, Gemini through Antigravity CLI, opt-in)**: when the selected channel is Auto-terminal and the slash arguments contain `agy`, `gemini`, or `antigravity`, use `dispatch-gemini.py` and expect `Review-Antigravity.md`. The canonical invocation is `/vet agy`; `/vet gemini`, `/vet antigravity`, the compatible `/implement-review` forms with `auto` or `cli`, and plain phrases such as `use agy as reviewer`, `use gemini as reviewer`, or `review with antigravity` are aliases under the same negation guard. A reviewer token does not need an `auto` token when Auto-terminal came from `IMPLEMENT_REVIEW_DEFAULT_CHANNEL=auto`. If the orchestrator is Agy, Gemini, or Antigravity, rewrite to Codex before dispatch and state why.
- **Reviewer-backend selection (two reviewers in one invocation)**: reviewer
tokens are a set rather than a single choice. When the invocation names
more than one, dispatch each named backend for the same round. `both`
selects the two backends from `claude`, `codex`, and `agy` that are not
coordinating. Under Claude Code it is `codex agy`, under Codex
`claude agy`, and under Agy `codex claude`. Any other coordinator gets
`codex agy`. Resolve `both` using the coordinator identity passed to
dispatchers in Phase 1c step 5. Do this before the self-review rewrites
in the other reviewer-backend items. The resolved pair excludes the
coordinator, so those rewrites leave it unchanged. When `both` includes
Claude, Auto-terminal requires every staged path to be in scope; see
Phase 1b. `both` means that pair specifically rather than every backend
that exists, so Copilot is never part of it; name the tokens explicitly
for any other combination. An invocation or plain phrase naming the
resolved pair is equivalent to `/vet both` under the same negation
guard. Under Claude Code, examples are `/vet codex agy` and
`review with codex and agy`. The channel lines name the coordinator and
the resolved pair, for example `both → claude agy (coordinator: codex)`.
The same token reaches different reviewers depending on who runs the
skill, and those lines keep the target visible. Everything downstream
already takes a set. The expected reviewer set is the comma-separated
normalized list: `Codex,Antigravity` under Claude Code,
`Claude-Code,Antigravity` under Codex, and `Codex,Claude-Code` under
Agy. Each reviewer gets its own watcher on its exact `Review-<Name>.md`.
Phase 2's multi-reviewer consolidation classifies each finding as
Convergent or Single-source. With one reviewer token, or none, nothing
here changes.
**Dispatch them in parallel when memory allows, one after another
otherwise.** Before the second dispatch, check available memory. On
macOS, multiply the system-wide free percentage from
`memory_pressure -Q` by `sysctl -n hw.memsize`; Linux reports
`MemAvailable` in `/proc/meminfo`, and Windows reports
`FreePhysicalMemory` (in KB) through `Win32_OperatingSystem`. With at
least 16 GB available, dispatch the reviewers in parallel. Below that,
or when the check fails, dispatch them sequentially. With 4.5 GB free on
a 32 GB workstation, two concurrent dispatches were once both killed for
low memory, and neither produced a review. Sequential dispatch survived
the same conditions.
In both modes the watcher is a wait-for-any helper. `auto-watch` exits as
soon as one file matching its glob appears, so a single watcher over
`Review-*.md` would report the round finished while the second reviewer
was still working. Run **one dispatch, one watcher and one health check
per reviewer**, each with that reviewer's own `STATE-DIR` and its exact
`Review-<Name>.md` rather than the wildcard. Where a later step names a
single reviewer, a single `DONE`, or a whole-round fallback, apply it per
reviewer in a multi-reviewer round. A failing reviewer goes through the
failover queue while the other slots continue, and consolidation waits
for every expected reviewer.
In sequential mode, before launching the next reviewer, require that the
previous dispatcher returned, or identity-checked evidence that its worker
exited. `DONE`,
`REVIEW-READY` and a clean health check authorize **intake only**: all
three are computed from the review file's freshness and quietness, and
each was reproduced while the worker was still alive, so none of them is
a statement about the process. Repeating `await-review` after
`REVIEW-READY` cannot help, because it keeps returning the same
publication result. Where the harness has been lost and termination
cannot be established, hold the existing bounded checkpoint and ask,
rather than reading `REVIEW-READY` as permission to launch: a file
appearing is not proof the previous worker exited, and launching on that
signal alone would defeat the memory sequencing this mode exists for.
Phase 1d's `DONE` instruction resumes Phase 2 immediately for a
single-reviewer round. During an unfinished multi-reviewer round it
means this reviewer is ready for intake, and in sequential mode the next
dispatch is the next step. Advance to Phase 2 only once every reviewer in
the round has finished.
Keep the full expected set for consolidation, so Phase 2 still classifies
findings as Convergent or Single-source. If a reviewer fails, still run
the rest; when the round ends with one healthy current-round source and
the others confirmed failed, that source is complete intake, labelled
Single-source, and the manual follow-up and direct-paste steps Phase 2
otherwise asks for when several reviewers were expected do not apply,
because a confirmed dispatch failure already answers what they ask.
Emit one channel line per reviewer so the user can see which backend
each round actually reached.
- **Reviewer-backend selection (explicit Codex)**: when the selected channel is Auto-terminal and the invocation contains the reviewer token `codex`, keep the normal `dispatch-codex` backend and expect `Review-Codex.md`. This makes the pointer-file hint explicit without changing the no-token default. When Codex coordinates this skill, a `codex` token or the bare Auto-terminal default is rewritten to the Claude backend before dispatch. If the Claude probe fails, try Agy and then Copilot, skipping any backend whose probe fails. One user-visible line states why. Phase 1c step 5 then sets `IMPLEMENT_REVIEW_ORCHESTRATOR=codex` on every dispatcher call, so the script-level guards apply.
- **Reviewer-backend selection (Copilot cross-vendor backend, opt-in)**: when the selected channel is Auto-terminal and the invocation names Copilot, the backend is GitHub Copilot instead of `codex exec`. Triggers: slash args `/implement-review auto copilot`, `/implement-review copilot auto`, or `/implement-review copilot`; plain phrases `use copilot as reviewer`, `copilot as reviewer`, `auto copilot`, `review with copilot`, under the same negation guard; and role-reversal wording naming Codex as the implementer and Copilot as the reviewer. A reviewer token does not need an `auto` token when Auto-terminal came from the user-scoped default. With no reviewer token, Auto-terminal selects Codex. When Copilot is the backend, dispatch uses `dispatch-copilot.{ps1,sh}` and the expected review file is `Review-GitHub-Copilot.md` (see Auto-terminal Copilot backend above).
- **Reviewer-backend selection (Claude, with mechanical self-review guard)**: select `dispatch-claude` only when BOTH of the following hold: (1) the selected channel is Auto-terminal and the slash args include `claude` (e.g., `/implement-review claude`, `/implement-review auto claude`, or `/vet claude`), OR a plain phrase matches the patterns `use claude as reviewer`, `claude as reviewer`, `review with claude`, or role-reversal wording naming Codex as implementer and Claude as reviewer, all under the same negation guard on the line above; AND (2) the orchestrator running this skill is NOT Claude Code. A reviewer token does not need an `auto` token when Auto-terminal came from the user-scoped default. Bare Auto-terminal with no `claude` / `copilot` / `agy` / `gemini` / `antigravity` token still selects Codex. **Orchestrator detection** keys on a single documented signal: when this skill is interpreted by Claude Code, the orchestrator is `claude` by definition (the skill is running INSIDE the CC session). The script-level dispatch guard separately checks `CLAUDECODE=1` so a script launched as a Bash / PowerShell subprocess from a CC session also detects it. When orchestrator is `claude` and the slash args / phrase ask for the Claude backend, rewrite the selection to Codex (or Copilot if Codex is unavailable on PATH) BEFORE any dispatch script is launched, and emit one user-visible line: `Claude backend skipped: Claude Code cannot review its own implementation; using <backend> instead.` Pair this with the script-level `IMPLEMENT_REVIEW_ORCHESTRATOR` / `CLAUDECODE=1` exit-2 in `dispatch-claude.{sh,ps1}` so direct callers that bypass path-selection cannot self-dispatch either. The script-level test covers the four enforceable cases in `tests/test_dispatch_claude.py::_DispatchContractMixin` guard methods; the path-selection rewrite itself is prose-level (no parser / selector function exists to call), so its verification is the negative live test in Task 4.7, not a unit test. Dispatch uses `dispatch-claude.{ps1,sh}` and the expected review file is `Review-Claude-Code.md`.
3. **Plugin path** is available on every platform when the user initiates it (e.g., "use the plugin") but is never a default. The user can override at any time (e.g., "use the plugin", "use the terminal").
4. **MCP forward-compat slot**: when an MCP-based Codex channel is later added (currently out of scope), it follows the same trigger pattern: `/implement-review mcp` slash arg + analogous plain phrases (`use mcp`, `mcp mode`) under the same negation guard. The trigger UX is a single extensible mechanism across all channels.
Emit one line at Phase 1c entry stating which channel was picked and why (e.g., `Channel: Auto-terminal (triggered by "/implement-review auto"; codex on PATH at C:\...\codex.cmd)`), so the user can immediately override if it picked wrong. When the channel came from the user-scoped default rather than a per-invocation token, name the variable as the reason, e.g. `Channel: Auto-terminal (IMPLEMENT_REVIEW_DEFAULT_CHANNEL=auto; codex on PATH at C:\...\codex.cmd)`. When the variable held an unrecognized value, say so and state that the shipped default applied, e.g. `Channel: Terminal-relay (IMPLEMENT_REVIEW_DEFAULT_CHANNEL="plugin" not recognized; shipped default applied)`. Naming the source matters because a user-scoped default is invisible at the call site: without it, a user who set the variable months ago cannot tell why a round dispatched itself.
## Prerequisites
Begin each run by showing the cross-agent usage snapshot, so the quota situation is visible at this run (Codex has no statusLine, so an implement-review run is where a Codex-primary session surfaces it). Invoke `agent-quota` with a real Python interpreter matched to your shell. Do not execute the bare `~/.claude/agent-quota.py` path (it hits the Windows Store launcher shim), and do not call the extensionless `~/.claude/hooks/_python` wrapper from PowerShell (it is a Bash script and runs there as a silent no-op). In Bash or Git Bash, use `"$HOME/.claude/hooks/_python" "$HOME/.claude/agent-quota.py"`. In PowerShell, run the script with your project Python directly, for example `& <project-python> "$HOME/.claude/agent-quota.py"`. In either shell you may instead run the repo copy with the project's Python (`scripts/agent-quota.py`, or `.agent-config/repo/scripts/agent-quota.py` in a consumer). Show its Claude, Codex, Agy Gemini, and Agy Claude/GPT rows. The two Agy groups have independent 5h / 7d buckets; `/vet agy` uses Gemini by default, while the second pool is available through an explicit model override. Read all percentages as **remaining** quota (headroom left), not consumed: a high value such as `93% left` means plenty of quota is still available and is not a reason to downgrade the channel or stop the review; only a low remaining figure near `0% left` signals real quota pressure. Claude and Codex are local disk reads. Agy is also rendered from a local cache; when that cache is stale, the script starts one lock-protected, bounded `agy -p "/usage"` refresh in the background. Agy documents this slash query as non-agent usage metadata, and its JSON reports zero turns and zero tokens. The current command remains responsive while a later render picks up the refreshed values. If neither the script nor a Python runner is available, skip silently and never block the review on it.
At skill start, check for staged changes (`git diff --cached`). If nothing is staged but unstaged or untracked changes exist, list them and ask the user whether to stage all (`git add -A`), stage specific files, or abort. Do not auto-stage without confirmation; untracked files may be sensitive or unrelated. With no changes at all there is nothing to review; inform the user and stop.
Then check the other direction, which the paragraph above does not cover: a path that is staged **and** changed again since staging. Intersect `git diff --cached --name-only --no-renames` with what `git diff --name-only` and `git ls-files --others --exclude-standard` report together. A non-empty result means the reviewer would receive that path as it stood at `git add`. Its findings would then read as findings about the current file. List those paths and resolve them before dispatching. Paths changed but never staged are ordinary and not this case.
Both flags earn their place. `git diff` reports tracked changes only, so a staged deletion whose file comes back stays silent in it; the untracked list catches that. Rename detection prints only the destination, so a staged `git mv a b` would leave `a` outside the staged set and recreating `a` would go unnoticed; `--no-renames` catches that. `dispatch-gemini` refuses the drifted case itself, because its reviewer is handed an export of the index and cannot see the drift. `IMPLEMENT_REVIEW_ALLOW_STAGED_DRIFT=1` overrides that refusal for a deliberate review of the index as it stands. The other dispatchers do not enforce it yet, so this check is the only cover there. See anywhere-agents#57.
## Pre-Review Checks (optional)
Before sending staged changes for review, run automated checks that catch mechanical issues locally. This lets reviewers focus on content and judgment calls instead of issues a script could find. Skip this phase if the user says to proceed directly, or if the project has no relevant tooling.
| Content type | Checks |
|---|---|
| LaTeX paper or proposal | Compile. Scan the log for overfull/underfull box warnings and undefined references. Report counts. |
| Anonymized submission | Grep staged files for author names, GitHub/lab URLs, institutional names, and tool names. Source these from the project's de-anonymization checklist if one exists; otherwise use the git user name, institution domain, and any names in the paper's author metadata or `\author{}` block. |
| Code | Run the project linter and type checker if configured. |
| Prose (`.md` / `.rst` / `.txt` / `.tex`, or prose-heavy diff) | Run `<python> <scripts-dir>/style-audit.py --repo <repo>`, resolving `<scripts-dir>` through the same lookup order as the other scripts. It finds the staged prose itself, skips anything under `agent-io`, and reports `file:line:col RULE detail` for every deterministic rule. It then names the rules it covered only in part, and the rules no detector here reaches, so a partial audit does not read as a clean pass. RULE-07 antithesis (`not just ... but`, `it is not ... it is`, clause-level `X, not Y`) is among the ones no detector reaches; audit that one by reading the rule pack. Fix locally before sending to the reviewer. |
Report any findings to the user before proceeding to Phase 1. Findings here do not go to the reviewer; fix them locally first.
**The style audit runs on content, and never gates the round.** Run `style-audit.py` whenever the staged diff carries prose, so skipping it is a decision someone made rather than a default. The script always exits 0, including when it finds violations and when `agent_style` is absent, so no step in the loop can fail on it. Style findings stay out of the reviewer's prompt, out of the Round history `Still open` list, and out of the verdict.
That placement is deliberate, because three properties would otherwise let mechanical findings hold the loop open. RULE-12 fires on any sentence over thirty words, so splitting one long sentence can produce another, and the rule has no fixed point. A prose fix is a diff change, and a diff change earns another round under the normal rules. Handed a list of style findings, a reviewer will treat them as findings and ask for them to be resolved. Running the audit before Round 1 costs zero rounds. Inside the loop is where each of those three turns into one.
## Phase 1: Prepare and Send Review
### 1a. Detect content type
Inspect the file extensions in the staged diff to classify the change:
| Extensions | Content type |
|---|---|
| `.py`, `.js`, `.ts`, `.go`, `.rs`, `.java`, `.c`, `.cpp`, `.h`, `.sh`, `.yaml`, `.json`, `.toml` | `code` |
| `.tex`, `.bib` (in a paper or manuscript directory) | `paper` |
| `.tex`, `.bib` (in a proposal or grant directory) | `proposal` |
| `.md`, `.rst`, `.txt` (in a proposal or grant directory) | `proposal` |
| Everything else or mixed | `general` |
If the diff spans multiple types, pick the dominant one. The user can override by saying, e.g., "review this as a proposal." For proposals, also ask which agency lens to apply (NSF or NIH) since they use different evaluation frameworks.
### 1b. Build the review context
Prepare a review request with:
1. **Summary** -- one to three sentences on what changed and why.
2. **Diff scope** -- choose the exact diff command before assembling the prompt, and list the changed files. If every staged path is in scope, use `git diff --cached`. If staging is broader than the review scope, use `git diff --cached -- <in-scope paths>` and list the staged paths that are explicitly out of scope, so the reviewer is not left inferring the boundary. This command applies to Terminal-relay, Plugin, Codex Auto-terminal, and Copilot Auto-terminal. The Claude Auto-terminal backend cannot honour it: its dispatcher embeds a bare `git diff --cached` in the relay prompt and has no scope argument, so use that backend only when every staged path is in scope. Path scoping keeps unrelated staged content out of the primary review input before the run; health checks 1 through 3 catch a missing or stale review only after one has been spent. Do not paste the diff inline in the primary prompt; this keeps the prompt compact and avoids bloat across rounds. The only exception is the Auto-terminal embedded-diff retry (a sandbox-strict-environment fallback when Codex's own shell tool cannot read the repo), described in Codex Channels, which must embed the same in-scope diff selected here.
3. **Splitting the round** -- use a single pass by default. Split only when no shared contract crosses the boundary; keep mirrored platform files, a script and its tests, and a source and its generated or vendored copies inside one reviewer's scope. Independence is a judgement made before the split, so verify it afterwards rather than assuming it held: re-read the boundary you drew, and run the mechanical checks that apply, such as `scripts/check-parity.sh`. See anywhere-agents#41.
4. **Review lens** -- the content-type-specific criteria from [references/review-lenses.md](references/review-lenses.md). If a focused sub-lens or agency-specific lens fits better than the full lens, use it (e.g., `paper/formatting` for a layout-only change, `proposal/nsf` when the agency is known). See the lens tables in that file.
5. **Additional focus** -- specific concerns beyond the generic lens. This is often the highest-value part of the prompt because it catches real bugs that generic criteria miss. **Always ask the user explicitly rather than guessing.** Recurring project concerns belong here: phased-development coupling, anonymization checks, page-limit compliance, budget-to-narrative consistency, terminology drift, benchmark-claim calibration, overclaim flagging. If there are no project-specific concerns this round, write "none" rather than padding the line. Examples: "check that all appendix URLs are anonymized", "verify Year 3 budget matches the narrative", "flag any overclaim in intro / conclusion", "watch for Phase 1 / Phase 2 coupling issues". When a focus names the writing rules, paste the banned-word list from `AGENTS.md` § Writing Defaults and the four mechanical checks (sentences over 30 words, repeated sentence openers, casual dashes, contractions) into the focus item itself, using the wording of the General lens item 7 in [references/review-lenses.md](references/review-lenses.md); never send the reviewer to `AGENTS.md` for them.
6. **Round number** -- which iteration this is (starting at 1).
7. **Variant targets (multi-target reviews)** -- if the staged files cover two or more variant targets that should be reviewed separately (long + short paper version, narrative + appendix tracker, internal + external report, primary + supplement), list each target by directory or file pattern. Tell the reviewer to review each target in its own top-level section and then add a cross-variant drift check at the end (tables that should match, claims that should be consistent, terminology that should align).
8. **Round history** (rounds 2+ only) -- a one-line-per-finding summary of what prior rounds raised and how each was resolved. Tag each finding as `Resolved`, `Still open`, `Deferred`, `Refuted`, or `Inconclusive`. The last two come from the Phase 2.5 verification step (see below); they let the reviewer see when a prior factual claim did not hold up under verification, with a pointer to the evidence so the reviewer can either retract or sharpen the claim. This prevents the reviewer from re-litigating closed decisions and lets them verify that fixes landed instead of re-reviewing from scratch. Example:
```
Prior findings:
- DMP listed wrong project name (Resolved — fixed in round 1)
- Budget table exceeds page width (Still open)
- Consider reordering Section 3 (Deferred — user decision)
- Citation [Smith2023] does not exist (Refuted in round 2 — arxiv.org/abs/2023.XXXXX confirms paper)
- Compile error in section 4 (Inconclusive in round 2 — could not run latexmk in this env)
```
### 1c. Send to reviewer
All review prompts sent to the reviewer (regardless of channel) must include a save instruction **at the very top of the prompt, before the summary or diff**, so the reviewer sees it first. This lets Claude Code read the feedback directly from the file, and lets the user read or forward it without copy-pasting from chat. The save instruction is:
> IMPORTANT: Save your complete review to `Review-<YourAgentName>.md` in the repository root. Normalize `<YourAgentName>` as follows: choose the stable agent or product name visible to the user (not a transient model/version list unless that is the only identity available); convert any run of whitespace to a single dash; delete every character except ASCII letters, digits, and dashes; collapse repeated dashes; trim leading and trailing dashes. Examples: `Codex` → `Review-Codex.md`, `GitHub Copilot` → `Review-GitHub-Copilot.md`, `Antigravity` → `Review-Antigravity.md`, `Claude Code` → `Review-Claude-Code.md`. If the normalized result is empty or you cannot identify your own name with reasonable confidence, use `Review-Unknown.md` and note the uncertainty at the top of the file. Publish each round atomically: write the complete review to a temporary file in the same directory, then rename that file over `Review-<YourAgentName>.md`. Do not clear the target first, and do not append across rounds. A reader then sees either the previous complete review or this one, and never a hole. A round interrupted before it finishes leaves the previous round's review intact, which is when it matters most. Use plain Markdown. Start the file with a `<!-- Round N -->` comment (matching the round number below) so the reader can verify freshness. **Begin the review with a short "Verification notes" section (paragraph or short bulleted list; "Validation notes" is also an accepted name) stating the exact commands and outcomes (e.g., `latexmk built cleanly`, `pytest pyod/test/... 5 passed`, `checked citation X against arXiv:YYYY`). Then add one standalone `Verification status: VERIFIED` or `Verification status: UNVERIFIED` line. Use `VERIFIED` only when at least one relevant verification command actually completed. If no relevant command completed, write `Verification notes: none.`, `Verification status: UNVERIFIED`, and `Commit verdict: UNVERIFIED`; do not issue `PASS` or `BLOCK`.** Separate findings into **New** (raised for the first time) and **Previously raised** (with status: Fixed, Still open, Reopened, or Deferred) sections. On Round 1, the Previously raised section may be omitted or shown as "None." Then include the file/diff scope, review lens, findings in priority order, and concrete recommended changes. **For any finding flagged High priority, include an exact suggested rewrite with file path and line range. Use a fenced code block for multi-line rewrites.** Do not skip this step. **The response format is fixed by this paragraph; do not look for example reviews or the coordinator's skill documentation to learn it.**
**Recording the expected reviewer set**: Before presenting the prompt, record two pieces of Claude-side state that Phase 2 will use:
- **Expected reviewer set**: the reviewers the user intends to invoke this round. Infer from, in order of preference: (1) explicit user statement in this or a recent turn (e.g., "I'll run Codex and Copilot", "just Gemini", "only Codex"); (2) prior-round pattern with no change announced; (3) channel default of `{Codex}` when only Terminal-relay, Auto-terminal, or Plugin has been engaged and no other reviewer is in scope. If none of these produces a confident set, ask the user which reviewers they plan to invoke before presenting the prompt; do not guess.
- **Phase 1c emission time**: the timestamp when the prompt is shown to the user. Used as an mtime tiebreaker in Phase 2 for files that cannot be classified by expected set alone.
Phase 2 uses the expected set as a scope partition axis and the emission time as a freshness tiebreaker.
**Terminal path**: Present a compact, copy-pasteable review prompt as a fenced text block. Keep the prompt to about 20 lines. Tell the reviewer to run the exact diff command chosen in Phase 1b rather than pasting the diff inline; this prevents prompt bloat as rounds accumulate. The abbreviated save instruction below inherits the full contract stated above (statuses, Round 1 behavior, required sections).
````
IMPORTANT: Save your complete review to Review-<YourAgentName>.md in the repo root. Normalize your name: pick the stable product name, whitespace → one dash, keep only ASCII letters/digits/dashes, collapse repeated dashes, trim edge dashes. Examples: Codex → Review-Codex.md, GitHub Copilot → Review-GitHub-Copilot.md, Antigravity → Review-Antigravity.md, Claude Code → Review-Claude-Code.md. Use Review-Unknown.md if the result is empty or you cannot identify yourself, and note the uncertainty at the top of the file. Write the complete review to a temporary file in the same directory, then rename it over that filename rather than clearing the target first. Start with <!-- Round N -->. Begin with Verification notes listing exact commands and outcomes. Add one standalone Verification status: VERIFIED or Verification status: UNVERIFIED line. Use VERIFIED only after at least one relevant verification command completed. With no completed verification, write Verification notes: none., Verification status: UNVERIFIED, and Commit verdict: UNVERIFIED; do not issue PASS or BLOCK. Include file/diff scope and review lens. Separate findings into New and Previously raised (Fixed / Still open / Reopened / Deferred) sections. For High-priority findings, include an exact rewrite with file:line. The response format is fixed by this paragraph; do not look for example reviews or the coordinator's skill documentation to learn it.
Review staged changes in <repo path>. Round <N>.
Run `<diff command chosen in Phase 1b>` to see the diff. Files changed: <in-scope file list>.
<When staging is broader than the review scope, add:> Staged but out of scope: <excluded staged paths>.
Reading contract: your task, lens, and response format are supplied here, and the project rules already supplied to you apply, including local overrides. Skip router dispatch and coordinator-workflow discovery at all skill lookup locations (skills/implement-review/, .claude/skills/implement-review/, .agent-config/repo/skills/implement-review/, .agents/skills/implement-review/). Do not load coordinator skill files or example reviews to discover the workflow or response format. Files under review remain readable. A targeted read outside the review scope is permitted when it supplies missing context, resolves truncation, or answers a concrete verification question. Read an applicable instruction file that was not supplied (for example AGENTS.local.md) once. Obtain the selected diff once with the command above; if this request embeds that diff, use it and do not obtain another copy. Do not read the same content twice.
Summary: <one to three sentences>
Lens: <content type> — <abbreviated criteria, sub-lens, or agency-specific lens name>
Focus: <additional focus if any, or omit line>
Working method (optional, only if your runtime supports it): for a large or multi-file diff, you may parallelize across scopes only where no shared contract crosses the boundary (mirrored platform files, a script and its tests, and a source and its generated or vendored copies stay in one scope), keeping every shared-contract pair in one reviewer's context and re-reading that boundary while consolidating; for any finding that rests on a checkable external fact (citation, link, library or API behavior, version), use web search to verify it before asserting, and record what you checked in Verification notes.
Scope-challenge focus (mandatory; reviewer must take an explicit position): (a) Why exactly this scope — what user pain or opportunity does it close, could a smaller scope close most of it, could a marginally larger scope close all of it for low cost? (b) Is each "out of scope / deferred to vNext" worth its process tax (extra plan-review + implement + execution-review + CI + publish cycle) versus inclusion now? (c) Is this plan the simplest path? Be adversarial about purpose and shape; do NOT fish for low-probability edge cases.
<When the staged diff spans two or more variant targets:>
Variant targets:
- TARGET A: <path or pattern>
- TARGET B: <path or pattern>
(Review each target in its own top-level section and add a Cross-variant drift check at the end.)
<For rounds 2+:>
Prior findings:
- <finding> (Resolved | Still open | Deferred | Refuted | Inconclusive)
````
Then wait for the user to relay the reviewer's feedback or confirm that the reviewer has finished (see Phase 2 for how Claude Code picks up the review).
**Auto-terminal path (opt-in)**: When the user has opted in per the trigger rules in Codex Channels > Path selection, **first check whether the required Auto-terminal scripts exist**. Look them up in this order: repo-local `skills/implement-review/scripts/`, then pack-deployed `.claude/skills/implement-review/scripts/`, then bootstrapped `.agent-config/repo/skills/implement-review/scripts/`. Required scripts are the selected backend (`dispatch-codex.{ps1,sh}`, `dispatch-copilot.{ps1,sh}`, `dispatch-claude.{ps1,sh}`, or `dispatch-gemini.py`) plus `health-check.{ps1,sh}`, `stall-watch.{ps1,sh}`, and `await-review.py`; the probe must also resolve a `python-interpreter` for the Python-only resolver and Gemini dispatcher, otherwise downgrade **without** setting sticky downgrade. `await-review.py` is required because a stopped task is resolved through it. Without it the round cannot tell a live reviewer from a dead one, which is the failure this channel is most exposed to. `await-review.py` and `dispatch-gemini.py` have no shell variants. Check `<state-dir>/python-interpreter` when a previous dispatch recorded one, otherwise resolve one the way `dispatch-codex` does. A missing interpreter is the same class of absence as a missing script. Say `Auto-terminal needs a Python interpreter and none was found; using Terminal-relay for this round.` and downgrade **without** setting sticky downgrade. This matters beyond Python projects: the Copilot and Claude backends never write `python-interpreter`, and `dispatch-codex` itself proceeds after failing to resolve one. If any required script for the current platform is missing, say `Auto-terminal is documented but unavailable until the required scripts are present; using Terminal-relay for this round.` Then downgrade to Terminal-relay **without** setting sticky downgrade (the absence is design state, not failure; sticky should kick in only for real Auto-terminal runtime failures).
If the scripts are present, Claude Code does NOT present a copy-paste prompt block. Instead, the skill:
1. Probes the selected backend: `codex`, `copilot` (with `gh copilot` fallback), `claude`, or `agy`. For Gemini on Windows, accept `%LOCALAPPDATA%\agy\bin\agy.exe` even when the current process has the installer's older PATH. If absent: warn the user, and take the next entry of the Phase 1c failover queue if one probes clean, otherwise downgrade to Terminal-relay for this round. This is a preflight absence rather than a dispatched failure, so it does not set sticky downgrade either way.
2. Probes session sticky-downgrade state. If active: downgrade to Terminal-relay (silent unless the user asks why).
3. Assembles the prompt: byte-identical to the fenced Terminal-relay block above (same save contract, lens, focus, scope-challenge focus, prior findings).
4. Writes the prompt to `<scratch>/agent-io/round-<N>-prompt.txt` under `%TEMP%` / `$TMPDIR`. See **Where the round's own files go** below for why the directory is named.
5. Invokes the selected dispatcher from the same lookup root: `dispatch-codex.{ps1,sh}`, `dispatch-copilot.{ps1,sh}`, `dispatch-claude.{ps1,sh}`, or `<python> dispatch-gemini.py`. Pass `--prompt-file <temp-path>`, `--round <N>`, and the matching expected review file: `Review-Codex.md`, `Review-GitHub-Copilot.md`, `Review-Claude-Code.md`, or `Review-Antigravity.md`. Set `IMPLEMENT_REVIEW_ORCHESTRATOR` in the dispatcher's environment to the coordinator's own identity: `claude` for Claude Code, `codex` for Codex, `agy` for Agy. Do this on every dispatch, including retries and failover, so an inherited runtime marker never decides who the coordinator is. Run in the background and size the timeout to the backend and reasoning tier. For Codex, use about 20 minutes (`1200000`) at `xhigh` and about 45 minutes (`2700000`) at `max`. Agy defaults its own `--print-timeout` to 45 minutes. Each dispatcher launches the shared `stall-watch` in the background. Its default 600-second silence threshold records `<state-dir>/stall-warning` without killing any process. A terminal Codex response-stream suffix records `<state-dir>/stream-death` and invokes the bounded reap contract below. Run the dispatcher from the reviewed repository's root, which is the session's working directory; to review another repository, change directory in its own tool call first, never as `cd <path> && <dispatcher>`, which the guard denies (23 of the 97 re-dispatches in 60 days were that denial).
6. Reads the dispatch script's stdout: it emits exactly one line `STATE-DIR <abs-path>` (the only stdout line). Capture this path for Phase 2 to pass to `health-check --state-dir <abs-path> --round <N> --review-file Review-<Reviewer>.md`. Pass the backend's exact review filename explicitly. All other dispatch diagnostics plus the last 80 backend lines go to the script's stderr.
7. Phase 1d auto-watch runs once per reviewer; each watcher polls for that backend's exact review file with the current round marker. Phase 2 prologue (defined in Phase 2 below) adds Auto-terminal-specific gating before silent advance.
**Where the round's own files go**: this skill writes two kinds of file around the reviewer. One is the prompt handed over, the other is the output captured back. Both belong under an `agent-io` directory inside the session scratch area. Step 4 writes `<scratch>/agent-io/round-<N>-prompt.txt`, and a captured round output goes to `<scratch>/agent-io/round-<N>.txt`. That directory name is a declaration the writing-style hook reads, and it skips any path carrying the segment. Neither file is this skill's prose to rewrite: a prompt is an instruction to another agent, and a captured review is another agent's words. Files written for a human reader, such as `PLAN-<identifier>.md` or the review body itself, stay outside `agent-io` and stay covered.
**Bounded retry after confirmed Codex stream death**: `IMPLEMENT_REVIEW_STREAM_RETRY_LIMIT` sets the maximum number of automatic redispatches, default 1; `0` disables them. The dispatcher records the effective limit and current count in the state directory. A retry begins only when both `<state-dir>/stream-death` and `<state-dir>/stream-retry-request` exist, the count is below the limit, and `<state-dir>/stream-reap-complete` confirms the old worker tree has been reaped. The dispatcher archives the failed attempt under `<state-dir>/attempt-N`, clears the active attempt markers, updates the timestamp and count, and runs the same prompt again. It reuses the original state directory, so the initial `STATE-DIR <abs-path>` remains the only dispatcher stdout line. Each redispatch writes `AUTO-REDISPATCH attempt=N/M reason=codex-response-stream-disconnected state-dir=<abs-path>` to dispatcher stderr; auto-watch mirrors that schema in its own output after the count advances. Silence can write `stall-warning` and wake the coordinator, but it never requests or starts a retry.
**This paragraph is the single authority for where a confirmed Auto-terminal runtime failure goes.** A confirmed failure is a non-zero exit, a timeout, a missing fresh review file, a failed Health check, or an `await-review` `DEAD` verdict. Every other passage that names Terminal-relay as a destination for a dispatched-then-failed backend defers here; the preflight absences below are a different case and keep their own wording.
Maintain a **failover queue** for the round from these normalized backend identities, in order: `Codex`, `Antigravity`, `GitHub-Copilot`, `Claude-Code`. Include only backends whose backend-specific probe passes. Remove the implementing agent, since no agent reviews its own implementation, and any backend that already published a healthy review this round. The queue is fixed when the round starts, and each attempt consumes its entry, so failover never retries a backend that has failed. Failover does not affect the separate bounded same-backend stream retry, which runs first where it applies. The bounded embedded-diff retry, where it applies, also runs before failover moves to the next entry.
On a confirmed failure, consume the next entry and dispatch to it, stating in one line which backend failed and which is taking over. Record the replacement's expected review filename and its own state directory, and add it to the expected reviewer set so Phase 2 does not read an automatic replacement as an unexpected source.
Track requested reviewers and failover attempts by the same normalized identity. Every dispatch consumes that backend's entry, including an originally requested dispatch. A healthy review satisfies that backend's requested slot, so skip any later scheduled dispatch for it. A confirmed failed attempt is also final for that backend this round, apart from the bounded same-backend retries above.
When no eligible automatic backend remains, finish with Single-source intake if the round already has a healthy review and all remaining requested reviewers are conclusively resolved. Do not set sticky downgrade in that case. Use Terminal-relay and set sticky downgrade only when automatic attempts have produced no healthy review.
Routing a confirmed failure to a working reviewer rather than to the user is what § "Agent Fungibility" in `AGENTS.md` asks for: the review loop is the core function, and it has to survive one vendor being unavailable. Terminal-relay needs a person to carry the prompt and the answer by hand, so a session that reaches for it first has stopped being a review loop and started being a transcription task. Some users never take that step at all, which turns an automatic downgrade into a silent end of reviewing for the session. Terminal-relay stays as the final fallback, because a channel that needs no working CLI is the only one left when none of them run. A harness notification that the background task was stopped is not one of these triggers, and neither is a watcher that stopped with it. Resolve that case with `await-review` first (see Phase 1d); only its `DEAD` verdict is an Auto-terminal runtime failure. With the default `--sandbox danger-full-access` Codex can inspect the repo directly and tool-sandbox failures should be vanishingly rare; if one does occur (typically only when `CODEX_DISPATCH_SANDBOX` has been narrowed in a CI / shared environment) and Phase 2.0 surfaces it via Check 7 / 8 / Substance heuristics with dispatch exit 0, use the embedded-diff retry once before setting sticky downgrade (see "Embedded-diff retry for Codex tool-sandbox failure" in Codex Channels for the procedure and the diff-scoped-findings caveat). Do not add a truncated positional-argument fallback.
**Script contract invariants** (apply to all `dispatch-codex` / `health-check` / `stall-watch` `.sh` and `.ps1` variants):
- **State-dir lifecycle**: created once per logical dispatch by `dispatch-codex` under `%TEMP%` / `$TMPDIR`; preserved on any `WARN` or `FAIL` outcome so the user can inspect; deleted only after a clean Phase 2 intake completes silently. State-dir names carry `<pid>-<nonce>` and are unique across unrelated dispatches. Automatic stream-death attempts reuse the same directory and preserve each failed attempt under `attempt-N`, which keeps the single `STATE-DIR` stdout contract stable.
- **Deployed-script release**: before it creates dispatch state or starts the reviewer, `dispatch-codex` re-executes from a private temp copy. `IMPLEMENT_REVIEW_DISPATCH_REEXEC` prevents a loop, and `IMPLEMENT_REVIEW_DISPATCH_SOURCE_DIR` keeps sibling-script lookup at the original deployed directory. The wrapper removes the temp copy after the dispatch and propagates its exit status. This releases the deployed `dispatch-codex.sh` path before Bash can block a composer replacement.
- **Timestamp units**: all values in `<state-dir>/timestamp` and `<state-dir>/pre-mtime` are **integer Unix epoch seconds**. Every producer writes integers (`date +%s`, `ToUnixTimeSeconds()`), and both readers parse with `int()` and reject a float spelling as a state-contract failure, so a fractional value is not a permitted alternative spelling. mtime comparisons happen in UTC; cross-platform stat is the implementer's responsibility. POSIX may use `stat -c %Y` (GNU) or `stat -f %m` (BSD/macOS). PowerShell must convert `LastWriteTimeUtc` to Unix epoch seconds, for example `$utc = (Get-Item -LiteralPath <path>).LastWriteTimeUtc; ([DateTimeOffset]$utc).ToUnixTimeSeconds()` (two-step form, avoids cast-precedence confusion) or an equivalent Unix-epoch conversion. **Do NOT write Windows FILETIME values from `ToFileTimeUtc()`** (FILETIME is 100-nanosecond ticks since 1601-01-01, not Unix epoch; mixing units across `.ps1` and `.sh` would silently break Health check 2 freshness comparisons).
- **stdin invocation shape**: POSIX uses redirection with the prompt file as stdin (`codex exec --sandbox "$CODEX_DISPATCH_SANDBOX" [isolation args] -c "developer_instructions=..." - < <prompt-file>`). PowerShell writes a transient `<state-dir>/run-codex.cmd` helper with the same argument order and `< <prompt-file>` handle redirection, then invokes it through `& $cmdHelper`. These shell-level redirections preserve the byte-identical prompt invariant and let Codex child processes inherit the logon-session token cleanly. The `--sandbox` value comes from `$env:CODEX_DISPATCH_SANDBOX` (default `danger-full-access`); see the Auto-terminal Trust-Model paragraph above for why. By default both variants pass `--ignore-user-config -c model=<M> -c model_reasoning_effort=<R>`. `M` follows the order the configuration boundary above describes. `R` comes from `$env:CODEX_DISPATCH_REASONING` and defaults to `xhigh`. Isolation keeps a user-level Codex MCP server, plugin, or hook from auto-starting inside the headless reviewer and recursing into a nested codex, the failure recorded as agent-config#1. Set `CODEX_DISPATCH_ISOLATE_MCP=off` to opt out and run with the full user config. Do this if the review needs the user's `service_tier` or a custom `model_provider`, which isolation drops and does not re-pass. Independently of isolation, both variants pass the separate `-c developer_instructions=...` argument before the trailing `-`. That argument suppresses only bootstrap and shared configuration refresh in the child session; it leaves `AGENTS.md` and all other project rules active. Because it is CLI configuration and the prompt still arrives only through input redirection, it does not modify the prompt-file bytes. `--ignore-user-config` is used because the narrower `-c mcp_servers={}` is deep-merged by Codex 0.139 and leaves the configured servers running. It also resets the model and the reasoning effort, so the re-pass carries the review model and restores the dispatcher's cross-model compatibility floor (`xhigh` by default). Set `CODEX_DISPATCH_REASONING=max` for maximum single-agent reasoning, and `ultra` only when automatic task delegation is intended. A model accepts `max` or `ultra` only when its entry in `codex debug models` lists that level. Re-passing `service_tier` is deliberately skipped: hardcoding the maintainer's tier would make every round fail for a consumer whose account lacks it, so a temp-`CODEX_HOME` sanitizer (config minus the MCP tables) is the documented path to full config-preserving isolation. This shape replaced an earlier `Get-Content -Raw <prompt-file> \| codex exec -` and a `Start-Process -RedirectStandardInput` variant: the pipe form could inject a BOM under PS 5.1, and `Start-Process` routes through `CreateProcessAsUserW` which strips the token (Codex's git subprocess then failed with Windows error 1312). Neither variant may use command substitution (`codex exec "$(cat <file>)"`) or positional-arg passing of the full prompt, because both hit ARG_MAX on Windows. Path values interpolated into the cmd helper must escape `%` to `%%` so cmd does not env-expand them, and the helper is written as UTF-8 (no BOM) with a `chcp 65001` prefix so non-ASCII paths survive cmd's codepage layer.
- **`stall-watch` silence, stream death, and bounded reaping**: the silence threshold is `STALL_THRESHOLD_SECONDS`, default 600 seconds. A no-growth interval writes `STALL <timestamp> tail-no-growth-for-<seconds>s` to `<state-dir>/stall-warning`. Silence NEVER kills a process. Stream-death detection runs only after tail growth, reads only the last 16 physical lines, removes empty lines, and requires the terminal suffix to contain the Codex response URL error followed by `tokens used` as the penultimate non-empty line and a bare numeric final line. A match writes `STREAM-DEATH <UTC timestamp> codex-response-stream-disconnected` to `<state-dir>/stream-death`. The watcher records the dispatcher's start identity and the worker roots' process-creation identities. It may reap only for `stream-death` or `parent-exited`, and only after a recorded worker identity still matches; unrelated processes are never targeted. If `stall-watch` itself errors, it exits silently.
- **Stream-death retry handoff**: `stall-watch` writes `stream-retry-request` before `stream-death`, performs the identity-checked reap, then writes `stream-reap-complete`. The dispatcher waits up to 10 seconds for that completion marker before archiving and retrying. Missing handoff or archive evidence stops redispatch and emits an `AUTO-REDISPATCH-SKIPPED` diagnostic. The configured retry count is a hard bound. Silence never enters this handshake.
- **Exit codes**: `dispatch-codex` propagates the final Codex exit code unchanged. `health-check` exits non-zero when Phase 2 must refuse to read the review file (Check 1-6 or Check 10 FAIL, or required dispatch state missing/stale); WARN-only outcomes (Check 7/8/9 hit, Substance heuristic flag) exit 0 with WARN lines on stdout. **Phase 2 must parse `health-check` stdout and treat any `WARN` line as a silent-advance blocker; exit code 0 alone is NOT sufficient to proceed silently.** `stall-watch` exits 0 regardless of what it detected. `await-review` (Python only, no shell variants) writes one terminal line to stdout and exits 0 for `REVIEW-READY`, 3 for `ALIVE`, 4 for `DEAD`, 2 for a checkpoint (`TIMEOUT` or `REAP-UNKNOWN`), and 1 for a state-contract failure. It reads the same `pre-mtime`, `timestamp`, `tail`, and retry-marker files as the checks above, and applies the same state contract, freshness rule, and round-marker rule. Its `REVIEW-READY` therefore cannot contradict Health checks 1 to 3 or the state contract. It says nothing about Checks 4 to 10: size, verification notes, and the verdict-verification contract stay Phase 2's job, and a `REVIEW-READY` review can still fail them.
- **Check 8 breakdown emission**: when Check 8 fires (any TOOL_FAILURE_PATTERNS hits in `<state-dir>/tail` after backtick-code-span exclusion), `health-check.py` emits a per-pattern count breakdown after the marker total so downstream Claude can recognize known-noise shapes without re-grepping the tail. Form: `WARN check-8 N tool-failure-markers breakdown=label1:n1 label2:n2 ...` with labels derived from the longest word run in each pattern, sorted by count descending. See the FP-tuning principle subsection for the catalogue of known-noise shapes (WSL-stub-bash 1312 burst, etc.) that Claude should fast-Proceed when Substance heuristics also pass.
#### Evidence for latency claims
`<state-dir>/tail` is a transcript for a human to read. It has no timestamps, and the reviewer's prose can quote the same error strings that appear in real diagnostics. Counting occurrences in a tail therefore does not measure elapsed time, call count, or failure frequency. For any claim about where time goes, use `~/.codex/sessions/**/rollout-*.jsonl`. Rollout records carry per-event millisecond timestamps and token counts, and a run can be joined to them by session id. A latency claim without rollout evidence should not be acted on.
Two corollaries matter when reading retained tails. A single tail block can represent one call within a parallel batch; repeated material inside that block does not establish several separate calls. When the diff under review includes this skill, the skill's own diagnostic strings appear in the tail as reviewed content. Any cross-tail scan must exclude that case.
**Plugin path**: Tell the user the changes are ready for review and provide **the same review prompt content used by the Terminal-relay path** (save contract first, then round, diff scope, summary, lens, focus, scope-challenge focus when applicable, variant targets when applicable, prior findings for rounds 2+). The user pastes the same content into the plugin sidebar; the surrounding Markdown fence may be omitted if the plugin UI does not need it, but the prompt content itself must match Terminal-relay byte-for-byte (after the optional fence-strip). This preserves the cross-channel prompt invariant: Codex receives the same instructions regardless of channel.
Example instruction to the user:
> Paste the same review prompt shown for Terminal-relay into the Codex plugin sidebar. The plugin can inspect the repository directly (so the diff is visible without copying), but the prompt content itself must still include the save contract, round number, files changed, summary, lens, focus, scope-challenge focus when applicable, variant targets when applicable, and prior findings when applicable. Ask the plugin reviewer to save the complete review to `Review-<YourAgentName>.md` in the repo root using the normal Phase 1c save contract.
Then wait for the user to relay the reviewer's feedback or confirm that the reviewer has finished.
### 1d. Auto-watch (Terminal-relay and Auto-terminal channels)
After Phase 1c emits the Terminal-relay prompt or dispatches Auto-terminal (and records the expected reviewer set + emission/dispatch time), the skill **automatically** launches one background watcher per expected reviewer. Each watcher detects when its reviewer writes its `Review-<Name>.md`, eliminating the manual "done" relay. The `DONE` handling below says when Phase 2 resumes. The watcher runs by default for both channels; the user does not need to confirm. To opt out, the user can say so explicitly (e.g., "stop auto-watch", "manual mode this round") and the skill terminates the background process and falls through to the wait-for-user path. Plugin path skips this subsection entirely (IDE plugins typically have the file open and gain little from auto-watch).
Launch the platform-appropriate watcher script immediately after emitting the prompt, using positional arguments `(FILE_GLOB, ROUND_NUMBER, EXPECTED_REVIEWERS)`. Look up the script in this order: `skills/implement-review/scripts/auto-watch.{sh,ps1}` (repo-local), then `.claude/skills/implement-review/scripts/auto-watch.{sh,ps1}` (pack-deployed), then `.agent-config/repo/skills/implement-review/scripts/auto-watch.{sh,ps1}` (bootstrapped). Use the Bash variant on macOS / Linux and the PowerShell variant on Windows. `FILE_GLOB` is the reviewer's exact `Review-<Name>.md` and `EXPECTED_REVIEWERS` its normalized name (e.g., `Codex`). A multi-reviewer round launches one watcher per reviewer, each with its own state directory, never one watcher over `Review-*.md`. Run the watcher in the background so the skill can keep accepting user input while it polls. On the Auto-terminal path, set `IMPLEMENT_REVIEW_STATE_DIR` to the absolute path from the dispatcher's `STATE-DIR` line when launching the watcher. That hands the directory over instead of leaving the watcher to find it. Its fallback is a 30-second window around its own start time, which a contended process launch can miss.
The watcher polls every 5 seconds. It fires when (a) the file's mtime has advanced past the snapshot taken at watcher startup, (b) the file has been quiet for 10 seconds (mtime is at least 10 seconds in the past), AND (c) its first line equals `<!-- Round N -->` after stripping trailing `\r` and whitespace. Hard timeout is 60 minutes. Stdout starts with `WATCH-START round=N reviewers=<csv> timeout=3600s`. Each confirmed retry may add `AUTO-REDISPATCH attempt=N/M reason=codex-response-stream-disconnected state-dir=<abs-path>`. One terminal line then follows: `DONE <absolute-path>` (exit 0), `TIMEOUT` (exit 2), `REAP-UNKNOWN <state-dir>` (exit 2), `STREAM-DEAD <state-dir>` (exit 3), or `STALL <state-dir>` (exit 3). Output remains bounded by the configured retry limit.
When the watcher emits `DONE <path>`, resume Phase 2 immediately for a single-reviewer round. In a multi-reviewer round, that reviewer is ready for intake; begin consolidation only after every reviewer has a ready report or a confirmed failure. The watcher's path output is informational; Phase 2 still re-lists `Review-*.md` itself and applies the freshness + scope partition described below. A reviewer whose watcher ends without a report is handled by Phase 2's missing-reviewer steps once every other reviewer is resolved.
When the watcher emits `AUTO-REDISPATCH ...`, the bounded retry has started; keep watching the same logical dispatch and state directory. No user action is required.
When the watcher emits `STREAM-DEAD <state-dir>`, `stall-watch` has recorded the terminal stream-disconnect suffix AND written `stream-reap-complete`, so the identity-checked worker-tree reap finished and no further retry started. The watcher waits for that completion marker before it emits, because `stream-death` is written before the reap runs and this output costs the session its channel. The bound may be exhausted, disabled, or blocked by an incomplete handoff. Treat exit 3 as an Auto-terminal runtime failure and apply the existing downgrade and sticky-downgrade rules.
When the watcher emits `REAP-UNKNOWN <state-dir>`, `stall-watch` recorded the stream disconnect and then never confirmed its reap. Both variants swallow a failed write of `stream-reap-complete`, so the proof `STREAM-DEAD` requires may never arrive, and waiting the remaining hour for it buys nothing. A missing confirmation is not a death certificate either. Exit 2 is a checkpoint, so resolve the round with `await-review` as below, and do not set sticky downgrade from this line.
When the watcher emits `STALL <state-dir>`, surface the silence warning and inspect or continue monitoring the dispatch. Exit 3 wakes the coordinator; it does not mean the worker was killed or that the review cannot still complete. Do not terminate the dispatch or set sticky downgrade from silence alone.
**When the harness reports the dispatch or the watcher as stopped**: a background-task notification carrying `killed`, whose summary reads `Background command "..." was stopped`, is a statement about the harness's task wrapper. It is not evidence about the reviewer, which runs detached and outlives its wrapper. Do not conclude failure, do not redispatch, and do not set sticky downgrade from that notification. It routinely arrives for the dispatch and the watcher in the same second, so a lost watcher is not corroboration; `await-review` replaces the watcher for the rest of that round.
Resolve the round from state-dir evidence instead. `await-review.py` has no `.sh` or `.ps1` wrapper and is not executable by filename, so invoke it through an interpreter: `<python> <scripts-dir>/await-review.py --state-dir <abs-path> --round <N> --review-file Review-<Reviewer>.md`. Resolve `<scripts-dir>` through the same lookup order as the other scripts. Take `<python>` from `<state-dir>/python-interpreter` when the dispatcher resolved one. Use the backend's own review filename: `Review-Codex.md`, `Review-GitHub-Copilot.md`, `Review-Claude-Code.md`, or `Review-Antigravity.md`. Run it in the **foreground**. Its default budget stays under the 600-second cap a single tool call gets, and a background probe would be exposed to the same stop it exists to diagnose. Three verdicts and two checkpoints:
- `REVIEW-READY <path>` (exit 0): the round's review is published, fresh against `pre-mtime`, carries this round's marker, and has gone quiet. Continue into the Phase 2.0 health check exactly as if the dispatch had reported exit 0.
- `ALIVE <state-dir> reason=working|tail-idle-unproven|round-marker-mismatch ...` (exit 3): the round has not provably ended. Run it again; the round deadline below is what stops the loop. `tail-idle-unproven` means the tail has been silent past the idle threshold. `round-marker-mismatch` means a review was published for this dispatch carrying another round's marker. Both are reasons to look, never verdicts.
- `DEAD <state-dir> reason=stream-death` (exit 4): `stall-watch` recorded the terminal stream disconnect and its identity-checked reap completed, so the worker tree is gone. Only this verdict counts as an Auto-terminal runtime failure and may set sticky downgrade.
- `TIMEOUT <state-dir> round-deadline=<epoch>` (exit 2): the round outlived the deadline below, and nothing observed says the reviewer died. Stop calling `await-review`, print `Auto-watch timed out after 60 min; reply 'done' when the reviewer finishes.`, and fall through to the wait-for-user path.
- `REAP-UNKNOWN <state-dir> tail-idle=<s>` (exit 2): the state the watcher reports under the same name, reached from the resolver. Treat it the same way, as a checkpoint for a person.
Neither exit-2 line may set sticky downgrade. Both say the evidence ran out, which is the opposite of the evidence `DEAD` requires.
**The round's deadline outlives the session that started it.** The stop took the wrapper holding the dispatch timeout, and it usually takes the watcher's 60-minute timer with it. So `await-review` stamps `<state-dir>/round-deadline` on its first call, at the round's origin plus 60 minutes, and reads that file back on every later call. The origin is the earliest readable timestamp in the state directory. That is the root `timestamp`, unless an automatic redispatch archived an earlier one into `attempt-N/timestamp` and rewrote the root. The archived value is then the round's real start. The stamp is never rewritten. An automatic redispatch therefore cannot buy the round a second hour, whether or not a resolver ran before it, and an agent that lost the dispatching session's context still inherits the original bound. Pass `--timeout` at or below 540 seconds to stay inside the 600-second cap on one tool call; the resolver ends the call at the round deadline when that comes first. Repeat on `ALIVE` until a verdict or a checkpoint arrives; the deadline is what guarantees one does. A `round-deadline` file that exists and cannot be read as an integer epoch is a state-contract failure (exit 1), never a reason to stamp a fresh hour.
**Silence is never terminal here.** `DEAD` requires evidence that the round ended: `stream-death` paired with a completed `stream-reap-complete`. A review published under the wrong round marker is not that evidence either, because a reviewer can publish twice in one round, and the second write is the one worth having. A quiet tail is not that evidence, which is why `stall-watch` treats even 600 seconds of it as a soft signal, and why a shorter idle window may not be promoted to a death certificate. When the reviewer dies in a way that writes no marker, `await-review` keeps returning `ALIVE` and the round's own timeout is what ends it. That is the intended shape. The timeout is a bounded wait with a human at the end of it, and an unproven death is the judgement this rule exists to prevent.
This rule exists because the alternative was measured and it was wrong every time. On 2026-08-21, six such notifications arrived across two repositories. In all six the detached reviewer was still running. Every one of those rounds published its review afterwards, the latest about sixteen minutes after the stop. One session read two of them as consecutive dispatch failures, set sticky downgrade, and stopped. The review it was waiting for landed ten minutes later and was never read.
When the watcher emits `TIMEOUT`, print `Auto-watch timed out after 60 min; reply 'done' when the reviewer finishes.` and resume the existing wait-for-user path. On explicit opt-out, user interrupt, or watcher launch failure, also fall through to the same wait-for-user path. The fallback is the unchanged Phase 1c to Phase 2 flow, so no Phase 2 logic depends on whether auto-watch was used.
## Phase 2: Intake Feedback
### Phase 2.0 prologue: Auto-terminal Health check (Auto-terminal channel only; Terminal-relay and Plugin skip)
For the Auto-terminal channel only, run 10 structural Health checks plus 3 Substance heuristics on `Review-Codex.md` before the existing freshness + scope partition begins. In a multi-reviewer round, run them once per reviewer with that reviewer's state directory and exact review file. `Review-Codex.md` in the table below then means the file passed to that run. Required dispatch state files (`<state-dir>/pre-mtime`, `<state-dir>/timestamp`, `<state-dir>/tail`, plus optionally `<state-dir>/stall-warning` when stall-watch wrote it) come from the `dispatch-codex` and `stall-watch` scripts via dispatch-codex's stdout `STATE-DIR <abs-path>` line. Health checks 2, 7, 8, 9 and the Substance heuristics depend on these files.
| # | Check | Failure → |
|---|---|---|
| 1 | `Review-Codex.md` exists at repo root | Surface failure; route through the Phase 1c failover queue |
| 2 | File mtime is later than the Phase 1c dispatch timestamp AND later than `<state-dir>/pre-mtime` (catches stale file from prior round or task) | Same as 1 |
| 3 | First line equals `<!-- Round N -->` | Same as 1 |
| 4 | File size ≥ 500 chars | Same as 1 |
| 5 | "Verification notes" section present (`Verification notes:` paragraph form OR `## Verification notes` / `**Verification notes**` heading) | Same as 1 |
| 6 | If the dispatch prompt named a plan file or staged file list, the review's file scope mentions that current scope | Same as 1 |
| 7 | **Review-text** suspicious-phrase scan: case-insensitive match in the saved review body on any of `could not`, `i cannot`, `failed to`, `permission denied`, `rate limit`, `unable to access`, `unable to execute`, `unable to run`, `not able to execute`, `not able to run`, `do not have access`, `not authenticated`, `authentication failed`, `unauthorized`, `timed out`, `timeout`, `quota`, `command not found`, `no such file`, `sandbox.*fail`. Exclude content inside backtick code spans (Codex meta-discussing the pattern list is not failure narration). | Do not hard-fail; block silent advance and surface: `Auto-terminal review-text scan: N suspicious phrases (lines L1, L2, ...) -- review may be partial. Proceed?` |
| 8 | **Dispatch-tail** tool-failure scan: case-insensitive regex match in `<state-dir>/tail` (full codex-exec stdout+stderr, NOT the review body) for `tool ... failed`, `mcp tool failed`, `HTTP/status 429/5xx`, `rate limit`, `quota exceeded`, `insufficient_quota`, `connection refused/reset/timed out`, `context_length_exceeded`, `maximum context length`, Windows sandbox launch failures such as `CreateProcessAsUserW failed: 1312` or `windows sandbox: runner error`, the Windows NTSTATUS spawn-failure family `-107374\d{4}` (signed-decimal `0xC000xxxx` exit codes printed when a spawned shell dies before entry), and errno forms `ENOSPC` / `EACCES` / `ETIMEDOUT` / `ECONNRESET` / `ECONNREFUSED`. The scan is line-level (Fix A + Fix B; see `health-check.py`): strip backtick code spans, then skip modeled pattern-echo lines (a line-numbered source citation, or a line quoting a literal regex-source fragment such as `\bENOSPC\b` or `sandbox.*runner error`), then on the surviving lines count **intrinsic** failure forms (`HTTP/status 429/5xx`, `Too Many Requests`, `rate limit exceeded`, quota / `insufficient_quota`, `context_length_exceeded`, the 1312 / sandbox-runner shapes, signed-decimal NTSTATUS exits, errno forms) on their own, and **generic** words (bare `rate limit`) only when an error-frame token sits on the same or an adjacent line. A bare line-number prefix or a backslash never suppresses a line, so a real line-numbered or Windows-path failure still surfaces. This check also runs when Check `1` fails. See `health-check.py` for the canonical pattern lists. A `backend-failure` record in the state directory counts as one marker (`recovered`). The Agy dispatcher writes it just before publishing a review from a result that ended in a backend error. | Do not hard-fail; block silent advance and surface: `Auto-terminal dispatch-tail scan: N tool-failure markers -- Codex's CLI / OS / network layer leaked errors. Proceed?` |
| 9 | **Stall-watch** check: `<state-dir>/stall-warning` file does NOT exist. The `stall-watch` background daemon (launched by `dispatch-codex` alongside `codex exec`) appends to this file when `<state-dir>/tail` has zero growth for at least 600 seconds (10 minutes by default). This is a low-confidence silence signal that can reflect long model generation as well as a CLI or network stall; it never kills the worker. File present = at least one silence period occurred. | Do not hard-fail; block silent advance and surface: `Auto-terminal stall-watch: N silence period(s) detected (first at T+<min>); output paused for 10+ min during run, and the worker was left running. Proceed?` |
| 10 | **Verdict-verification contract**: A labeled `PASS` or `BLOCK` verdict has a standalone `Verification status: VERIFIED` marker. An `UNVERIFIED` status or verdict, conflicting status or verdict values, `PASS` or `BLOCK` with a missing status, or `VERIFIED` with Verification notes that explicitly report no execution is invalid. A review with no labeled verdict remains accepted for compatibility. | Hard-fail intake and surface the exact `FAIL check-10 ...` detail. Do not treat the verdict as execution-backed. |
> Implementer note: pipes inside Check 7 and Check 8 regex patterns are escaped as `\|` where Markdown-table parsing requires it. The runtime regex engine (Python `re`, PowerShell `-match`, etc.) must receive unescaped alternation (`|`). The health-check script should compile patterns from a source-of-truth list rather than from the rendered Markdown.
**Outcomes**:
- Checks 1-6 and 10 all pass AND Checks 7, 8, and 9 all clear (0 suspicious phrases, 0 tool-failure markers, no stall-warning file present) → eligible for silent proceed (still subject to Substance heuristics and Phase 1d coordination below).
- Checks 1-6 and 10 all pass AND Check 7, 8, OR 9 hit (any marker or stall-warning present) → emit the corresponding one-line note(s); block silent advance; ask user to Proceed or Downgrade.
- Any of Checks 1-6 or Check 10 fails → refuse that reviewer's intake and surface the failed check. Route its dispatch through the Phase 1c failover queue and keep other reviewer slots active.
**Required dispatch state contract**: Missing or stale `<state-dir>/pre-mtime` or `<state-dir>/timestamp` is `FAIL` (Health check 2 freshness and the time-floor heuristic cannot be trusted). Missing `<state-dir>/tail` is `WARN check-8 1 missing-dispatch-tail` and blocks silent advance; it must NOT be silently treated as Check 8 hit 0. When `Review-Codex.md` is missing, Check `1` hard-fails while Check `8` and Check `9` still run against `<state-dir>/tail` and `<state-dir>/stall-warning`. A missing review file is exactly the case Check `8` exists to diagnose: a dispatch can spend a round retrying a shell spawn that always fails, write no review, and leave a growing tail. Reporting that state as tail-present-but-unscanned is incorrect. This replaces the pre-fix `PASS check-8 tail-present-not-scanned` and `check-9 stall-warning-present` output shapes; Check `9` now reports `N stall-periods` in both branches.
**Windows state-dir diagnostics**: In a PowerShell session, inspect state-dir files by running PowerShell code directly in the current shell, or by writing a temporary `.ps1` and invoking it with `-File`. Do not wrap diagnostics as `pwsh.exe -Command "foreach($f in ...) { ... }"` from inside an existing PowerShell shell. The outer shell expands `$f`, `$_`, and `$cutoff` before the inner process sees them, producing errors such as `Missing variable name after foreach` or `=: The term '=' is not recognized`. If a nested PowerShell process is unavoidable, use a temp `.ps1` file instead of inline `-Command`.
#### Substance heuristics (Auto-terminal only; soft signals; ANY hit blocks silent advance; never hard-fails)
The 10 structural Health checks above verify the file shape, dispatch signals, and verdict-verification marker. They do NOT catch a review that is structurally clean but substantively shallow (Codex's tools silently failed mid-run; `rate limit`; context overflow; or the model did not engage). Three lens-aware heuristics fire after the 10 Health checks and surface independently. **Any hit blocks silent advance**: the user is prompted "Proceed? / Downgrade?" before Phase 2 reads the file.
| Heuristic | Signal | Surface format |
|---|---|---|
| Time-to-completion floor | Elapsed wall time from `<state-dir>/timestamp` to `Review-Codex.md` finalization is less than 30s, when the dispatched prompt is ≥ 2000 chars | `Substance heuristic: review completed in <T>s for <P>-char prompt -- Codex may have bailed early. Spot-check before trusting.` |
| Anchor density | Review body > 1000 chars AND zero file-line anchors matching `:\d+`, `\bline \d+\b`, `\blines? \d+\s*[-–]\s*\d+\b`, or `<file>:<line>` patterns | `Substance heuristic: review has <N> chars and 0 file:line anchors -- may be generic prose without code/line grounding. Spot-check High findings.` |
| Scope-challenge engagement (plan-review lens only) | Review does NOT contain visible evidence of ALL three scope-challenge axes. Evidence per axis = `(a)` / `(b)` / `(c)` enumeration OR keyword coverage (case-insensitive): **Axis 1** (`scope` AND one of `smaller` / `larger`), **Axis 2** (one of `deferral` / `process tax` / `release cycle`), **Axis 3** (one of `simplest` / `do nothing` / `doing nothing` / `smaller path` / `shrink` / `docs only` / `document only` / `script only` / `no-op`). Axis-3 keyword set is intentionally broad to avoid false positives on substantive reviews. | `Substance heuristic: plan-review did not visibly engage scope-challenge axes <missing-axis-numbers>. May be incomplete per skill mandate.` |
Substance heuristics are skipped for Terminal-relay and Plugin path (the user has direct eyes on the run).
#### Phase 1d coordination (Auto-terminal-specific silent-intake rule)
For Terminal-relay, auto-watch `DONE` is sufficient signal to silently advance that reviewer into intake. For Auto-terminal, `DONE` is necessary but not sufficient, and the gates below apply to each reviewer. In a multi-reviewer round, consolidation starts only after every expected reviewer has a ready report or a confirmed failure. Phase 2 may silently advance a reviewer only when ALL of the following hold:
1. Auto-watch emits `DONE <path>` (file appeared with current round marker).
2. The dispatch subprocess exited 0 (Bash background task notification reports `exit code 0`), or the harness reported the task stopped and `await-review` then returned `REVIEW-READY`.
3. Health checks 1-6 all pass.
4. Health check 10 passes the verdict-verification contract.
5. Health check 7 hits 0 phrases.
6. Health check 8 hits 0 markers.
7. Health check 9 (stall-warning file absent) holds.
8. Substance heuristics flag 0 signals.
If 2, 3, or 4 fails for a reviewer, refuse that review and route its dispatch through the Phase 1c failover queue while other reviewer slots continue. If 5 hits at least 1 phrase, 6 hits at least 1 marker, 7 detects the stall-warning file, or 8 flags at least 1 signal, Phase 2 stops at a human checkpoint with `Proceed? / Downgrade?`; no silent advance. Auto-watch `TIMEOUT`, `REAP-UNKNOWN`, and `STALL` also stop at a human checkpoint. `STREAM-DEAD` is an Auto-terminal runtime failure handled by the downgrade rules above.
#### False-positive tuning principle
The Health checks (especially Check 7) and Substance heuristics are intentionally soft (surface + block silent advance, never hard-fail) because false positives are expected. Observed FP modes during dogfooding:
- Check 7 firing on Codex's own meta-discussion of the regex pattern list. Mitigated: Check 7 excludes content inside backtick code spans.
- Check 8 firing on Codex stdout echoing pattern strings, especially when /implement-review reviews the implement-review skill itself or any review prompt that names the patterns. Observed 12-152 markers across 4 dogfood runs (ac self-review r2/r3, random, NSF, Letter-) with 0 real tool failures in the same runs. Mitigated in three layers: (1) backtick-code-span exclusion, as Check 7; (2) Fix A, a line-level echo classifier that skips line-numbered source citations and literal regex-source lines; (3) Fix B, an intrinsic/generic split so terse real failures (a bare `Rate limit exceeded` or `HTTP/1.1 429` line) still count while bare ambiguous words need an error frame nearby. After these, a normal review whose tail has no failure vocabulary scans to 0 markers. A review whose subject IS the failure vocabulary (the implement-review skill itself) still surfaces a reduced count: prose discussion of intrinsic terms is indistinguishable from real failures, so the residual is the safe floor (real-failure-wins), recognized downstream via the breakdown shape. A live run on the preserved R1 self-review tail dropped 48 markers to 40 under this rule while every injected real-failure line still surfaced.
- **Check 8 firing on WSL-stub-bash 1312 burst** (Windows hosts only). When Codex's tool dispatcher tries `bash <script>` and PATH `bash` resolves to the WSL launcher stub (`C:\Windows\System32\bash.exe` or the Microsoft Store WSL alias under `WindowsApps\`), the stub brokers a syscall to the WSL service and requires elevation-token resolution that `CreateProcessAsUserW` cannot supply from Codex's spawn context. Result: bursts of `CreateProcessAsUserW failed: 1312` + `windows sandbox: runner error` envelope lines (typically 20-30 hits within <100 ms). This is a DISTINCT 1312 class from the workspace-write sandbox bug fixed by `--sandbox danger-full-access`; Codex falls back to alternative tools (PowerShell, Git native, or explicit Git Bash absolute path) and the review completes substantively. NOT mitigated in code: PATH-prepend approaches (PowerShell `$env:PATH` assignment and cmd `SET PATH=...;%PATH%` injected via the helper) both trigger Bitdefender's PATH-hijacking heuristic and block `dispatch-codex.ps1` from loading entirely, which is strictly worse than the noise. Mitigation is at the SIGNAL layer instead: Check 8 emits a per-pattern breakdown so downstream Claude can recognize the WSL-stub shape -- if the breakdown is dominated by `createprocessasuserw:N` + `windows:M` + `sandbox:M` (envelope co-occurrence) AND Substance heuristics pass AND review-mtime is fresh, treat as known graceful-fallback noise and Proceed without escalation. User-side resolution (optional): remove the WSL stub from PATH (`Get-Command bash` showing `System32\bash.exe` or `WindowsApps\` -> uninstall WSL or move `C:\Program Files\Git\bin` ahead of `System32` in PATH); Auto-terminal then emits 0 WSL-stub 1312 markers on subsequent runs.
- **Check `8` breakdown dominated by `107374:N` with `FAIL check-1`**. This is real signal: a spawned shell died during process initialization and was retried for the whole round. On Windows, the common cause is a `pwsh` command that resolves only to a Microsoft Store App Execution Alias. Install PowerShell `7` from the MSI on the [PowerShell GitHub releases page](https://github.com/PowerShell/PowerShell/releases), or remove the alias from `PATH`. The winget package now ships only the MSIX bundle, so `winget install Microsoft.PowerShell` creates this state. The dispatcher's `pwsh` probe in the Auto-terminal section prevents the round from being spent this way; this signal remains relevant for tails created before the probe or for other spawn-init failures.
- Substance axis-3 firing on synonym phrasings (`shrink to docs only` rather than `simplest path`). Mitigated: axis-3 keyword set broadened.
- Anchor density firing on a precise convergence review with low anchor count. Accepted as rare; one-click Proceed.
Operational principle: better to under-flag a real signal than to cry wolf so often the user clicks through every flag without reading. Alarm fatigue is not recoverable; once the user trains themselves to click past every surface, the silent-failure mitigation collapses. Track FP rate per check during the first ~10 Auto-terminal runs; if any check class exceeds 30% FP, tune the trigger criteria (broaden keywords, exclude additional contexts, raise size threshold) until signal-to-noise is workable.
---
### Phase 2.1: Partition and classify (all channels)
Reviewers are instructed to write their review to a `Review-<AgentName>.md` file in the repository root, using their own self-reported name (see Phase 1c). When every expected reviewer has a ready report or a confirmed failure, or the user says the reviewers are done, list the files matching `Review-*.md` at the repo root. Apply the two-axis partition described below (freshness + scope) to decide which files to read, and report any ignored files to the user.
Multi-reviewer consolidation: if two or more current-round review sources are available (current-round `Review-*.md` files and/or reviewer feedback the user relays directly), classify each new finding as:
- **Convergent** -- two or more reviewers raise substantially the same point. High confidence; treat as "will fix" unless wrong on the merits. If reviewers agree on the underlying problem but differ on severity, scope, or recommended remedy, classify as **Convergent with differences**: preserve each reviewer's severity and recommended fix in the consolidation report, and use the highest severity until the user or implementer resolves the difference.
- **Single-source** -- only one reviewer raises the point. Label the source (e.g., "from Review-Codex.md") when presenting.
- **Divergent** -- reviewers take opposite positions on the same finding. Flag the disagreement explicitly; present both sides; ask the user to decide.
Before trusting any file, verify that its `<!-- Round N -->` comment matches the current round number. Partition matching `Review-*.md` files along two axes.
**Freshness axis**: **current-round** (first line is `<!-- Round N -->`), **stale-round** (a different round marker), **empty**, or **unreadable** (cannot be read, or has a malformed or missing round marker).
**Scope axis** (if the expected reviewer set from Phase 1c is known): **expected** (the reviewer name extracted from the filename is in the expected set) or **unexpected** (the name is not in the set, possibly a leftover artifact from an earlier task that happens to share the round number).
Read and consolidate only files that are both **current-round** and **expected**. Report every ignored file by filename, grouped by reason (stale-round, empty, unreadable, unexpected), before presenting findings, so the user sees which reviewers produced usable output this round.
If a file is current-round but unexpected, flag it to the user before inclusion or exclusion: it may be a leftover from another task, or an additional reviewer the user invoked without announcing. Treat the file mtime against the recorded Phase 1c emission time as a secondary signal only, subject to clock skew, filesystem timestamp granularity, and editor-touch noise: mtime clearly older than emission weakly suggests a prior-task artifact; mtime at or after emission is consistent with a current-round file. Require at least one corroborating signal before including (e.g., file/diff scope matching current staged files, verification notes referencing current commands or files, the reviewer being named in the conversation). Do not silently include or exclude.
If the expected reviewer set is unknown, treat mtime as weak evidence only: use it to rank candidates, not to auto-classify. Ask the user to confirm which current-round files belong to this review before consolidating.
Keep a reviewer's slot pending while its dispatch is still active, and skip the steps below for a confirmed dispatch failure (see Path selection). If the expected reviewer set is known and any reviewer in it is not represented in the current-round + expected bucket (absent entirely, or its file is stale, empty, unreadable, or unexpected):
1. Present a reviewer-specific follow-up prompt the user can paste back into that reviewer, identifying which reviewer is missing so the user knows where to paste it: `Save your review to Review-<YourAgentName>.md in the repo root. Normalize your name: pick the stable product name, whitespace → one dash, keep only ASCII letters/digits/dashes, collapse repeated dashes, trim edge dashes. Examples: Review-Codex.md, Review-GitHub-Copilot.md, Review-Antigravity.md, Review-Claude-Code.md. Use Review-Unknown.md if the result is empty or you cannot identify yourself, and note the uncertainty at the top. Write the complete review to a temporary file in the same directory, then rename it over that filename rather than clearing the target first. Start with <!-- Round N -->. Begin with a "Verification notes" paragraph or short bulleted list. Separate findings into New and Previously raised (Fixed / Still open / Reopened / Deferred) sections. For High-priority findings, include an exact rewrite with file:line.`
2. If the file is still missing, still empty, or still carries a stale round marker after the follow-up, ask the user to paste that reviewer's feedback directly.
If only one current-round source remains after retry and direct-paste handling when multiple reviewers were expected, proceed with single-reviewer intake and label every finding as Single-source (no Convergent classification is possible without a second source). If the user did not invoke multiple reviewers this round, treat the single current-round source as the complete intake.
- When feedback arrives (from any `Review-*.md` file or relayed by the user), acknowledge each point.
- If a reviewer separated findings into "New" and "Previously raised" sections, verify the classifications. If a reviewer did not separate them (older prompts or non-compliance), do the separation yourself based on the round history.
- Categorize each **new** point as:
- **Will fix** -- clear, actionable, and correct.
- **Needs discussion** -- ambiguous or potentially wrong; ask the user before acting.
- **Disagree** -- explain why and let the user decide.
- For **previously raised** points, check the status the reviewer assigned:
- **Fixed** -- the reviewer confirms the prior finding was addressed. No action needed.
- **Still open** -- the fix did not land or was incomplete. Treat as "will fix" unless the user overrides.
- **Reopened** -- the reviewer re-raises a point that was marked Resolved. Flag to the user: this needs a decision, not silent re-litigation.
- **Deferred** -- the user chose not to address this. The reviewer acknowledges it as unchanged. No action unless the user reconsiders.
- **Verify factual claims (Phase 2.5)** for High-priority findings, and for Medium findings produced via the Auto-terminal embedded-diff retry channel, that make checkable factual assertions (citation existence, code behavior, link reachability, count or size, compile error). Verification outcomes (`Verified` / `Refuted` / `Inconclusive`) override or augment the categorization above: a Refuted finding is not applied, regardless of its original `Will fix` / `Needs discussion` category.
- Present the categorized list (including verification outcomes from Phase 2.5) and confirm with the user before making changes.
- For follow-up questions within the same review round, prepare a short prompt the user can paste into the reviewer.
## Phase 2.5: Verify Factual Claims (when triggered)
After Phase 2 categorization but before the user is presented with the categorized list, verify any High-priority finding that makes a checkable factual claim, and also verify any Medium finding from the Auto-terminal embedded-diff retry channel that makes a checkable factual claim. Reviewer assertions are not always correct; verifying first prevents revising in a wrong direction and produces an evidence trail when the claim is refuted. This phase is the inverse failure mode of "blindly accept reviewer feedback": a confidently-worded but wrong finding can otherwise propagate into the next round and waste both sides' time.
### Trigger
Verification fires for a finding when **all** of the following hold:
1. The finding is tagged High priority, OR the finding is tagged Medium **and** the review was produced via the Auto-terminal embedded-diff retry channel (see Phase 1c), OR the user explicitly requests verification on a Medium / Low finding.
2. The finding makes an objectively checkable factual claim — citation existence, code behavior, link reachability, page-limit or word-count assertion, compile or type error, anonymization leak, etc.
3. The reviewer's "Verification notes" section did not already cover this specific claim with a method that resolves it. If the reviewer ran a check that covers the claim, trust it unless contradicting evidence appears during fix application.
4. The cost of verification is reasonable relative to the cost of applying the fix blindly. Skip verification when the proposed fix is trivially correct (a one-line typo) or when the cheapest verification path takes longer than the user can wait on a single finding.
Skip entirely for stylistic, opinion-based, or structural findings (term choice, paragraph order, tone, organization). Those have no factual ground truth to verify against.
**Why Medium-from-retry-channel is mandatory-verify**: the embedded-diff retry hands Codex only the staged diff hunks, not the surrounding file context. A retry-channel reviewer that flags "X is missing" or "X is wrong" may be reasoning correctly from the visible hunks while X actually exists or is correct in a non-diff line. Refuted findings of this shape have already been observed in dogfooding (e.g., a Medium claim that a regex set was case-sensitive, which turned out to be Refuted because the scanner applied `re.IGNORECASE` at compile time in a non-diff line). Phase 2.5 verification on every Medium from the retry channel turns this Refuted class into a hard pre-apply step instead of relying on the implementer to remember.
### Methods
Pick the cheapest applicable method per claim type:
| Claim type | Verification method |
|---|---|
| Citation / paper / author existence | `WebSearch` for title and author; `WebFetch` arXiv / DOI URL; cross-check against the project's `.bib` file |
| Code behavior assertion | Run the targeted test (`pytest path::test_name`); read the source to trace; eval a small repro snippet |
| Link reachability | `WebFetch` of the URL; HEAD request via curl |
| Page limit / word count / file size | `wc -w`, `wc -l`, `ls -la`; compile and read the LaTeX log |
| Compile-time / type error | Run the compiler (`latexmk`, `tsc`, etc.) or type checker (`mypy`, `pyright`) on the affected file |
| Anonymization or leak claim | `Grep` for the alleged term; cross-check against the project's de-anonymization checklist |
If no listed method applies, ask the user before either accepting the claim or pushing back.
### Outcome
Record one of three outcomes for each verified claim:
- **Verified** — the reviewer's claim is correct. Proceed with the fix as a normal `Will fix` item.
- **Refuted** — the reviewer's claim is wrong, or the issue does not reproduce. **Do not apply the fix.** Push back via a follow-up reviewer prompt that cites the verification evidence (command run, output, URL fetched, file path inspected). The user may override this by asking to apply anyway, but the default is to push back.
- **Inconclusive** — verification could not run (no internet, no test runner, claim under-specified) or returned ambiguous results. Surface the ambiguity to the user and let them decide.
### Round history integration
Findings flagged Refuted or Inconclusive enter the next round's `Prior findings` block (see Phase 1b item 8) so the reviewer sees the evidence and can either retract the claim or sharpen it. Refuted findings appear as `(Refuted in round N — <one-line evidence>)`. Inconclusive findings appear as `(Inconclusive in round N — <reason>)`, prompting the reviewer to supply a more specific or pre-verified check.
### Example
Round 2 of a paper review. Reviewer flags High: "Citation [Smith2023] does not exist; could not find this paper on arXiv or Google Scholar."
1. Trigger fires (High priority + checkable factual claim).
2. Verification path: `WebSearch "Smith 2023 <topic keywords>"`, then `WebFetch arxiv.org/abs/<id>` if a candidate surfaces.
3. Outcome:
- If a real paper is found → **Refuted**. Reply to the reviewer with the URL and arXiv ID, ask them to confirm before re-flagging. Do not edit the citation.
- If no paper is found after a reasonable search → **Verified**. Remove the citation or replace it with a real one.
4. Record in round history: `[Smith2023] reference (Verified in round 2 — not present on arXiv or Google Scholar)` or `[Smith2023] reference (Refuted in round 2 — arxiv.org/abs/2023.XXXXX confirms paper)`.
## Root Review Sink (per reviewer)
When a review produces substantial written feedback, each reviewer saves the latest review to `Review-<AgentName>.md` in the repository root. Normalize `<AgentName>` as follows: choose the stable agent or product name visible to the user (not a transient model/version list unless that is the only identity available); convert any run of whitespace to a single dash; delete every character except ASCII letters, digits, and dashes; collapse repeated dashes; trim leading and trailing dashes. Examples: `Codex` → `Review-Codex.md`, `GitHub Copilot` → `Review-GitHub-Copilot.md`, `Antigravity` → `Review-Antigravity.md`, `Claude Code` → `Review-Claude-Code.md`. If the normalized result is empty or a reviewer cannot identify itself with reasonable confidence, it uses `Review-Unknown.md` and notes the uncertainty at the top of the file.
One file per reviewer, one round per file. Treat each file as a reusable scratch file for the current review round, not as a permanent archive. By default, publish each new round by writing a complete temporary file beside the target and atomically renaming it over the reusable review file; do not create per-directory review files, and do not append multiple rounds, unless the user explicitly asks to preserve history. Running two reviewers in the same round produces two files (e.g., `Review-Codex.md` and `Review-GitHub-Copilot.md`), which Phase 2 reads together and consolidates.
Legacy `CodexReview.md` files from pre-upgrade sessions are ignored by Phase 2 intake. If one is present in the repo root or under `docs/`, treat it as stale scratch output from the old single-reviewer flow unless the user explicitly asks to inspect it.
The purpose of the `Review-*.md` files is to let the user and Claude Code read, reuse, and forward the latest review(s) without copy-pasting from chat. Keep each file in plain Markdown and make it directly useful on its own. Include:
- a `<!-- Round N -->` HTML comment on the first line (used by Phase 2 to verify freshness)
- a `Verification notes` paragraph or short bulleted list at the top of the review (immediately after `# Review`), stating what was compiled, run, or verified; write "none" if nothing at runtime
- the file or diff scope reviewed
- the review lens or context
- findings separated into **New** and **Previously raised** sections (previously raised items tagged Fixed, Still open, Reopened, or Deferred; on Round 1 the Previously raised section may be omitted or shown as "None")
- concrete recommended changes, with exact values when relevant
- for any finding flagged High priority, an exact suggested rewrite with file path and line range (use a fenced code block for multi-line rewrites)
Do not stage, commit, or move `Review-*.md` files unless the user explicitly asks. Before the first review round, check whether `Review-*.md` is excluded from git. Look in `.gitignore` and `.git/info/exclude`. If the pattern is not excluded anywhere, append `Review-*.md` to `.git/info/exclude` (a local, untracked ignore file) so that `git add -A` during the revision flow does not accidentally stage scratch files. A repo that already ships `Review-*.md` in the committed `.gitignore` (as this repo does) satisfies the exclusion requirement without a local edit.
## Phase 3: Revise
- Address all "will fix" points and any "needs discussion" points the user approved.
- Update the round history: mark addressed findings as `Resolved`, keep unaddressed ones as `Still open`, and tag user-deferred items as `Deferred`. This history carries forward into the next round's prompt (Phase 1b, item 8).
- Stage the revised changes.
- Return to Phase 1 with an incremented round number.
## Phase 4: Conclude
The loop ends when:
- The user says the review is done or approved.
- No reviewer raises actionable issues.
- The user decides to stop iterating.
At conclusion, present a short summary: total rounds, key changes made, and any unresolved points from the last review.
## When Not To Use
- Trivial changes where review adds no value (typo fixes, config tweaks).
- Changes that require running tests or builds to validate -- run those first, then review.
- When the user wants a single-shot review with no revision loop; just ask a reviewer directly.