agentleFS
EfiPerezEfiPerez/claude-code-skills/skills/codec-review/SKILL.md

codec-review

Skill4 starsChanged 11 days agoMITView on GitHub

Runs a bounded adversarial review loop between Claude (builder and arbiter) and OpenAI Codex (newest GPT Sol model, read-only critic) over a document or codebase. Codex attacks the work and returns a structured verdict; Claude judges each finding, rewrites the target, and re-submits to the SAME Codex thread until Codex approves or the round cap is hit (5 by default). Runs silently: the only question the user is asked is whether to spend more rounds at the cap. Output: one report file. Use when the user says "/codec-review", "codec review this", "adversarial review", "have Codex review this", "stress-test this plan", "argue this with Codex", "get a second model to check this", or is about to build something expensive to get wrong (auth, schema, migrations, concurrency, payments, personal data). Reviews only; never writes implementation code. To have Codex BUILD a frozen spec, use codec-build. For a fast one-shot critique of a git diff with no loop, the Codex plugin's /codex:adversarial-review is cheaper.

  • Reads credentials
  • Installs packages

The file

Show its rules and text

We split this skill into the rules it gives an agent

14 rules. About 0 are common, 10 are distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈10

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Every command block below comes in two forms.

    line 32
  2. Distinctive:

    Windows-only: does not apply on macOS or Linux.

    line 38
  3. Distinctive:

    Run silent: No per-round status in the conversation.

    line 47
  4. Very specific:

    Arbitrate in the main session, never a subagent: If you set CLAUDE_CODE_SUBAGENT_MODEL (for example to sonnet), an "Opus" subagent may …

    line 52
  5. Distinctive:

    Shell access: No Bash or PowerShell tool in this session (Claude Desktop's Chat tab, claude.ai, mobile) → this skill cannot run here: it …

    line 95
  6. Distinctive:

    When check 2 or 3 fails (or a re-run after a pre-work Codex failure still fails), first look for any other working route to Codex: a …

    line 111
  7. Distinctive:

    If it turns out to be the main session's own model, the run is still valid (fresh context, no shared reasoning) but the report says so.

    line 136
  8. Distinctive:

    Never assume the last round finished.

    line 157
  9. Very specific:

    Lock: Create <project>/adversarial-review/.lock exclusively — never check-then-write.

    line 158
  10. Distinctive:

    When the target is a plan, instruct Codex to verify each claim against the repository and flag any step assuming a file, function or …

    line 210
  11. Very specific:

    Not JSON → strip fences, then take the last match of VERDICT: APPROVED|REVISE on a line of its own (case-insensitive).

    line 343
  12. Distinctive:

    No sign-off question: after the cap question, Claude decides and that is it.

    line 432
  13. Very specific:

    Use the path verbatim; skip the scope question.

    line 489
  14. Distinctive:

    Update the record with the bundled helper, at three points:

    line 493

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

codec-review — adversarial review loop

If a Codex call fails before any review work starts, re-run Step 0's checks, then read references/troubleshooting.md. The Windows sandbox/ACL failure and the two model-slug failures all surface as an identical hard error from Codex's stderr, and that file is the only place the three are told apart. It does not load by itself.

Claude is builder and arbiter. Codex is a read-only critic that can read files but cannot write one. They argue through a working copy plus one persistent Codex thread.

The user is asked exactly one question, and only if the loop hits its cap (plus, only when Codex can't be reached, the Step 0a same-family offer before the run starts). Nothing else is escalated, confirmed, or reported mid-run. The user does not want to referee the debate — they want the converged artifact and a one-page table of what changed.

This file is self-contained: every command the loop needs is below. codec-loop.md is reference for when something breaks — the failure playbook, recovery order, and the reasoning behind each command form. Read it on failure or when changing this skill, not on every run.

Adapted from chaseai-yt/claudex-loop's codex-review (MIT — see the repository's NOTICE). Chase's grill-me-codex / grill-with-docs-codex / claudex-loop skills are out of scope.

Which shell

Every command block below comes in two forms. In the PowerShell tool (Windows) use the PowerShell block. In the Bash tool (macOS or Linux; Git Bash on Windows also works) use the bash block. Never mix the two in one call, and run each block as a whole — the $-variables and the shell's working directory do not survive between tool calls, so re-assign them at the top of every call. The bash blocks use only bash, POSIX tools that ship with macOS, git and node. They were tested under Git Bash on Windows; macOS itself is untested. Advice marked Windows-only does not apply on macOS or Linux.

SKILL_DIR below means the folder of this skill: the path the skill loader printed as "Base directory for this skill" when it loaded SKILL.md. Never guess it.

Hard rules

  • Rewrite the target. Never annotate it. This is the rule that decides whether the loop is cheap or ruinous — see "Why rewriting is the whole trick" below.
  • Run silent. No per-round status in the conversation. One line at the end with the report path. A round that found nothing new is not news.
  • Never write implementation code.
  • Never approve on Claude's own judgement. Approval comes only from Codex (or, in same-family mode, the critic subagent — Step 0a), and only when all five invariants below hold.
  • Arbitrate in the main session, never a subagent. If you set CLAUDE_CODE_SUBAGENT_MODEL (for example to sonnet), an "Opus" subagent may silently be a smaller model. The only legitimate subagent is a read-only Explore gatherer on a very large diff, which condenses and never decides.
  • No per-run exposure prompt — but know what this does. Codex sends whatever it reads to OpenAI, under the user's own account. -s read-only restricts writes only; it does not confine reads to the project. Codex can read any file the user's account can read: the project's .env and data files, and also files elsewhere such as home-directory credentials and global config. Do not raise the topic on every run — the repository README carries the warning — and never describe scope exclusions as enforced: they are cost and noise hygiene, not a boundary. The user should run this skill only on projects and machines where that exposure is acceptable.

Tunables

VarDefaultMeaning
MAX_ROUNDS5Hard cap. Raising it is the user's decision, asked once at the cap.
EFFORThighCodex reasoning effort.
MODELresolvedNewest-version listed gpt-<N>-sol / gpt-<N.M>-sol model in ~/.codex/models_cache.json (Sol family only — never Astra/Luna/Terra or -mini variants). Like opus/sonnet aliases: a new Sol release is picked up with no edit. Missing or unreadable cache → pass no -m and let Codex use its own default.

Honour rounds=N if passed. Do not echo the resolved values — log them.


Step 0 — Can this app reach Codex?

A one-time setup check per computer, shared with codec-build. Stop at the first check that fails: tell the user in two or three plain sentences what you found and what to do next. Check 1 failing ends the run. Checks 2–3 failing end it too, unless the user takes the same-family offer in Step 0a.

  • Check 1 is free and always applies — it only looks at this session's tool list.
  • Checks 2–3 run only when ~/.claude/state/codec/codex-ready.json is absent. When both pass, write that file ({"codex_version": "...", "checked": "<ISO date>"}, creating the folder if needed) and continue silently. When it exists, skip them.
  • If any later Codex call fails before review work starts, delete the marker and re-run checks 2–3 before reading references/troubleshooting.md — a since-logged-out or uninstalled Codex must get this plain explanation, not a raw error.
  • Any failed check 2–3 deletes the marker — no exceptions, even when the cause looks temporary or session-specific (a probe kept it once, reasoning "the real setup is probably fine"). The marker means "verified working", never "probably fine".
  1. Shell access. No Bash or PowerShell tool in this session (Claude Desktop's Chat tab, claude.ai, mobile) → this skill cannot run here: it drives the Codex command-line program on this computer and reads and writes project files, and this app can reach neither. Next step: open the project in Claude Code — the Claude Desktop Code tab with a Local session, a terminal, or VS Code — and run the skill again. If a Codex-looking MCP tool is connected, say it isn't used: OpenAI removed the official codex mcp-server, and the loop needs local files anyway.
  2. codex --version. Fails → Codex isn't installed or isn't on PATH. Next step: npm install -g @openai/codex (needs Node.js), then codex login. With the Codex plugin for Claude Code installed, /codex:setup checks the same things.
  3. codex login status. Non-zero exit / Not logged in → Next step: the user runs codex login in a terminal (it opens a browser; a ChatGPT plan or an API key works). Never run codex login yourself — it is interactive.

Step 0a — No Codex connection: offer same-family mode

When check 2 or 3 fails (or a re-run after a pre-work Codex failure still fails), first look for any other working route to Codex: a connected MCP tool or plugin that can run a Codex review on local files. None of the known ones can drive this loop (OpenAI removed codex mcp-server; the loop needs --output-schema, thread resume and local file reads) — so in practice the answer is "no connection found". Then, in the same message as the fix-it steps, offer:

No connection to Codex was found (no working Codex command-line program, and no other Codex connection this skill can use). You can fix that with the steps above and run the skill again. Or reply "use Claude" and I'll run the review with a different Claude model as the critic instead of a different AI company's model. That is weaker — models from the same family share more blind spots and agree more easily — but it is still a valid, independent review: the critic sees only the target, never my reasoning.

This is the one extra question this skill may ask. No answer, a headless run, or anything but a clear yes → end the run. Never switch silently, and never offer it while Codex works. Same-family mode never writes the codex-ready.json marker. It also sends nothing to OpenAI: the critic runs on the same Claude account as the session.

What changes in same-family mode (everything else — staging, working copy, rewriting, arbitration, the five approval invariants, the cap question — is unchanged):

  • Critic = an Agent call with subagent_type: "Plan" (read-only: no Edit/Write) and a model different from the main session's (session on Opus → sonnet; on Sonnet → opus). CLAUDE_CODE_SUBAGENT_MODEL can override the requested model — so the critic's brief asks it to state its exact model id in summary's first line, and the report records what it said. If it turns out to be the main session's own model, the run is still valid (fresh context, no shared reasoning) but the report says so.
  • Brief = the same references/review-prompt.md charge, pointing at the working copy's absolute path, plus: "End with ONLY a JSON object matching the schema at <SKILL_DIR>/references/review-output.schema.json" (give its absolute path). No codex exec, no model resolver, no tune.
  • Rounds = round 1 starts the critic; later rounds continue the same agent with SendMessage (its own earlier critiques stay in its context — the thread rule). If the agent can't be continued, start a fresh one with the previous findings and settled ids in the brief, and log it.
  • Parse = the critic's final message, with the same fail-closed ladder (JSON → regex fallback that never yields approved → unparsed). Approval still comes only from the critic, never the main session.
  • Report header line: · Critic: Claude <model id> (same-family mode — no Codex connection; weaker than a cross-vendor review, still independent) in place of · Codex <model>. Log critic: claude-<model> in the run log frontmatter.

Step 1 — Preflight

  1. Resume check. Scan <project>/adversarial-review/*.md for status: in-progress with a matching target. Found → recover per codec-loop.md §6 and continue from its thread_id. Never assume the last round finished.
  2. Lock. Create <project>/adversarial-review/.lock exclusively — never check-then-write. Held → prove no live process matches its nonce before breaking it (§6).

    PowerShell:

    $lock = '<project>\adversarial-review\.lock'
    New-Item -ItemType Directory -Force -Path (Split-Path $lock) | Out-Null
    $fs = [System.IO.File]::Open($lock, 'CreateNew', 'Write', 'None')   # throws if it exists
    $b = [Text.Encoding]::UTF8.GetBytes($run_id); $fs.Write($b, 0, $b.Length); $fs.Dispose()

    bash (noclobber makes > fail if the file exists):

    lock='<project>/adversarial-review/.lock'
    mkdir -p "$(dirname "$lock")"
    if ( set -o noclobber; printf '%s\n' "$run_id" > "$lock" ) 2>/dev/null; then
      echo "lock acquired"
    else
      echo "lock held: $lock"
    fi

    Release it (Remove-Item -LiteralPath $lock -Force / rm -f "$lock") on every terminal path, including error and deadlock.

  3. Create the run: run_id, safe_id, .scope-<hex>/, raw/. Log the resolved model, version and binary path.

Step 2 — Resolve the input

  • Absolute path → verbatim. Relative → against the project root.
  • Bare filename → search the project. One hit → use it. Several → list and ask. None → stop; never substitute a similarly-named file.
  • A codebase → ask once: whole project / uncommitted only / specific paths. In a non-git folder "uncommitted" is unavailable — say so and re-ask; never silently widen to whole-project. Untracked files count: check git status --short --untracked-files=all and git diff --shortstat and --shortstat --cached, or a new feature in new files reads as "nothing to review".

Copy the target into the staging dir as the working copy. The original is untouched until Step 5. Record target, target_sha256, working_copy.

Step 3 — The loop

Round 1 opens the thread; later rounds resume it. Per round:

1. Write the brief. Round 1: the adversarial charge in references/review-prompt.md. Later rounds: what changed, plus your per-finding verdicts in one line each, and ask Codex to put resolved ids in settled. Do not restate the target in the brief — Codex re-reads it from disk, so pasting it doubles the round's cost for nothing.

When the target is a plan, instruct Codex to verify each claim against the repository and flag any step assuming a file, function or behaviour that does not exist. A plan reviewed in isolation yields only generic objections.

2. Prepare. Assert the preconditions — a missing brief costs a real round.

PowerShell:

$SKILL_DIR = '<the "Base directory for this skill" path>'
$SCHEMA = Join-Path $SKILL_DIR 'references\review-output.schema.json'
$attempt_nonce = [guid]::NewGuid().ToString('N')      # persist to frontmatter NOW
$stem  = "$raw\$safe_id-$attempt_id-$attempt_nonce"
$brief = "$stem-brief.md"; $last = "$stem-last.json"
$exit  = "$stem-exit.txt"; $jsonl = "$stem.jsonl"

$mc = Join-Path $env:USERPROFILE '.codex\models_cache.json'
# The slug allowlist is load-bearing - do not widen it to take whatever is
# top-priority. models_cache.json is written and overwritten by Codex itself, so
# a Codex update can float an unrunnable model to the top: on 2026-09-15
# gpt-6-astra surfaced here and a review round launched with -m gpt-6-astra,
# dying on "requires a newer version of Codex" even with config.toml on sol.
# Allowlisting means the next such model also can't be auto-selected. Always
# the NEWEST Sol by version number, not by Codex's priority - so gpt-6.1-sol
# beats gpt-6-sol, and a future gpt-6.2-sol/gpt-7-sol is taken automatically.
# Version is parsed, so 6.10 > 6.9. Full incident: references/troubleshooting.md.
$model = ((Get-Content -LiteralPath $mc -Raw -Encoding utf8 | ConvertFrom-Json).models |
          Where-Object { $_.visibility -eq 'list' -and $_.slug -match '^gpt-\d+(\.\d+)?-sol$' } |
          Sort-Object { [version](($_.slug -split '-')[1] + '.0') } -Descending |
          Select-Object -First 1).slug
$tune = @(); if ($model) { $tune += @('-m',$model) }
if ($effort) { $tune += @('-c', "model_reasoning_effort=`"$effort`"") }

if (-not (Test-Path -LiteralPath $brief))        { throw "brief missing: $brief" }
if ((Get-Item -LiteralPath $brief).Length -eq 0) { throw "brief empty: $brief" }
if (Test-Path -LiteralPath $last)                { throw "output exists: $last" }

bash:

SKILL_DIR='<the "Base directory for this skill" path>'
SCHEMA="$SKILL_DIR/references/review-output.schema.json"
attempt_nonce=$(node -e 'process.stdout.write(require("crypto").randomBytes(16).toString("hex"))')  # persist to frontmatter NOW
stem="$raw/$safe_id-$attempt_id-$attempt_nonce"
brief="$stem-brief.md"; last="$stem-last.json"
exitf="$stem-exit.txt"; jsonl="$stem.jsonl"

# Newest Sol model by version number (same allowlist and numeric sort as the PowerShell
# resolver above; 6.10 > 6.9). Prints nothing when the cache is missing or unreadable.
resolve_sol_model() {
  node -e '
    const fs = require("fs"), os = require("os"), path = require("path");
    try {
      const mc = path.join(os.homedir(), ".codex", "models_cache.json");
      const models = JSON.parse(fs.readFileSync(mc, "utf8").replace(/^\uFEFF/, "")).models || [];
      const rx = /^gpt-(\d+(?:\.\d+)?)-sol$/;
      const sol = models
        .filter(m => m.visibility === "list" && rx.test(m.slug))
        .map(m => ({ slug: m.slug, v: m.slug.match(rx)[1].split(".").map(Number) }));
      sol.sort((a, b) => {
        for (let i = 0; i < Math.max(a.v.length, b.v.length); i++) {
          const d = (b.v[i] || 0) - (a.v[i] || 0);
          if (d) return d;
        }
        return 0;
      });
      if (sol.length) process.stdout.write(sol[0].slug);
    } catch (e) {}
  '
}
model=$(resolve_sol_model)
tune=()
if [ -n "$model" ];  then tune+=(-m "$model"); fi
if [ -n "$effort" ]; then tune+=(-c "model_reasoning_effort=\"$effort\""); fi

die() { echo "$*" >&2; exit 1; }
[ -f "$brief" ] || die "brief missing: $brief"
[ -s "$brief" ] || die "brief empty: $brief"
[ ! -e "$last" ] || die "output exists: $last"

3. Launch with run_in_background: true and timeout: 7200000 (rounds exceed the 10-minute foreground ceiling, and a background command stops at 30 min unless timeout is set; max 2 h). Re-assign $cwd_root, $brief, $last, $exit, $jsonl, $SCHEMA, $tune at the top of this call (or paste the block above in front of it) — variables do not carry over from the Prepare call.

PowerShell:

$ErrorActionPreference = 'Continue'      # native stderr must not abort the call
Set-Location -LiteralPath $cwd_root      # = staging_dir; SAME every round
$prompt = "Read the brief at '$brief' and follow it exactly."

if ($round -eq 1) {
  codex exec --skip-git-repo-check -s read-only -C "$cwd_root" `
    @tune --output-schema "$SCHEMA" --json -o "$last" $prompt > "$jsonl"
} else {
  codex exec resume $thread_id --skip-git-repo-check -c sandbox_mode="read-only" `
    @tune --output-schema "$SCHEMA" --json -o "$last" $prompt > "$jsonl"
}
$LASTEXITCODE | Set-Content -LiteralPath $exit -Encoding utf8   # persist BEFORE anything else

bash:

cd "$cwd_root" || exit 1                 # = staging_dir; SAME every round
prompt="Read the brief at '$brief' and follow it exactly."

if [ "$round" -eq 1 ]; then
  codex exec --skip-git-repo-check -s read-only -C "$cwd_root" \
    "${tune[@]}" --output-schema "$SCHEMA" --json -o "$last" "$prompt" > "$jsonl" < /dev/null
else
  codex exec resume "$thread_id" --skip-git-repo-check -c sandbox_mode="read-only" \
    "${tune[@]}" --output-schema "$SCHEMA" --json -o "$last" "$prompt" > "$jsonl" < /dev/null
fi
echo $? > "$exitf"                       # persist BEFORE anything else
  • -c sandbox_mode= on every resume. resume rejects -s, so without it Codex inherits ~/.codex/config.toml, which could let a review write files.
  • Pass the thread UUID explicitly. Never resume --last — cwd-scoped, and it will silently resume an unrelated session, which looks exactly like success.
  • Capture thread_id from {"type":"thread.started",...}. No id → abort after round 1. A fresh thread per round loses Codex's own prior critiques and re-litigates settled points forever — a loop that converges on nothing while looking like it works.
  • On an unknown-model or unsupported-effort error, retry once with no -m/effort override ($tune = @() / tune=()) as a new attempt_id and nonce (never reuse an output path; it destroys evidence).

4. Parse — fail closed.

  1. Exit code ≠ 0 → hard stop, log the stderr tail, no verdict. (A run can exit 0 while printing ERROR to stderr, so stderr alone never means failure.)
  2. Parse $last as JSON → read verdict.
  3. Not JSON → strip fences, then take the last match of VERDICT: APPROVED|REVISE on a line of its own (case-insensitive). PowerShell: (?im)^\s*VERDICT:\s*(APPROVED|REVISE)\s*$. bash: grep -Eio '^[[:space:]]*VERDICT:[[:space:]]*(APPROVED|REVISE)[[:space:]]*$' "$last" | tail -1.
  4. Both tokens → revise. No match → unparsed; re-ask once on the same thread, then stop.
  5. The regex fallback may only yield revise or unparsed — never approved.

Take the last agent_message, never the first. Codex sometimes emits a schema-valid envelope carrying a statement of intent before reading anything — observed as {"verdict":"approved","summary":"I'll verify … then re-read …","findings":[]} as message one of a round whose real verdict, 19 items later, was revise with 3 findings. Treat an approved with empty findings arriving before any command_execution item as unparsed: Codex cannot have approved a target it never opened.

Ignore cosmetic item.type: error warnings that plugins emit every run, such as clamping SessionEnd hook timeout and Skill descriptions were shortened.

Approval is validated, not reported. Strict mode ignores description, so the schema permits approved beside a full findings array. Reject it unless all five hold:

  1. $last is attempt-specific and never used before. Missing file = error, never approval.
  2. findings empty. 3. next_steps empty. 4. Exit 0, no fatal JSONL event.
  3. The ledger reconciles — every id from any earlier round is accepted (and addressed), rejected (with cited evidence), or in Codex's settled.

Any failure → treat as revise and log which invariant broke.

5. Arbitrate, in one table row per finding. accepted / rejected / escalated, with a one-line reason. A rejection cites a file:line or a test result, not an assertion. Keep rejected (you judge it wrong) distinct from escalated (a human decision) — recording an escalation as a rejection is how a real risk becomes invisible. Prose essays per finding are how a run gets expensive; the reason is one sentence.

6. Rewrite the working copy, append the round row, increment. If the plan-review mod started this run, renew its record now (Step 6).

Why rewriting is the whole trick

The first run of this skill took seven rounds and grew its target by 1,077 lines, because each round annotated the target with a [CORRECTED] block instead of fixing the text. Three things went wrong at once, and they compound:

  • The target grew 1,913 → 2,990 lines, so every later round paid to re-read the record of the earlier ones.
  • Each block quoted a superseded rule in prose sitting next to its replacement, so the annotations became the contradiction surface. Rounds 4–6 were almost entirely findings against the previous round's own fixes. Findings went 10, 7, 7, 5, 5, 5 — flat, not converging.
  • The deliverable became a log of its own review, and a separate clean plan had to be written afterwards anyway.

So: state each rule once, in the present tense, as if it had always been that way. The correction history goes in the run log, which is the only place anyone needs it. When the fix touches more than one section, rewrite the whole section or the whole file in one pass — every such rewrite in that run settled several findings and regressed none.

Two traps that rewriting introduces, both of which cost a round:

  • Sweep the parts you did not rewrite. A rewrite changes the whole document's consistency surface. Round 7 found a superseded rule sitting in an untouched section, contradicting its replacement in the rewritten one.
  • Before deleting a correction block, check whether it defines anything. A [CORRECTED] header makes a load-bearing block look like commentary. That same rewrite deleted the sole definition of a required schema enum and left two pointers aimed at removed text. Grep for inbound references first.

Sweep flattened, so a line-wrapped survivor cannot hide (bash; run it with the Bash tool, or in Git Bash on Windows):

tr '\n' ' ' < "$file" | tr -s ' ' | grep -o "the phrase you superseded"

Step 4 — Termination

  • approved + all five invariants → Step 5.
  • Cap reached → the one question. Ask the user whether to spend N more rounds, and give a recommendation: recommend continuing when findings are still falling round over round or an open finding is high; recommend stopping when the count is flat across two rounds, since that means the loop is re-litigating rather than converging. Log the answer as a human override. If the user declines, go to Step 5 with status: deadlock-cap-reached.
  • Error or unparsed → stop and surface it. unparsed is never an approval. If the plan-review mod started this run, remove its record entry (Step 6) so the next approval asks for a review again.

Say which kind of deadlock it is: cap-reached and irreconcilable mean different things, and only the second implies the two models actually disagree.

Step 5 — Finish. Claude decides.

No sign-off question: after the cap question, Claude decides and that is it.

  1. Re-hash the original and compare to target_sha256. Match → overwrite it with the converged copy and write a .bak alongside (never clobber an existing .bak — suffix it). Mismatch → do not write. It changed under us; leave both files and say so in the report.
  2. If the target is a plan and a handoff skill is installed (the session-handoff plugin from this marketplace provides one), append "Run the handoff skill" as its final task, in prose. No handoff skill installed → skip this step.
  3. Write the report (below). Set status, release the lock. If the plan-review mod started this run, mark its record reviewed (Step 6) — on convergence and on deadlock alike.
  4. Say one line in the conversation: the verdict, the round count, and the report path. Nothing else. If a high finding is unresolved, add one sentence naming it.

The report — ~/.claude/reports/<run_id> report.md

Central, not per-project — one place to scan across every project's reviews, rather than a folder buried in each one. run_id already carries a project-descriptive slug plus a 4-hex nonce (2026-09-02 11-28-50 pre-deploy-review 4119), so the filename alone identifies which project and run it came from with no separate index needed. Create ~/.claude/reports/ if it doesn't exist yet. The run log, raw round artifacts, and staging copy stay exactly where they are, under <project>/adversarial-review/ — only this one human-readable summary moves.

Written for someone who did not watch the debate. A table, then at most three short notes.

# <target> — adversarial review

**<approved | deadlock, cap reached>** after N rounds · Codex <model> (or the Step 0a critic line) · <date>
Project: <project path> · Converged file: <path> · Backup: <path>
Run log: <project>/adversarial-review/<run_id> review.md

| # | Topic | Resolution |
|---|---|---|
| 1 | Readiness banner claimed an unverified state | Fixed — renamed, per-gate states |
| 2 | Codex read boundary described as enforced | Fixed — reframed as mitigation |
| 3 | Elevated bypass escapes rollback | Open — needs the user's call, hatch kept |

**Still open:** one line each, or "nothing".
**Unverified:** anything changed after the final round, or "nothing".
**Accepted risks:** one line each, or "nothing".

One row per topic, not per finding — three rounds arguing the same rule is one row. Resolution is Fixed — <what changed>, Open — <why>, or Rejected — <evidence>. No round numbers, no finding ids, no Codex quotes. The full per-round record, with Codex's raw final message at a deadlock, stays in the run log for whoever needs to reopen a decision.

Step 6 — When the plan-review mod started the run

The optional plan-review mod (/plugin install plan-review@efi-mods, from the EfiPerez/claude-code-mods marketplace) saves each plan you approve in plan mode to plans/<slug>.md and asks for this skill with that absolute path. On Windows it also keeps a review record, plans/.codec-reviewed.json, keyed by the SHA-256 of the saved plan, so an unchanged plan is not reviewed twice. Its entry stays reviewing only while a 20-minute lease is live — so this skill must keep that lease alive, and close the entry when it finishes.

  • Use the path verbatim; skip the scope question. Never call ExitPlanMode, and never begin implementing the plan you just reviewed.
  • target_sha256 (Step 2) must be the SHA-256 of the plan file's raw bytes, taken before Step 5 overwrites it — that is the record's key.
  • Update the record with the bundled helper, at three points:

    WhenAction
    Start of the run, then after every round (Step 3.6)renew
    Step 5.3, converged or deadlockedreviewed
    Error, unparsed, or the run is abandonedremove

    PowerShell:

    $SKILL_DIR = '<SKILL_DIR>'; $plan = '<absolute plan path>'
    $hash = (Get-FileHash -LiteralPath $plan -Algorithm SHA256).Hash.ToLower()   # Step 2 only
    node (Join-Path $SKILL_DIR 'scripts/plan-record.mjs') renew $plan $hash

    bash:

    SKILL_DIR='<SKILL_DIR>'; plan='<absolute plan path>'
    hash=$(shasum -a 256 "$plan" | cut -d' ' -f1)   # Step 2 only
    node "$SKILL_DIR/scripts/plan-record.mjs" renew "$plan" "$hash"

    Replace renew with reviewed or remove as the table says, always passing the target_sha256 from Step 2. It prints one word. no-record is normal: the mod did not start this run (or runs where it keeps no record), so there is nothing to update. locked (exit 2) means another approval held the record for about 10 seconds: retry once, and if it is still locked, mention it in the report — the lease expires on its own, and the next approval of the same plan then asks for a review again.

References

  • codec-loop.md — failure playbook, recovery, mechanics
  • review-output.schema.json — the contract
  • references/review-prompt.md — the adversarial charge sent to Codex
  • troubleshooting.md — Windows sandbox ACL fix, model-slug and old-CLI errors, stale broker

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.