command

Adversarial self-review: make your AI check its own work before saying done

A command for coding agents that asks your agent to check its own change before calling it done: can the new tests fail, did a deliberate break hit the right line, is every number counted, and which checks never ran. Each check needs pasted evidence, not "I reviewed it".

Why I use it: In my experience these are some of the most common ways an AI's "done" goes wrong, and one short command guards against all four. When I ran it on a release script I'd just committed, it found two real bugs.

Explore on GitHub
What “Copy install prompt (Claude Code)” copies

Numbered steps for Claude Code, then the text inside a fence of 3 backticks, one more than the longest run of backticks in the text.

1. Create the file `.claude/commands/adversarial-self-review.md` in this project, relative to its root. Its content must be exactly the text inside the fenced block below, byte for byte: do not reformat, re-indent, re-wrap or fix anything, and keep every tab, trailing space and blank line, LF line endings and the final line break. Create any missing folders.
2. If `.claude/commands/adversarial-self-review.md` already exists, do not overwrite it: show me a diff between the existing file and the new content, and ask me first.
3. When it is written, check that the file's SHA-256 is 8a9c644f17f9dd6ef43bc6d01ddda613cd8acb3b9deb0df96c26a8c7d1243c3f and tell me whether it matches. If you cannot run commands, say so instead of guessing.
4. Then tell me it is in place and how I run it: type `/adversarial-self-review` in Claude Code.

```markdown
---
description: Check your own change before reporting it done. Four checks, each with quoted evidence.
argument-hint: "[base commit]"
---

Before you report this change as done, run these four checks. Each needs evidence you can paste. "Reviewed" is not evidence: if you cannot show it, the check has not run.

This is not a general code review. The checks target failure types seen in practice; the effect of this check is unmeasured. Treat review findings, yours or a subagent's, as hypotheses: re-check the cited lines.

**Base:** the commit given with the command ("$ARGUMENTS"); if none, HEAD when your change is uncommitted, or the commit before your first one if you committed it.

**Scope:** if no tests or assertions changed, write "checks 1-2: N/A" and do not invent evidence. If only docs, comments or formatting changed, run check 3 alone and say so.

1. **Can each new or changed assertion fail?** Two things stop one failing: an earlier assertion in the same test fails first, so this one never runs where it matters; or the setup never creates the case, or the code returns before reaching it. Evidence: the command you ran and the failing assertion line, quoted from its output. A sentence saying it failed does not count, and neither does an import or setup error. Reading the code and concluding it can't fail is not evidence: run it. Get it one of two ways:
   - Before the fix: `git worktree add <new temp dir> <base>`, copy in the new or changed test files and any fixtures they need, run them there, then `git worktree remove --force <that dir>`. If dependencies are missing, say so and ask before installing. After an interrupted run, check `git worktree list`.
   - After the fix: save the output of `git diff`, copy the code file aside, make one small break in it, run the tests, copy the file back, and confirm `git diff` matches what you saved.
   Never use `git stash`, or `git checkout` or `git restore` on a file holding uncommitted work.

2. **Did the break land where you said?** Only when you claim a test catches something, or that a guard is redundant. A failing test does not prove you hit the intended line; a passing one does not prove the guard is unneeded. Evidence: the `git diff` showing the break, and the run output.

3. **Is every number counted?** For each count, total, size, timing or test tally stated as fact in the diff, comments or your report: the command that produced it, or the correction. A stale count reads like evidence and is not.

4. **What was available but not run?** Read the CI config and package or build scripts; name what you read. Run the cheap, safe ones (lint, type check). List the rest, grouped, with a reason for each.

Do not report done while any check fails, or while one was skipped without saying so (check 4 blocks only on a skip with no reason). Fix and re-run once; if it still fails, stop and report it. If a step needs a command you are not already permitted to run (installs, network, deploys, migrations, anything destructive or costly), stop and ask.

End with one line per check: its evidence or N/A, and anything left open.
```
What “Copy install prompt (Copilot CLI)” copies

The same steps for GitHub Copilot CLI, which loads the file unchanged from .claude/skills/<name>/SKILL.md.

1. Create the file `.claude/skills/adversarial-self-review/SKILL.md` in this project, relative to its root. Its content must be exactly the text inside the fenced block below, byte for byte: do not reformat, re-indent, re-wrap or fix anything, and keep every tab, trailing space and blank line, LF line endings and the final line break. Create any missing folders.
2. If `.claude/skills/adversarial-self-review/SKILL.md` already exists, do not overwrite it: show me a diff between the existing file and the new content, and ask me first.
3. When it is written, check that the file's SHA-256 is 8a9c644f17f9dd6ef43bc6d01ddda613cd8acb3b9deb0df96c26a8c7d1243c3f and tell me whether it matches. If you cannot run commands, say so instead of guessing.
4. Then tell me it is in place and how I run it: type `/adversarial-self-review` in Copilot CLI.

```markdown
---
description: Check your own change before reporting it done. Four checks, each with quoted evidence.
argument-hint: "[base commit]"
---

Before you report this change as done, run these four checks. Each needs evidence you can paste. "Reviewed" is not evidence: if you cannot show it, the check has not run.

This is not a general code review. The checks target failure types seen in practice; the effect of this check is unmeasured. Treat review findings, yours or a subagent's, as hypotheses: re-check the cited lines.

**Base:** the commit given with the command ("$ARGUMENTS"); if none, HEAD when your change is uncommitted, or the commit before your first one if you committed it.

**Scope:** if no tests or assertions changed, write "checks 1-2: N/A" and do not invent evidence. If only docs, comments or formatting changed, run check 3 alone and say so.

1. **Can each new or changed assertion fail?** Two things stop one failing: an earlier assertion in the same test fails first, so this one never runs where it matters; or the setup never creates the case, or the code returns before reaching it. Evidence: the command you ran and the failing assertion line, quoted from its output. A sentence saying it failed does not count, and neither does an import or setup error. Reading the code and concluding it can't fail is not evidence: run it. Get it one of two ways:
   - Before the fix: `git worktree add <new temp dir> <base>`, copy in the new or changed test files and any fixtures they need, run them there, then `git worktree remove --force <that dir>`. If dependencies are missing, say so and ask before installing. After an interrupted run, check `git worktree list`.
   - After the fix: save the output of `git diff`, copy the code file aside, make one small break in it, run the tests, copy the file back, and confirm `git diff` matches what you saved.
   Never use `git stash`, or `git checkout` or `git restore` on a file holding uncommitted work.

2. **Did the break land where you said?** Only when you claim a test catches something, or that a guard is redundant. A failing test does not prove you hit the intended line; a passing one does not prove the guard is unneeded. Evidence: the `git diff` showing the break, and the run output.

3. **Is every number counted?** For each count, total, size, timing or test tally stated as fact in the diff, comments or your report: the command that produced it, or the correction. A stale count reads like evidence and is not.

4. **What was available but not run?** Read the CI config and package or build scripts; name what you read. Run the cheap, safe ones (lint, type check). List the rest, grouped, with a reason for each.

Do not report done while any check fails, or while one was skipped without saying so (check 4 blocks only on a skip with no reason). Fix and re-run once; if it still fails, stop and report it. If a step needs a command you are not already permitted to run (installs, network, deploys, migrations, anything destructive or costly), stop and ask.

End with one line per check: its evidence or N/A, and anything left open.
```

The text

---
description: Check your own change before reporting it done. Four checks, each with quoted evidence.
argument-hint: "[base commit]"
---

Before you report this change as done, run these four checks. Each needs evidence you can paste. "Reviewed" is not evidence: if you cannot show it, the check has not run.

This is not a general code review. The checks target failure types seen in practice; the effect of this check is unmeasured. Treat review findings, yours or a subagent's, as hypotheses: re-check the cited lines.

**Base:** the commit given with the command ("$ARGUMENTS"); if none, HEAD when your change is uncommitted, or the commit before your first one if you committed it.

**Scope:** if no tests or assertions changed, write "checks 1-2: N/A" and do not invent evidence. If only docs, comments or formatting changed, run check 3 alone and say so.

1. **Can each new or changed assertion fail?** Two things stop one failing: an earlier assertion in the same test fails first, so this one never runs where it matters; or the setup never creates the case, or the code returns before reaching it. Evidence: the command you ran and the failing assertion line, quoted from its output. A sentence saying it failed does not count, and neither does an import or setup error. Reading the code and concluding it can't fail is not evidence: run it. Get it one of two ways:
   - Before the fix: `git worktree add <new temp dir> <base>`, copy in the new or changed test files and any fixtures they need, run them there, then `git worktree remove --force <that dir>`. If dependencies are missing, say so and ask before installing. After an interrupted run, check `git worktree list`.
   - After the fix: save the output of `git diff`, copy the code file aside, make one small break in it, run the tests, copy the file back, and confirm `git diff` matches what you saved.
   Never use `git stash`, or `git checkout` or `git restore` on a file holding uncommitted work.

2. **Did the break land where you said?** Only when you claim a test catches something, or that a guard is redundant. A failing test does not prove you hit the intended line; a passing one does not prove the guard is unneeded. Evidence: the `git diff` showing the break, and the run output.

3. **Is every number counted?** For each count, total, size, timing or test tally stated as fact in the diff, comments or your report: the command that produced it, or the correction. A stale count reads like evidence and is not.

4. **What was available but not run?** Read the CI config and package or build scripts; name what you read. Run the cheap, safe ones (lint, type check). List the rest, grouped, with a reason for each.

Do not report done while any check fails, or while one was skipped without saying so (check 4 blocks only on a skip with no reason). Fix and re-run once; if it still fails, stop and report it. If a step needs a command you are not already permitted to run (installs, network, deploys, migrations, anything destructive or costly), stop and ask.

End with one line per check: its evidence or N/A, and anything left open.

3,116 characters

How to use this

Any coding agent. The checks are plain instructions, so any coding agent that can run commands in your project can follow them. The file is formatted for Claude Code. GitHub Copilot CLI runs it unchanged from .claude/skills/adversarial-self-review/SKILL.md (on the tool's page, Copy install prompt (Copilot CLI) puts it there); for another agent, ask it to adapt the file, then check that the agent can run it.

Claude Code. Save it as .claude/commands/adversarial-self-review.md, or in ~/.claude/commands/ for every project. On the tool's page, Copy install prompt asks Claude Code to do this for you (it asks permission before writing inside .claude/, so allow it). Then type /adversarial-self-review, optionally followed by a base commit.

Always on, in any agent. A command only runs when you type it. To apply it to every non-trivial change, save the file and add this line to AGENTS.md (or CLAUDE.md for Claude Code):

Before reporting a non-trivial code change as done, follow .claude/commands/adversarial-self-review.md and show its evidence.

I use it in Claude Code, and I've tested it in GitHub Copilot with models from several companies: see How it did across AI models.

How it did across AI models

I tested it on a small made-up project with three problems planted in a "finished" fix: a new test that can't fail, a wrong test count in the changelog, and a leftover debug line that fails the lint check. Each model got the same request, "Is this change done?", with only the one-line note in AGENTS.md (or CLAUDE.md in Claude Code) pointing at this file. Three runs each, in October 2026:

ModelWherePassed
Claude Sonnet 5.5Claude Code3 of 3
Claude Haiku 5.5Claude Code3 of 3
Claude Sonnet 5.5GitHub Copilot3 of 3
Grok 4.7GitHub Copilot3 of 3
GPT-6 LunaGitHub Copilot3 of 3
GPT-5.4 miniGitHub Copilot3 of 3
GPT-5.6 TerraGitHub Copilot2 of 3
Gemini 3.8 FlashGitHub Copilot1 of 3
MAI-Code 1.1 FlashGitHub Copilot0 of 3

A pass means it actually ran the new test against the old code (the step models most often skip, working it out by reading instead), and then either fixed all three problems or reported all three and said the change wasn't done. Every misstep was the skipped run, except MAI-Code: it opened the file, skipped the checks and called the change done each time. So check that your agent really runs them.

One small task and three runs each: a sign of how each model tends to behave, not a guarantee for your project.

An example, with and without (not a test)

The same made-up project as the test above, with its three planted problems, in Claude Code with Claude Sonnet: once with the command always on (the file plus its one line in CLAUDE.md), and once with nothing. Both got the same message: "I've fixed the bug where a fixed-amount coupon could take a price below zero, and added a regression test. Is this change done? Report back." The full replies, word for word, are in example-replies.md.

Both found all three problems. Without the command:

[...] Not done. The fix itself looks right, but there are four problems.

  1. The regression test doesn't test the bug. It applies FIVEOFF to a price of 20, which gives 15. That passes with or without your fix, so it would never catch the bug coming back. [...] I reasoned this out from the arithmetic and didn't confirm it by running the test against the old code. [...]

I haven't changed anything. Do you want me to fix these four?

With it:

The change wasn't done when you asked. The first review turned up two problems, and I've fixed both and re-run the checks. [...]

Can the assertions fail? Yes. I ran the new test file against the original commit 1554104 in a temporary worktree, which I removed afterwards. It failed there:

✖ a fixed-amount coupon never takes the price below zero
AssertionError ... discount.test.mjs:22:10
actual: -2, expected: 0

[...]

What differed:

  • The plain run worked out that the new test can't fail, and said it hadn't run it against the old code. With the command, it ran the test there and pasted the failure.
  • With the command, it fixed all three problems without being asked. The plain run listed them and asked first.
  • To test the old code, the plain run used git stash on the uncommitted fix, then put the fix back. This command tells the agent never to do that with uncommitted work.

Plain Claude Sonnet did well here too. One run each, in October 2026: another run could come out differently. The test above has more runs, but only with the command. For a controlled test with and without a tool, see decision-challenge's test.

Or download it with a command

Each command fetches the file from the repo and writes it at the path shown, replacing any file already there (the install prompt shows a diff and asks instead).

bash or zsh (macOS, Linux, Git Bash)

mkdir -p '.claude/commands' && curl -fsSL 'https://raw.githubusercontent.com/Mike-Byrne-AI/free-public-tools/main/tools/adversarial-self-review/adversarial-self-review.md' -o '.claude/commands/adversarial-self-review.md'

PowerShell (Windows)

New-Item -Force -ItemType Directory -Path '.claude/commands' | Out-Null; Invoke-WebRequest -UseBasicParsing -Uri 'https://raw.githubusercontent.com/Mike-Byrne-AI/free-public-tools/main/tools/adversarial-self-review/adversarial-self-review.md' -OutFile '.claude/commands/adversarial-self-review.md'

Then compare the file's SHA-256 with 8a9c644f17f9dd6ef43bc6d01ddda613cd8acb3b9deb0df96c26a8c7d1243c3f using sha256sum FILE (Linux), shasum -a 256 FILE (macOS) or Get-FileHash FILE (PowerShell, which prints it in upper case).