Files
adversarial-review/README.md
T
ddadmin 442f2e78c7 fix(skill): positive content-bind для secondary session-id (round-6 finding)
- Зачем:
  - live e2e dogfood через codex нашёл HIGH: timestamp-only secondary (newest rollout с mtime > CODEX_SESSIONS_BEFORE) силой позволяет параллельному codex-инвокейшну подменить нашу session — resume на чужой thread проходит все sanity-проверки, skill применяет "fixes" по ревью чужого артефакта. Самые узкие временные окна эту проблему не закрывают.
- Что:
  - SKILL.md: каждый prompt (initial/resume/fresh-exec) первой строкой содержит `<!-- ADVERSARIAL-REVIEW-SESSION: ${REVIEW_ID} -->`. Secondary path переключён на `find -newer <prompt-file> -exec grep -l "${REVIEW_ID}" {} +` — positive content-match. Zero match → fail closed.
  - SKILL.md: placeholder `${CODEX_SESSIONS_BEFORE}` удалён (больше не нужен — timestamp anchor заменён на prompt-file anchor).
  - SKILL.md Rules: обновлены session-id и marker правила.
  - README.md: упрощён macOS-note (всё теперь POSIX: `-newer FILE`, `-exec CMD {} +`, `grep -l`), troubleshooting обновлён под positive-binding.
  - DESIGN.md §4.1: переписан decision — positive content-bind как chosen approach, rejected alternatives расширены (marker-file, XML-marker-vs-comment, newest-by-mtime explicitly rejected in round 6).
  - DESIGN.md §2.3: verify-snippet переписан на новую форму.
  - DESIGN.md §6.7: новая подсекция — round-6 lesson про silent wrong-session corruption.
  - DESIGN.md §7.1/§7.2 smoke tests переведены на positive-bind (заодно ушёл `-1` timestamp race).
  - DESIGN.md §8: новая строка в version log про round-6 переход.
  - DESIGN.md §9.5: GNU find limitation снята — всё POSIX.
- Проверка:
  - Empirically validated: rollout JSONL содержит prompt text (3 matches для unique phrase в тесте 2026-04-17).
  - Smoke tests §7.1/§7.2 проходят на POSIX командах.
  - Parallel-codex hazard структурно закрыт: чужой rollout не содержит нашего ${REVIEW_ID}, grep его отфильтрует.
2026-04-17 18:26:48 +03:00

305 lines
12 KiB
Markdown

# Adversarial Review
Claude Code skill for adversarial AI code and plan review.
One AI writes the code. Another tears it apart. Iterate until approved.
## What is this
Most AI code review tools validate your changes — "looks good, maybe add tests."
Adversarial review does the opposite: the reviewer **defaults to skepticism**
and tries to break confidence in the change. It looks for what will fail
in production, not what might be nice to improve.
This is a [Claude Code skill](https://docs.anthropic.com/en/docs/claude-code)
— a single `SKILL.md` file that teaches Claude how to run adversarial reviews
through an external AI model (currently OpenAI Codex).
## Key features
- **Plan review** — review the plan BEFORE writing code. Catch architecture
mistakes, missing steps, and risks early
- **Code review** — review the implementation. Bugs, security, data loss
- **Code-vs-plan** — verify the implementation matches the plan
- **Iterative** — Claude fixes issues based on reviewer feedback and resubmits
for re-review. Up to 5 rounds until approved
- **Lightweight** — one `SKILL.md` file, no server, no broker. Compare with
[codex-plugin-cc](https://github.com/openai/codex-plugin-cc):
~15 JS modules, App Server, JSON-RPC broker, lifecycle hooks
## How it works
```
┌─────────┐ ┌──────────┐ ┌─────────┐
│ Claude │────>│ Reviewer │────>│ Claude │
│ (code) │ │ (Codex) │ │ (fix) │
└─────────┘ └──────────┘ └─────────┘
^ │
│ ┌──────────┐ │
└─────────│ Reviewer │<───────────┘
│(re-review)│
└──────────┘
VERDICT: APPROVED
```
### Three modes
| Mode | What it reviews | When to use |
|------|----------------|-------------|
| `plan` | Implementation plan | Before writing code |
| `code` | Git diff (unstaged, staged, or branch) | After writing code |
| `code-vs-plan` | Code changes against the plan | Verify implementation matches plan |
Mode is auto-detected from context, or you can force it with an argument.
## Quick start
### 1. Prerequisites
[Claude Code](https://docs.anthropic.com/en/docs/claude-code) and
[OpenAI Codex CLI](https://github.com/openai/codex) must be installed.
Verify both are available:
```bash
claude --version # Claude Code CLI
codex --version # OpenAI Codex CLI (>= 0.115.0)
```
If Codex is missing: `npm install -g @openai/codex`
**Authentication.** Codex needs an OpenAI account. Either:
- Sign in interactively: `codex` (opens browser)
- Or set `CODEX_API_KEY` env var for non-interactive use
### 2. Install the skill
```bash
git clone https://github.com/dementev-dev/adversarial-review.git
ln -s "$(pwd)/adversarial-review" ~/.agents/skills/adversarial-review
```
Verify the skill is visible to Claude Code:
```bash
ls -la ~/.agents/skills/adversarial-review/SKILL.md
```
### 3. Add permissions
The skill runs `git`, `codex exec`, and writes temp files to `/tmp`.
Without pre-approved permissions, Claude Code will prompt for each action.
**Where to add.** Since the skill is installed globally
(`~/.agents/skills/`), permissions should go into the global config
so they work in any project:
| Install scope | Config file |
|---------------|-------------|
| Global (recommended) | `~/.claude/settings.json` |
| Single project | `<project>/.claude/settings.local.json` |
Merge the following rules into the `permissions.allow` array of the
chosen config file:
```jsonc
// --- adversarial-review permissions ---
// Git: diff, status, branch detection, repo root, submodule check
"Bash(git diff*)",
"Bash(git status*)",
"Bash(git symbolic-ref*)",
"Bash(git rev-parse*)",
// Codex: initial launch (uses -C; prompt fed via cat | pipe for env portability)
"Bash(cat /tmp/codex-prompt-* | timeout 600 codex exec *)",
// Codex: resume (cd prefix because resume has no -C flag; prompt via cat | pipe)
"Bash(cd * && cat /tmp/codex-resume-prompt-* | timeout 600 codex exec resume *)",
// Session-id filesystem fallback (POSIX: find -newer + grep -l for content-match)
"Bash(find ~/.codex/sessions*)",
// Diagnostic aid when filesystem fallback finds nothing
"Bash(ls -t ~/.codex/sessions*)",
// Temp files: prompts (initial + resume), plans, review output, JSONL stdout, stderr
"Write(/tmp/codex-plan-*)",
"Write(/tmp/codex-prompt-*)",
"Write(/tmp/codex-resume-prompt-*)",
"Read(/tmp/codex-review-*)",
"Read(/tmp/codex-stdout-*)",
"Read(/tmp/codex-stderr-*)",
// Archive failed-resume diagnostics before fresh exec overwrites them
"Bash(mv /tmp/codex-stdout-* /tmp/codex-stdout-*-failed-resume.jsonl)",
"Bash(mv /tmp/codex-stderr-* /tmp/codex-stderr-*-failed-resume.txt)",
// Cleanup
"Bash(rm -f /tmp/codex-*)"
```
<details>
<summary>Full example (if the config file is empty or does not exist)</summary>
```jsonc
{
"permissions": {
"allow": [
// adversarial-review
"Bash(git diff*)",
"Bash(git status*)",
"Bash(git symbolic-ref*)",
"Bash(git rev-parse*)",
"Bash(cat /tmp/codex-prompt-* | timeout 600 codex exec *)",
"Bash(cd * && cat /tmp/codex-resume-prompt-* | timeout 600 codex exec resume *)",
"Bash(find ~/.codex/sessions*)",
"Bash(ls -t ~/.codex/sessions*)",
"Write(/tmp/codex-plan-*)",
"Write(/tmp/codex-prompt-*)",
"Write(/tmp/codex-resume-prompt-*)",
"Read(/tmp/codex-review-*)",
"Read(/tmp/codex-stdout-*)",
"Read(/tmp/codex-stderr-*)",
"Bash(mv /tmp/codex-stdout-* /tmp/codex-stdout-*-failed-resume.jsonl)",
"Bash(mv /tmp/codex-stderr-* /tmp/codex-stderr-*-failed-resume.txt)",
"Bash(rm -f /tmp/codex-*)"
]
}
}
```
</details>
**Security note:** The `codex exec` rule allows any `codex exec` invocation
wrapped in `timeout 600`. The skill only uses read-only mode (`-s read-only`),
but Claude Code's permission patterns are prefix-based and cannot enforce flag
constraints. If you prefer tighter control, omit the `codex exec` rule and
approve each invocation manually.
### 4. Use
```bash
/adversarial-review # auto-detect mode
/adversarial-review plan # force plan review
/adversarial-review code # force code review
/adversarial-review path/to/f # review a specific file
/adversarial-review xhigh # higher reasoning effort
/adversarial-review model:gpt-5.3-codex # use a different model
```
## Prompt architecture
The skill uses XML-structured prompts with adversarial stance:
- **`<role>`** — adversarial reviewer, defaults to skepticism
- **`<operating_stance>`** — break confidence, not validate
- **`<attack_surface>`** — concrete checklist: auth, data integrity,
race conditions, rollback safety, schema drift, error handling, observability
- **`<finding_bar>`** — every finding must answer 4 questions:
what can go wrong, why vulnerable, impact, recommendation
- **`<scope_exclusions>`** — no style, naming, or speculative comments
- **`<calibration>`** — one strong finding > five weak ones
## Example output
See [examples/review-output.md](examples/review-output.md) for a sample review.
## Troubleshooting
**`codex exec` exits with model error.**
Some models are unavailable with ChatGPT accounts (e.g. `o3-mini`).
The default `gpt-5.4` works with both ChatGPT and API key auth.
Override with `/adversarial-review model:<name>`.
**Permission prompts on every action.**
Add the permissions from the [setup section](#3-add-permissions). Check that
the file is valid JSON and in the right location (project `.claude/settings.local.json`
or global `~/.claude/settings.json`).
**Codex hangs / timeout (exit code 124).**
All `codex exec` calls are wrapped in `timeout 600` (10 minutes). If you see
exit code 124, the reviewer did not respond in time. Retry — this is usually
transient.
**Resume fails with session error.**
The skill uses `codex exec resume <session-id>` for rounds 2+. On failure
(non-zero exit, `thread/resume failed` in stderr, or a malformed review),
the skill does NOT silently fall back. In an interactive session it asks
whether to run a fresh `codex exec` (higher token cost) or conclude the
review as NOT VERIFIED. In headless runs it decides based on the maximum
severity of the last successful round's findings: critical/high → fresh
exec; medium-only → conclude as NOT VERIFIED.
**`--json` stdout is empty in my Claude Code session (session ID capture noise).**
In some Claude Code sandbox configurations codex's `--json` event stream is
suppressed when stdout is redirected to a file — the `/tmp/codex-stdout-*.jsonl`
ends up 0 bytes even though the review itself (`-o /tmp/codex-review-*.md`)
completes correctly. The skill handles this automatically via a filesystem
fallback: every prompt includes a unique session marker
(`<!-- ADVERSARIAL-REVIEW-SESSION: <REVIEW_ID> -->`) that gets written to
the rollout JSONL on disk. When the JSONL stream is empty, the skill runs
`find ~/.codex/sessions -name 'rollout-*.jsonl' -newer <prompt-file> -exec
grep -l <REVIEW_ID> {} +` to positively identify this session's rollout by
content match (not by newest-mtime, which would be unsafe against parallel
codex invocations) and extracts the UUID from the filename. Resume continues
to work normally. The commands used are POSIX (`find -newer`, `-exec grep -l`)
and work identically on Linux and macOS.
**"NOT VERIFIED" result.**
The skill applied fixes but the reviewer did not re-verify them (resume
failed or the operator chose to conclude). This is not an approval —
manually review the applied fixes before merging.
**Running inside a git submodule.**
`git rev-parse --show-toplevel` returns the submodule path, not the parent
repo. The skill warns you and scopes the review to the submodule. If you
meant to review the parent, invoke the skill from the parent working tree.
**Bare repository or not inside a work tree.**
The skill aborts at Step 2 with a clear message. Run it from inside a
git working tree.
**Plan Mode exits when writing temp files.**
In Claude Code Plan Mode, writing to `/tmp` may trigger a permission prompt
or exit Plan Mode. This is a known Claude Code limitation. It does not affect
review correctness.
## Known limitations
- **Plan Mode and `/tmp` writes.** Writing review prompts to `/tmp` may trigger
a permission prompt or cause Plan Mode to exit. Does not affect review correctness.
- **`resume` inherits sandbox.** `codex exec resume` does not accept `-s`
sandbox is inherited from the original session (always `read-only`).
- **`resume` has no `-C` flag.** The skill captures `REPO_ROOT` via
`git rev-parse --show-toplevel` at Step 2 and prefixes every resume with
`cd '<REPO_ROOT>' && ...`. This requires paths without single quotes;
pathological paths (containing `'`, `"`, `$`, backtick, newline) cause
the skill to abort at Step 2.
- **Submodule scoping.** When invoked inside a submodule, the review is
scoped to the submodule — `git rev-parse --show-toplevel` does not walk
up to the parent. A warning is printed; invoke from the parent repo if
you want parent scope.
- **macOS end-to-end not tested.** The secondary session-id capture
uses only POSIX flags (`find -newer FILE`, `-exec CMD {} +`, `grep -l`),
so it should work identically on macOS as on Linux, but the skill has
not been end-to-end tested on macOS.
## Roadmap
- [ ] Gemini as alternative reviewer backend
- [ ] Local model support (Ollama, llama.cpp)
- [ ] CI integration (GitHub Actions)
- [ ] Multi-reviewer mode (parallel review by multiple models)
## Inspiration
Adversarial prompt structure developed after studying
[openai/codex-plugin-cc](https://github.com/openai/codex-plugin-cc) (Apache-2.0).
Borrowed ideas: XML-structured prompts, adversarial stance, attack surface
checklist, finding bar, calibration rules.
What we did differently:
- **Iterative loop** — Claude fixes issues and resubmits (not "stop and ask user")
- **Plan review** — reviews plans before code, not just code
- **Single file** — one SKILL.md vs 15+ JS modules
- **Verbatim output** — reviewer findings shown as-is, not rephrased
## License
Apache-2.0 — see [LICENSE](LICENSE).