opencode-adversarial-review
Skill3 starsChanged 7 days agoMITView on GitHub
Run an adversarial (red-team) review through the OpenCode CLI's free model pool (muse-spark, Nemotron, stealth models) under a machine-enforced read-only permission config. Use when a HIGH-risk change needs a second cross-family reviewer without spending paid quota, or as the cheap extra pair of eyes beside a primary review.
In this repository15 other files it gives its agents.
- templates/AGENTS.mdAGENTS.md
- agy-adversarial-reviewSkill
- agy-implementSkill
- claude-adversarial-reviewSkill
- claude-implementSkill
- codex-adversarial-reviewSkill
- codex-implementSkill
- configSkill
- cursor-adversarial-reviewSkill
- cursor-implementSkill
- dev-leadSkill
- grok-adversarial-reviewSkill
- grok-implementSkill
- opencode-implementSkill
- quota-setupSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 2 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 93
When any of this trips, the fourth leg runs on opencode-go/glm-5.3 and the report says why.
- Very specific: line 166
Call this what it is: a machine-enforced guardrail against a fallible delegate, not a security boundary against an adversarial one.
- Very specific: line 169
NOT on the list — git branch -d writes refs, and rev-parse --abbrev-ref HEAD answers the read-only question) — stronger than a behavioral …
- Very specific: line 179
Verify what actually governs before spending a run: opencode debug agent prints the merged rule array in evaluation order (runtime file).
- Very specific: line 239
A declaration only works when the config is untracked: If the target's own repo tracks opencode.json, the mv-and-write above produces M …
- Distinctive: line 272
NOT REACHED, one plain verdict line -- and your brief says what this round is about.
- Very specific: line 278
Before this recipe, complete the frozen-target triage step in dev-lead Phase 2.
- Very specific: line 374
Check before editing anything, and in this order — the error greps first, because every one of them names a cause that a model switch would …
- Distinctive: line 381
Only once all four come back clean — a run that errored nowhere and simply stopped — is the fifth shape in play.
≈ “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.
Files
Adversarial review via opencode (free pool)
The value here is an additional model family at zero quota cost: muse-spark (Meta) and Nemotron are neither GPT, Gemini, nor Claude, so this is the cheapest way to satisfy the cross-family rule's second reviewer on HIGH-risk work. The trade-off is best-effort capacity — congestion is normal, so this leg is the additional opinion or the unhurried gate, never the time-critical one.
Before the first run of a session
Read references/opencode-runtime.md
(same directory). It holds the
family mechanics — the free-model catalogue and the stealth-model
family-unknown caveat, the two silent permission traps (zero-commit project
binding, last-match-wins ordering), congestion behavior, and the audit log
lines. This file assumes them and covers only the review role.
Choosing the model for this leg — by LENS, not a fixed favourite
The owner ruled this on 2026-09-16 and it now lives in their roster
(roster.json, located per /dev-lead:config; it was
in their CLAUDE.md before the roster existed); what follows is the same
rule with the numbers behind it,
and this file is where those numbers belong. The division is now explicit in
both directions — the roster names which leg, dev-lead holds prices, buckets
and calibration rows. (The earlier attempt to write a single model in here as
"the default" was mine, on a peer's recommendation, and was retracted in
0.6.8; the rule below is the owner's, not a plugin preference.)
| lens | model | why |
| mechanical — consistency, is-it-still-true, convention, falsifiability | opencode-go/muse-spark-1.3-contributor; opencode/muse-spark-1.3-contributor-free only when the go plan is not available | owner, 2026-10-04: on the go plan this model has no rate limit and draws no quota, while the free twin is a rate-limited pool. Measured strong on fact-checking and line accuracy. (Before that statement this file priced the Go twin at ~$0.014 a leg; take the owner's word for your own plan, and measure if yours differs.) |
| judgement — challenge, design rulings, sequences | opencode-go/glm-5.3 | an independent family, ~$0.20 a leg against the large ($12 / 5 h) bucket. No calibration row yet — treat as unproven until one exists |
| top-tier judgement only | opencode-go/kimi-k3, opencode-go/qwen3.8-max | 1–2 legs per 5-hour window at most, and the buckets are shared across sessions, so probe immediately before dispatch |
Effort follows the lens, never the model. There is no per-variant price;
reasoning tokens bill as output. So xhigh on a cheap model is nearly free
and xhigh on a dear one can double the leg.
The terms question, and how it was settled. Both
muse-spark-*-contributor variants, free and paid, put the prompt into a
training set, and a review leg is handed the frozen tree: source, design
documents, sometimes a spec. That is a disclosure decision, not a cost one,
so it went to the owner rather than being settled here. Ruled 2026-09-16:
accepted for review legs — with the exception carried per change. If a
change carries material that must not reach a training set, say so and run
the fourth leg on opencode-go/glm-5.3 instead. The judgement stays with
whoever knows what is in the diff; the default no longer has to be guessed.
And the owner delegated the judgement itself to this layer (2026-09-16): dev-lead decides what may go to a contributor tier. So it needs criteria that can be CHECKED, not a feeling. Three rules:
1. Decide per REPOSITORY, once, and write it down — not per change. A repo's disclosure class is stable; a change's is not knowable at a glance, and a judgement re-made every round is one that decays exactly like a STOP rule that keeps firing on benign causes. Record the class beside the repo's other dispatch facts. Re-open it only when the repo gains a new kind of content, not when a change looks unusual.
2. The stop classes. A repo is NOT eligible for a contributor tier if its tracked tree carries any of: credential material of any kind; a customer's or third party's confidential material; personal data beyond work identifiers; or specifications for an unannounced product. Everything else — this account's own infrastructure code, its ADRs, its tooling — is eligible.
3. Screen the frozen target before the first dispatch, not from memory. The tree is what the leg receives, so look at it:
git ls-files | wc -l; git ls-files -z | xargs -0 cat | wc -c # what is actually handed over
grep -rlEi 'BEGIN (RSA|OPENSSH|EC) PRIVATE KEY|api[_-]?key *= *["'"'"'][A-Za-z0-9]{16}' .
grep -rhoE '[A-Za-z][A-Za-z.]+@[a-z]+\.com' . | sort -u # people, not rolesMeasured on one of the owner's infrastructure repositories 2026-09-16: no credential material; 153 files and 6.7 MB handed to each leg; colleague work addresses and internal project codenames present. That is the residual the owner accepted — named, not assumed absent. Eligible.
The brief is a prompt too. The claims file and lens text are authored by the lead and go to the model with the tree, so they can disclose material the repo does not contain — an audit excerpt with real actor ids, a quoted mail, a customer's words from a ticket. Screen what you WRITE by the same rules; this is the half no check on the repository can see.
When any of this trips, the fourth leg runs on opencode-go/glm-5.3 and the
report says why. A leg not run is always available too: three legs and a
stated reason beat four legs and a disclosure nobody decided.
Establish an immutable review target
One frozen directory per reviewer, at the exact commit, that nothing else touches — no lead activity inside it. Not "whenever possible": a reviewer reads the WORKING TREE, not your commit. Measured — a round ran mutation testing in the same worktree mid-review and the reviewer opened a CRITICAL on a mutated, non-compiling file it was never meant to see.
Freeze it with the suite's tested helpers instead of hand-rolling the shell;
every bug ever found in this step was in a hand-rolled copy. The suite-root
resolver and both calls — freeze-target.sh to create it, verify-target.sh
before AND after the run — are in
dev-lead Phase 2, and the reasoning is in
methodology.md §7. Everything below assumes
$REVIEW_TARGET_DIR is that frozen directory and $REVIEW_HEAD is the SHA it
was frozen at.
The read-only boundary is a config file, not a mode
If the target already has an opencode.json, save it first — writing
ours over a project's real config and then deleting "ours" at teardown
destroys the project's file:
RUN_DIR=$(mktemp -d "${TMPDIR:-/tmp}/opencode-review.XXXXXX") # created here; every later step reuses it
[ -f "$REVIEW_TARGET_DIR/opencode.json" ] && \
mv "$REVIEW_TARGET_DIR/opencode.json" "$RUN_DIR/opencode.json.orig"
# ... run ...; at teardown, restore:
# mv "$RUN_DIR/opencode.json.orig" "$REVIEW_TARGET_DIR/opencode.json"(The frozen worktree above does NOT make this step optional: it is a checkout
of the same repo, so a project that tracks its own opencode.json has one
there too. The backup is what stops teardown deleting it.)
Then write this as opencode.json in the target (wildcard FIRST — last
match wins; see runtime). freeze-target.sh … --opencode-lockdown writes exactly
this file, read-only, at freeze time (and refuses a commit that tracks its own
opencode.json); declare it to verify-target.sh as below:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"bash": {
"*": "deny",
"git status*": "allow",
"git log*": "allow",
"git diff*": "allow",
"git show*": "allow",
"git rev-parse*": "allow"
},
"edit": "deny",
"external_directory": "deny"
}
}external_directory: deny is what keeps a wrong path from ending the leg. Left
at its default (ask), a read outside the target is auto-rejected headless
and the leg stops with no verdict, however good the review was; with an
explicit deny the model gets an error message back and keeps reviewing. A peer
session measured it on 4 changes x 5 Google models: 3 of 20 legs died after
~20 s on a mis-typed path (the frozen path with its last components dropped),
and the failing case ran 206 s to a verdict once the line was added. opencode
debug agent build in the target lists the merged rule last, and opencode
re-appends its own tool-output allow after it, so truncated tool output stays
readable.
Call this what it is: a machine-enforced guardrail against a fallible
delegate, not a security boundary against an adversarial one. File edits
denied, shell denied except five git reads (git branch* is deliberately
NOT on the list — git branch -d writes refs, and
rev-parse --abbrev-ref HEAD answers the read-only question) — stronger
than a behavioral plan mode, and stronger than opencode's own plan agent
(whose edit-deny a shared config can override and whose bash is
unrestricted — measured; see runtime). But command wildcards match command
prefixes, not effects: git diff --output=<path> and git log
--output=<path> write files while matching the allowed patterns. The
threat model here is an honest model taking a lazy shortcut — which the
denials catch — and the post-run bracket below is what catches everything
else; a hostile-input review needs an OS-level read-only sandbox instead.
Verify what actually governs before spending a run: opencode debug agent
prints the merged rule array in evaluation order (runtime file). Use the
default agent with this config. A denied call comes back to the model as a
message, so an over-curious reviewer reports MACHINE-DENIED and keeps
reviewing instead of dying.
The emitted leg-cmd.sh command begins with
check-opencode-lockdown.py on the target and stops the run when
opencode.json is not exactly the shape below: only $schema and permission, edit denied, bash and
external_directory deny-by-default with at most the five read-only git rules (bash) or literal absolute paths
(external_directory), no opencode.jsonc or .opencode in the target, no OPENCODE_CONFIG* / OPENCODE_DISABLE*
in the environment, and the target is the root of a git checkout with a commit (otherwise opencode ignores the file).
A managed config under /etc/opencode is not read (known limit). Without the file opencode runs as
the build agent with edit and bash open (a peer session ran three legs that way before this check existed).
leg-cmd.sh --help prints which of --target, --base, --prompt-file and --add-dir each adapter takes.
The config file itself appears as untracked in the target, and this leg cannot move it out — it must sit at the project root to bind. Declare it to the certification instead of excusing it:
set -o pipefail # without it the digest below survives a MISSING file:
# sha256sum reports the bad operand on stderr, the
# second sha256sum still exits 0 and hashes whatever
# arrived, and the same omission after the run
# reproduces the same digest — certifying a run that
# never had the claims file. Measured.
for f in opencode.json REVIEW-CLAIMS.md; do
[ -f "$REVIEW_TARGET_DIR/$f" ] || { echo "scaffold missing: $f"; exit 1; }
done
# Immutable FOR THE DURATION, not merely equal at the endpoints — the third of
# the four properties in the materializing-evidence reference, which this flow
# violated by relying on the digest alone. A before/after digest cannot see the
# edit → read → restore sequence, and this
# leg's own allow-list documents the write: `git diff --output=<path>`. Measured
# that a-w blocks it at the source: "fatal: could not open 'REVIEW-CLAIMS.md'
# for writing: Permission denied".
chmod a-w "$REVIEW_TARGET_DIR"/opencode.json "$REVIEW_TARGET_DIR"/REVIEW-CLAIMS.md
SCAFFOLD_SHA=$(sha256sum "$REVIEW_TARGET_DIR"/{opencode.json,REVIEW-CLAIMS.md} | sha256sum)a-w on the files stops a rewrite through the allow-listed git … --output=.
Unlinking and recreating them would need write on the directory rather than
on the files, and no allow-listed command can do it: bash is deny except
five git reads, and none of those five unlinks a file. The two layers are what
make the digest meaningful — keep both, and chmod u+w before restoring.
Then pass those two paths as trailing arguments to the verify-target.sh
bracket the section above already requires — … "$REVIEW_HEAD" opencode.json
REVIEW-CLAIMS.md — and compare SCAFFOLD_SHA after the run.
Declaring a path permits that entry and no other, and fails if it has
vanished — but it permits an entry, not its content: ?? opencode.json
reads the same after a rewrite, and this leg's allow-list reaches
git diff --output=<path> (see above). So keep the SCAFFOLD_SHA comparison
in the lead as well; the two answer different questions. Remove the claims file
and restore any original config after the run.
A declaration only works when the config is untracked. If the target's own
repo tracks opencode.json, the mv-and-write above produces M
opencode.json, and verify-target.sh rejects a declared tracked path by
design — a modified tracked file is a mutation of the reviewed tree, never
scaffolding, and loosening that would reopen exactly the hole the rule closes.
Bracket outside the scaffold window instead, which is strictly stronger
than a declaration would have been:
| step | tracked opencode.json | untracked |
| after freeze, before writing the config | verify-target.sh "$REVIEW_TARGET_DIR" "$REVIEW_HEAD" — no declarations, must be fully clean | same |
write scaffold, record SCAFFOLD_SHA, run the leg | — | — |
| after the run | compare SCAFFOLD_SHA — meaningful only because the files were a-w for the whole window; a digest alone samples two endpoints and cannot see edit → read → restore | same |
| then restore | chmod u+w both, then mv "$RUN_DIR/opencode.json.orig" back and rm REVIEW-CLAIMS.md | chmod u+w both, then rm opencode.json REVIEW-CLAIMS.md |
| after restoring | verify-target.sh "$REVIEW_TARGET_DIR" "$REVIEW_HEAD" — no declarations, must be fully clean | same |
The closing bracket proving clean is what proves the restore was byte-exact:
a mv-back that produced anything other than the committed content would
still show M opencode.json. Order matters — compare SCAFFOLD_SHA before
restoring, or the restore erases the evidence of a rewrite and cleanup's
success gets reported as the run's.
Run it
Capture REVIEW_HEAD when you freeze the target, not immediately before
the launch assertion — captured at launch time the check compares HEAD
against itself and can only pass (see the same note in
claude-adversarial-review).
Since 0.6.30 the brief is a LENS inside this leg's framing
(references/adversarial-framing.md):
the frame carries the general discipline -- read the base revision, label
inherited defects instead of dropping them, a per-claim HOLDS / BROKEN /
NOT REACHED, one plain verdict line -- and your brief says what this round is
about. Adopted on measurement: on three frozen targets with known answers,
three runs per arm, the plain brief found 4 of the known defects and this
frame 7; the plain brief answered broken claims "HOLDS" (runtime
file, calibration table).
Before this recipe, complete the frozen-target triage step in dev-lead Phase 2; the review launcher requires its record for this exact commit.
REVIEW_HEAD=$(git -C "$REVIEW_TARGET_DIR" rev-parse HEAD) # at freeze time
# ... later, at launch (RUN_DIR was created in the boundary step above):
# Write the lens to "$RUN_DIR/prompt.md" in its own FOREGROUND step.
DEV_LEAD=${DEV_LEAD_ROOT:-$(ls -d "$HOME"/.claude/plugins/cache/dev-lead/dev-lead/* 2>/dev/null | sort -V | tail -1)}
[ "$(git -C "$REVIEW_TARGET_DIR" rev-parse HEAD)" = "$REVIEW_HEAD" ] && \
eval "$("$DEV_LEAD/scripts/leg-cmd.sh" opencode review --model opencode/<free-model> --effort <effort> \
--base "$BASE" --target "$REVIEW_TARGET_DIR" --run-dir "$RUN_DIR")" > "$RUN_DIR/review.log" 2>&1What it runs, spelled out (for a lead without the script): the lock-down check, the builder, then the leg inside the frozen target, in a subshell so your own cwd is kept:
{ python3 "$DEV_LEAD/scripts/check-opencode-lockdown.py" "$REVIEW_TARGET_DIR" && \
python3 "$DEV_LEAD/scripts/review-prompt.py" --adapter opencode --base "$BASE" \
--target "$REVIEW_TARGET_DIR" < "$RUN_DIR/prompt.md" > "$RUN_DIR/framed-prompt.md" && \
( cd "$REVIEW_TARGET_DIR" && opencode run --print-logs --log-level info \
-m 'opencode/<free-model>#<effort>' \
< "$RUN_DIR/framed-prompt.md" ); } > "$RUN_DIR/review.log" 2>&1$RUN_DIR must be absolute (leg-cmd exports it that way) and outside the
target; the lock-down opencode.json must already be in the target.
- The
#<effort>suffix on-mis the effort knob (opencode 2; v1 had--variant, which v2 rejects) — this family's--effort. Pass it explicitly; omitting it takes the provider default, and there is noreasoningEffortkey inopencode.jsonto set instead (runtime file). The free pool costs no quota, so this is the one leg where you can spend the top tier without a budget argument. - "In its own step" means its own FOREGROUND step. Measured: a prompt file written by a heredoc inside a backgrounded compound command raced the read in a second background command — opencode received 168 bytes of a 5 KB prompt and, sensibly, asked what the session was for. It looks exactly like a model ignoring instructions. Compare the size of what you piped against the size of what you wrote before believing anything about the run.
- The prompt goes on STDIN, not in argv. An argv prompt over ~1–2 KB
hangs before the session is even created —
message=initthen silence, forever, in every model (measured; size sweep in the runtime file). - Put the claims IN a file inside the repo, not outside it. Anything
outside the project trips
permission=external_directory. Left ataskit is auto-rejected headless and the model treats the refusal as fatal (measured: died in seven seconds having read nothing; the same run later hallucinated a neighboring absolute path and died identically). The config above denies it instead, so the model gets an error and goes on; the claims still belong inside the repo, because a read it cannot make is a finding it cannot check. State the repo root explicitly in the prompt ("your cwd is EXACTLY<path>; use relative paths"). Check the log mechanically before reading it as a verdict:
DEV_LEAD=${DEV_LEAD_ROOT:-$(ls -d "$HOME"/.claude/plugins/cache/dev-lead/dev-lead/* 2>/dev/null | sort -V | tail -1)} "$DEV_LEAD/scripts/leg-log-check.sh" "opencode" "$RUN_DIR/review.log"It passes only a log whose report carries a per-claim verdict word (HOLDS / BROKEN / NOT REACHED / FIXED, or
--expect <regex>for another format), so an empty log, an error dump and a refused read with nothing after it all exit 1. A leg it fails is a MISSING leg, never "no findings" -- measured 2026-09-22 (a peer session): two refused out-of-cwd reads and one 20-minute timeout each left a file that looked like a clean run. It catches "nothing"; the diagnosis below is still yours.Five terminal failures look alike in the log and are not. Ending with
auto-rejectingis the permission wall — YOUR bug; fix the prompt and rerun.Streaming response failed: [502]/[503] … ResourceExhaustedorqueue is fullis the provider out of capacity. A clean exit withtokens.output=0USUALLY means nothing was produced — but not always; see the retraction below.Error: unknown certificate verification errorafter a> build · <model>line is TLS failing before any token — transport; plain rerun. And a fifth shape that carries no error line at all: a clean exit that read every file, ran its greps, and then simply stopped. Observed 2026-08-19, opencode 1.18.18: two consecutivedeepseek-v4-flash-freelegs on the same prompt each worked through the evidence pack and exited 0 without writing a word, whilenemotron-3-ultra-freeon that identical prompt returned a full table minutes later — and a second session on this machine was concurrently runningdeepseek-v4-flash-free.That model id no longer resolves (measured 2026-09-16: zero hits in
opencode models; this account's free pool isbig-pickle,ling-3.0-flash-fin-free,mimo-v2.5-free,muse-spark-1.2/1.3-contributor-free,nemotron-3-ultra-free,nemotron-3.5-lightning-free). The incident above is kept because the FAILURE SHAPE is what matters and it recurs on other models; the id is history, not a dispatch target. Why it left the catalogue is unknown — do not infer it from the workspace switches, which provably do not touch the listing (runtime file).Check before editing anything, and in this order — the error greps first, because every one of them names a cause that a model switch would only reproduce:
grep -c auto-rejecting,grep -E '50[0-9]\]',grep -c 'certificate verification',grep -o 'tokens.output=[0-9]*' | tail -1.Only once all four come back clean — a run that errored nowhere and simply stopped — is the fifth shape in play. Per-model contention is a hypothesis there, not an established cause: one uncontrolled observation cannot separate it from provider state, a model-specific fault, or account quota, and
pgrepcannot prove the other process holds the same model. What the episode does establish is two cheap steps that cost nothing and settle it either way:pgrep -af "opencode run" # another local session? which model?Mind the self-match — a guard whose own command line contains the pattern always finds itself, so read the PIDs rather than counting them. Then rerun the same prompt on a different free model — as a recovery attempt, not as a diagnostic. Two runs separated in time confound the model with everything else that moves between them: provider state, quota, routing, and the model's own nondeterminism. So a second model returning a report buys you a report; it does not establish that the first failure was model-specific, and a second model failing too does not establish provider-wide congestion. If you actually need the cause — usually you do not, you need a review — the cheap next step is to re-run the original model once the switch succeeds: a failure that reproduces on A after B worked is worth believing, one that does not was transient and is not worth more of your afternoon.
Getting this order wrong has a cost beyond the wasted rerun: a permission wall gets rediagnosed as a dead provider pool, or the empty run gets written up as a model-behaviour finding — and that claim then outlives the session in whatever notes the lead keeps.
Retraction worth keeping:
tokens.output=0is a broken accounting metric, not a verdict. Measured: two runs with that fingerprint were genuinely empty; a third with the identical fingerprint contained a complete review with quoted evidence, verified 4/4 against the real files' line counts and last lines.The header grep is a ONE-WAY signal, and reading it as two-way cost a round. A hit (
grep -c '^## ', or your prompt's required section headers) does mean the review is real regardless of the token counter. A zero does NOT mean the run was empty. Measured: a leg returned a full report — a citation table, four findings, two of them that round's only catches — written entirely in bold labels and tables with no markdown headings at all, so the header count was 0. On that count alone it was called a silent-empty run, reported as such to the user, and its findings were nearly dropped. Nothing in the prompt had required##.So the discard decision needs more than one signal. In cost order:
wc -c "$RUN_DIR/review.log" # a real report is rarely tiny grep -o 'step=[0-9]*' "$RUN_DIR/review.log" | tail -1 # loop depth: did it work at all? grep -cE '^#{1,4} |^\*\*|^\| |NOT REACHED|HOLDS|BROKEN' "$RUN_DIR/review.log" grep -v '^timestamp=' "$RUN_DIR/review.log" | tail -200 # then just read the tailThe last line is what actually settles it. A 150 KB log whose final screen is
git statusoutput is empty; one whose final screen is prose carryingfile:linecitations is not — and no counter substitutes for looking. Reading 200 lines is cheaper than re-running the leg, and much cheaper than telling the user a leg produced nothing when it did.- The tell that it is the provider and not your prompt: did this SHAPE work recently? Measured: three consecutive failures on a six-property prompt made the shape look like the cause — but five reviews earlier the same day used exactly that shape and all returned full reports; the pool had simply degraded that afternoon. A prompt that worked in the morning did not become malformed by evening.
- Congestion is provider-wide; switching MODELS inside a bad window does not help — the free pool is one queue. Wait, or route to a different FAMILY.
Hand the delegate a merge-base range, never
origin/main..HEAD— this leg is the most exposed of all, because the prompt tells the model which git command to read the diff with and you never see the output. Pin it:BASE=$(git merge-base origin/main HEAD) git diff --stat "$BASE" HEAD # file list must match the change under review- Launch in the background with a generous timeout (10m+; budget 40m under
congestion — a measured healthy run went silent 10+ minutes mid-generation
and then delivered a full report at ~31 minutes). Treat a missing
evaluated permission=log line as "never really ran". - Running this leg as a subagent? You are a leaf — block, do not "wait".
Nothing will wake you when
opencode runexits; ending your turn on "waiting for the monitor" abandons the review. Poll to exit inside a single tool call, and issue another immediately if it times out (dev-lead Phase 2). This leg's long silent stretches make the urge to end the turn strongest here — and its documented silent-death mode means a leaf that leaves early cannot tell a dead run from a slow one. - Model: the named families are the accounting-valid reviewers. A stealth model (family undisclosed by design) is an ADDITIONAL pair of eyes, never the leg that satisfies the cross-family rule — and never review a stealth model's work with itself. Between the named models, choose by measured hit rate and by structural properties (a huge-context model earns its place when the review must hold a whole subsystem at once); tier and parameter count did NOT predict finding yield in measurement.
- First run in a fresh worktree: confirm the log's
projectID=is a hash, notglobal—globalmeans the read-only config silently isn't loaded and the boundary does not exist (runtime, trap 1).
Bracket the run regardless (belt and braces — the config is the boundary, this is the proof):
git -C "$REVIEW_TARGET_DIR" rev-parse HEAD # before, and again after —
git -C "$REVIEW_TARGET_DIR" status --porcelain=v1 # -C so it's the TARGET, not your cwdWriting the prompt
The prompt you write is the LENS; the framing (above) already opens with the falsify-don't-confirm stance, asks for the base revision, the per-claim answers and the verdict line. Put into the lens what only this round knows: the claims, the surface you worry about, what the tests do not enumerate. Same red-team discipline as the other review skills — it matters more than the model. The short list, with this family's specifics:
- First-party pre-merge framing; falsify, don't confirm; numbered claimed
properties; a declared approximation gets a BOUNDED property; trigger +
observable consequence + severity +
file:lineper finding; state fixes already made; forbid praise and generic summaries. - Ask what the tests do not enumerate — the highest-yield sentence in
the prompt on every family (see
docs/methodology.md). - State the read-only intent in the prompt as well as the config, and forbid recursive delegation.
- Tell it NOT to run tests or builds — the config machine-blocks them, but saying so converts a wasted denied attempt into the useful artifact: "if confirming a finding requires execution, name the exact command and expected result; we run it."
- Tell it not to call MCP tools if the target repo wires any — the diff and prompt are the complete context.
- This family's measured strength is DOCUMENT AND SPEC CONSISTENCY; it is the LENIENT one on attack paths. In paired rounds it alone caught a design silently contradicting two accepted documents, a key reused across protocols with no domain separator, and a test whose name claimed more than its body proved — while passing attack-path defects another family caught. Ask this leg "does this contradict document Y", "is this claim supported by its cited evidence"; do not rely on it alone to decide whether an attack works. (Measure your own split — this is one journal's data.)
- Size the evidence gate to what YOU cannot check (documents get the outsider-questions gate, not the re-derivation gate — see methodology).
- Give every claim an evidence gate with an unguessable anchor: per
file, line count AND verbatim last line; per claim,
file:lineplus the quoted code;NOT REACHEDacceptable, HOLDS-without-quote not.
A long review eats its own prompt — the ask lives in a FILE
Measured: a review that read nine files came back having lost the numbered claim list to its own context compaction — the honest model stopped and said so; a less careful one reconstructs the claims and answers a question you never asked. Reordering the prompt does not help (compaction drops the original user message as a unit). The defense:
- Put the claim list in a file inside the review directory (e.g.
REVIEW-CLAIMS.md) and tell the model to re-read it whenever it needs the wording. A file survives compaction because it can be read again; a prompt cannot. Say the file plusopencode.jsonare the expected untracked entries, or your own tree-dirty check will fire on your scaffolding. - Give a reading budget for big files ("read only lines 2150–2560 of the test file") — context is the resource being exhausted.
- Authorize a partial answer explicitly: "if you run low on context, stop and emit the verdicts you can support with quotes, mark the rest NOT REACHED."
Reporting back
Same contract as every review leg: relay findings faithfully; verify each against the code before acting (and record the measured hit rate in your calibration journal — it is this family's calibration data); hold rejections to the same evidence standard; review-only, no fixes unless asked.
Pairing rule across the suite: the reviewer must come from a different model family than whatever implemented the change. This adapter serves several DISCLOSED families and the accounting reads the served model's family, not the adapter — a stealth model whose family is undisclosed can never be the leg that satisfies the rule, however good its findings are.
The standing headline from measurement: model tier did not predict finding yield — a free leg found the only HIGH on a diff where two paid legs returned nothing usable. Do not drop this leg on the grounds that a stronger reviewer is already running.
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
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.

