In-flight adversarial check on a non-trivial decision BEFORE it stands — distinct from post-hoc review of a finished diff. Use on "stress-test this decision", "are we sure about this", "verify before commit", "poke holes in this", when working in unfamiliar code, or before an irreversible step (migration, prod deploy, public API). Runs earlier than code-review, while course-correction is cheap.
npx skills add https://github.com/BlackBeltTechnology/pi-agent-dashboard --skill doubt-driven-review
A confident answer is not a correct one. Long sessions accumulate context that quietly turns assumptions into "facts" without anyone noticing. Doubt-driven development is the discipline of materializing a fresh-context reviewer — biased to disprove, not approve — before any non-trivial output stands.
This is not /review. /review is a verdict on a finished artifact. This is an in-flight posture: non-trivial decisions get cross-examined while course-correction is still cheap.
A decision is non-trivial when at least one of these is true:
Apply the skill when:
When NOT to use:
If you doubt every keystroke, you ship nothing. The skill applies only to non-trivial decisions as defined above.
This skill is designed for the main-session orchestrator, where Step 3 (DOUBT, detailed below) can spawn a fresh-context reviewer.
skills: frontmatter. A persona that follows Step 3 would spawn another persona — the orchestration anti-pattern explicitly forbidden by references/orchestration-patterns.md ("personas do not invoke other personas").Copy this checklist when applying the skill:
Doubt cycle:
- [ ] Step 1: CLAIM — wrote the claim + why-it-matters
- [ ] Step 2: EXTRACT — isolated artifact + contract, stripped reasoning
- [ ] Step 3: DOUBT — invoked fresh-context reviewer with adversarial prompt
- [ ] Step 4: RECONCILE — classified every finding against the artifact text
- [ ] Step 5: STOP — met stop condition (trivial findings, 3 cycles, or user override)
Name the decision in two or three lines:
CLAIM: "The new caching layer is thread-safe under the
read-heavy workload described in the spec."
WHY THIS MATTERS: a race here corrupts user data and is
hard to detect in QA.
If you can't write the claim that compactly, you have a vibe, not a decision. Surface it before scrutinizing it.
A fresh-context reviewer needs the artifact and the contract, not the journey.
Strip your reasoning. If you hand over conclusions, you'll get back validation of your conclusions. The unit must be small enough that a reviewer can hold it in mind in one read — if it's a 500-line PR, decompose first.
The reviewer's prompt must be adversarial. Framing decides the answer.
Adversarial review. Find what is wrong with this artifact.
Assume the author is overconfident. Look for:
- Unstated assumptions
- Edge cases not handled
- Hidden coupling or shared state
- Ways the contract could be violated
- Existing conventions this might break
- Failure modes under unexpected input
Do NOT validate. Do NOT summarize. Find issues, or state
explicitly that you cannot find any after thorough examination.
ARTIFACT: <paste artifact>
CONTRACT: <paste contract>
Pass ARTIFACT + CONTRACT only. Do NOT pass the CLAIM. Handing the reviewer your conclusion biases it toward agreement. The reviewer must independently determine whether the artifact satisfies the contract.
In Claude Code, the role-based reviewers in agents/ start with isolated context by design and are usable here — see agents/ for the roster and per-domain match.
The adversarial prompt above takes precedence over the persona's default response shape. Personas like code-reviewer are written to produce balanced verdicts with both strengths and weaknesses; doubt-driven needs issues-only output. Paste the adversarial prompt verbatim into the invocation so it overrides the persona's default. If a persona's response shape can't be overridden cleanly, fall back to a generic subagent with the adversarial prompt.
A single-model reviewer shares blind spots with the original author — a colder, different-architecture model catches them. Doubt-driven is already opt-in for non-trivial decisions, so within that scope cross-model is part of the skill's value, not optional friction.
Automatic when a reviewer role is set. If a @propose-review-N role is configured AND probes clean (see *Choosing the reviewer* below), run the cross-model review automatically — do NOT ask the user first. A configured @propose-review-N series IS the user's standing decision that cross-model is wanted; asking again each cycle is redundant friction. Spawn the reviewer, take its output into RECONCILE, and announce in the output that cross-model ran (*"Cross-model review ran automatically on @propose-review-N."*).
Only ask when no reviewer role resolves. When the @propose-review-N series is empty or every role probes empty, fall back to the interactive offer below.
Step 1: Reviewer role set → run automatically; otherwise offer
After the single-model review in Step 3 above, but before RECONCILE:
@propose-review-N role resolves → skip the question. Run the cross-model pass automatically (probe-gated, walking the series), fold its findings into RECONCILE, and note in the output that it ran.@propose-review-N role resolves (none configured, or all probe empty) → pause and offer:> *"Single-model review complete. No reviewer role is configured. Want a cross-model second opinion? Options: configure a @propose-review-N role now, manual external review (you paste it elsewhere), or skip."*
The standing @propose-review-N config — not a per-cycle prompt — is where the user decides whether cross-model is worth the cost. When that config is absent, the agent surfaces the choice instead.
The cross-model path in Pi is a subagent on a @propose-review-N role. Pi's Agent tool takes a model param. Use a role ref (@propose-review-1, @propose-review-2, … @propose-review-x) — NOT a raw provider/model-id. Role refs resolve through the supported model:resolve handler and inherit the parent session's live registry, so a curated reviewer (including a custom-provider model) actually spawns. A raw provider/model-id bypasses that path; for a custom provider the child can build a registry that lacks the provider → empty output (the exact failure this role series exists to avoid). The subagent runs in isolated context by construction, passes ARTIFACT as a prompt string (no shell-escaping hazard), and can open repo files to verify the author's code claims. This skill does NOT shell out to an external review CLI; the only automated reviewer is the in-process subagent.
Agent(
subagent_type: "Explore", # or any read-capable label
model: "@propose-review-1", # a curated cross-model reviewer role
description: "Cross-model adversarial review",
prompt: "<adversarial prompt> + ARTIFACT + CONTRACT" # NO CLAIM, NO reasoning
)
Choosing the reviewer — walk the @propose-review-N role series. The operator pre-configures a numbered series of reviewer roles (propose-review-1, propose-review-2, … propose-review-x in ~/.pi/agent/providers.json#roles, editable via the update_roles tool or the dashboard Roles panel), each pointing at a different-architecture model than the author and verified reachable. Order matters: put cross-model reviewers first and any same-family-as-usual-author model (e.g. an Anthropic model when the author is typically Claude) last, so it only fires as the final fallback. The skill does not hand-pick or fuzzy-match a catalogue — it walks the series in order:
@propose-review-1. Skip any role whose architecture family matches the author's (author is Claude → skip a propose-review-N that maps to claude-*) — a reviewer that shares the author's family shares its blind spots."Reply with exactly: OK"). Non-empty → use that role for the adversarial pass. Empty, or a resolution error → advance to @propose-review-2, @propose-review-3, … until one probes clean.@propose-review-N role resolves (none configured, or all probe empty) and the session is interactive, offer to configure one on the spot — see *Bootstrap a reviewer role* below — before giving up. Only when the user declines, or the context is non-interactive, treat subagent cross-model as unavailable (Step 2) — offer manual review / skip. Never substitute a raw provider/model-id; that is the path that fails silently for custom providers.> Seeding the series (one-time). Assign each propose-review-N role a reachable, different-architecture model, cross-model reviewers first and any same-family fallback last — e.g. propose-review-1 → opencode-go/glm-5.2, propose-review-2 → deepseek/deepseek-v4-pro, propose-review-3 → anthropic/claude-opus-4-8 (Opus last). Enumerate reachable ids with pi --list-models or dashboard GET /api/models when deciding what to assign; assign via update_roles.
> Bootstrap a reviewer role (interactive, when the series is empty or unreachable). On a machine where no @propose-review-N role is set — or none resolves — do NOT jump straight to skip. Configure one live:
> 1. Enumerate the reachable set with the list_models tool (or pi --list-models / dashboard GET /api/models). Keep only credentialed rows (excludedReason: null); prefer reasoning: true.
> 2. Drop the author's own architecture family (author is Claude → exclude anthropic/* / claude-*) so the reviewer does not share the author's blind spots.
> 3. Ask the user to choose via ask_user (method select), offering a handful of the strongest different-family candidates from the enumerated set (do not auto-pick).
> 4. Assign it via the role API — the update_roles tool: set_role, role: "propose-review-1" (or the next free propose-review-N slot), ref: "<chosen provider/model-id>". This persists to providers.json#roles, so the choice carries to every future session; never hand-edit the file.
> 5. Probe then spawn on the freshly-assigned @propose-review-N (same "Reply with exactly: OK" gate).
>
> Interactive only — never enumerate-and-ask in CI / /loop / autonomous runs; there, skip per the non-interactive rule below.
Prerequisite + fallback (verify, do not assume):
@propose-review-N role must be configured AND reachable. An unconfigured role, or one pointing at a non-reachable id, makes the spawn return empty output. Probe first ("Reply with exactly: OK"); empty → advance to the next @propose-review-N, never proceed on the empty one. (This series is preferred precisely because a role ref resolves through the parent's live registry; a raw provider/model-id can silently miss a custom provider.)@propose-review-N series is exhausted with no clean probe, surface it explicitly (per Step 2 below) and fall back to manual external review. Do NOT silently proceed single-model, and do NOT substitute a raw provider/model-id.Step 2: If the subagent reviewer is unavailable or fails
Surface the failure explicitly. Offer: run the review manually (paste ARTIFACT + CONTRACT + adversarial prompt into an external model yourself), or skip. A @propose-review-N series exhausted with every role probing empty counts as a failure — announce it. Do not silently fall back to single-model — the user should know cross-model didn't happen.
Step 3: If the user skips
Acknowledge the skip in the output (*"Proceeding with single-model findings only"*) and continue to RECONCILE. Skipping is fine; silent skipping is not.
Non-interactive contexts (CI, /loop, autonomous-loop, scheduled runs):
Cross-model adds cost and latency. When a reviewer role is set it runs automatically; when none is set, the agent surfaces the choice and the user decides whether this artifact warrants manual review.
The reviewer's output is data, not verdict. You are still the orchestrator. Re-read the artifact text against each finding before classifying — rubber-stamping the reviewer is the same failure mode as ignoring it.
For each finding, classify in this precedence order (first matching class wins):
A fresh reviewer can be wrong because it lacks context. Don't defer just because it's "fresh."
Stop when:
If after 3 cycles the reviewer still surfaces substantive issues, the artifact may not be ready. Surface this to the user — three unresolved cycles is information about the artifact, not a reason to keep looping.
If 3 cycles is "obviously insufficient" because the artifact is large: the artifact is too big — return to Step 2 and decompose. Do not lift the bound.
| Rationalization | Reality |
|---|---|
| "I'm confident, skip the doubt step" | Confidence correlates poorly with correctness on novel problems. Moments of certainty are exactly when blind spots hide. |
| "Spawning a reviewer is expensive" | Debugging a wrong commit in production is more expensive. The check is bounded; the bug isn't. |
| "The reviewer will just nitpick" | Only if unscoped. Constrain the prompt to "issues that would make this fail under the contract." |
| "I'll do doubt at the end with /review" | /review is a final gate. Doubt-driven catches wrong directions early when course-correction is cheap. By PR time it's too late. |
| "If I doubt every step I'll never ship" | The skill applies to non-trivial decisions, not every keystroke. Re-read "When NOT to Use." |
| "Two opinions are always better than one" | Not when the second has less context and produces noise. Reconcile, don't defer. |
| "The reviewer disagreed so I was wrong" | The reviewer lacks your context — disagreement is information, not verdict. Re-read the artifact, classify, then decide. |
| "Cross-model is always better" | Cross-model catches blind spots a single model shares with itself, but it adds cost and latency. When a @propose-review-N role is set it runs automatically (the config is the standing decision); when none is set, offer it every interactive doubt cycle so the user decides. |
/review, not doubt-driven development@propose-review-N subagent; the CLI escape hatch was removed@propose-review-N role set, the offer must be visible — skipping is fine, silent skipping is not. (When a role IS set, cross-model runs automatically without an offer — that is the intended behavior, not a red flag.)code-review-and-quality / /review: complementary. /review is post-hoc PR verdict; doubt-driven is in-flight per-decision. Use both.source-driven-development: SDD verifies *facts about frameworks* against official docs. Doubt-driven verifies *your reasoning about the artifact*. SDD checks the API exists; doubt-driven checks you used it correctly under the contract.test-driven-development: TDD's RED step is doubt made concrete — a failing test is a disproof attempt. When TDD applies, that failing test *is* the doubt step for behavioral claims.debugging-and-error-recovery: when the reviewer surfaces a real failure mode, drop into the debugging skill to localize and fix.references/orchestration-patterns.md): this skill orchestrates from the main session. A persona calling another persona is anti-pattern B — see Loading Constraints above.After applying doubt-driven development:
@propose-review-N role was configured and probed clean, cross-model ran automatically (no per-cycle offer) and the fact it ran was announced in the output; when no role resolved, cross-model was explicitly offered to the user and the response was acknowledged. When run, the preferred Pi path (subagent on a @propose-review-N reviewer role) was tried first; an empty probe advanced to the next role in the series, and a fully-exhausted series was surfaced as a failure, not silently swallowed@propose-review-1..@propose-review-x role series (probe-gated), each a different architecture family than the author — not a hand-picked raw provider/model-id. When the series was empty/unreachable in an interactive session, a role was bootstrapped first: list_models → ask_user selection → update_roles set_role, before any manual-review/skip fallback@propose-review-N subagentTake blackbelttechnology/doubt-driven-review from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.