Executive Snapshot

Aspect Detail
What it is A Claude Code workflow that fans one change out to competing AI models, selects the strongest plan by a fixed rubric, and blocks completion until tests pass
Best for High-stakes or ambiguous changes where a single AI pass is too risky to accept unreviewed
Core primitives Subagents (.claude/agents/), a skill entry point, and a stop-class hook
Models Opus and Sonnet as advocates; an optional stronger model for multi-file reasoning
Setup time 30–45 minutes
What it buys you Reproducibility, an audit trail, and no silently averaged conflicts
Assumes Working knowledge of the Claude Code CLI and your project's test command

TL;DR

  • One AI pass is a single point of failure. The advocate/adjudicator pattern runs two or three models against the same brief, then reconciles their plans — you review a decision, not a guess.
  • Advocates plan; they never write. Read-only tool allowlists (tools: Read, Grep, Glob) make "propose only" a hard constraint enforced by Claude Code, not a polite request.
  • The adjudicator selects or synthesizes exactly one plan by an ordered rubric, and surfaces load-bearing conflicts instead of averaging them.
  • The test gate is a hook, not a reminder. A stop-class hook makes "tests must pass" mandatory — but the exact block mechanism is version-specific, so verify it before you rely on it.
  • Verify before you build. Claude Code's frontmatter keys, model aliases, and hook schema change often. A five-minute Phase 0 check against your installed version prevents silent failures.

Introduction

Claude Code will happily take a review comment or an audit finding and apply it in one shot. For a typo, that is exactly what you want. For a change that touches authentication, a data migration, or a module three other services import, a single unreviewed AI pass is a liability — you get one opinion, no dissent, and no record of why that approach won.

The advocate/adjudicator pattern borrows a discipline from engineering review boards. You ask two or three models to independently plan the same change, hand their plans to a neutral adjudicator that picks the strongest one by a written rubric, and only then execute — behind a gate that refuses to call the work "done" until the test suite is green. This tutorial shows how to build that workflow on Claude Code's own primitives: subagents, skills, and hooks. Every model routing decision, rubric criterion, and gate is a checked-in file, so the behavior is reproducible and auditable rather than a prompt you retype from memory.

Prerequisites

  • Claude Code installed and authenticated. Confirm with claude --version and record the version — the surfaces below are version-sensitive.
  • A project with a real test command (npm test, pytest -q, dotnet test, go test ./..., or your equivalent). The gate is only as honest as the suite behind it.
  • Familiarity with subagents and hooks. You do not need to have authored one before, but you should know they live under .claude/.
  • Comfort running /model and /hooks inside a session to inspect what your install actually supports.

1. Why one AI pass is not enough

A single agent applying a suggestion has three structural weaknesses. First, it commits to the first approach that looks plausible; you never see the road not taken. Second, when two reasonable approaches exist — inheritance versus composition, a migration versus a shim — the agent picks one silently, and you inherit that decision without knowing a fork existed. Third, "I ran the tests" is a claim, not a guarantee, unless something outside the agent enforces it.

The pattern addresses each weakness directly:

  • Divergence is made visible by running independent advocates and comparing their plans.
  • Load-bearing conflicts are surfaced, never averaged — the adjudicator stops and asks you rather than blending two incompatible designs into one that satisfies neither.
  • Passing tests become a precondition for completion, enforced by a hook that the agent cannot talk its way past.

Here is the full flow before we build each piece.

flowchart TD
    A["Suggestion or review finding"] --> B{"Complexity gate"}
    B -->|"Trivial / Standard"| C["Single advocate (Sonnet)"]
    B -->|"Complex"| D["Advocate: Opus"]
    B -->|"Complex"| E["Advocate: Sonnet"]
    B -->|"Multi-file refactor"| F["Advocate: superior model"]
    C --> G["Adjudicator"]
    D --> G
    E --> G
    F --> G
    G -->|"One plan chosen"| H["Execution"]
    G -->|"Load-bearing conflict"| I["Stop: surface to human"]
    H --> J{"Tests pass?"}
    J -->|"No (max 2 retries)"| H
    J -->|"Yes"| K["Change accepted"]

2. Phase 0: verify before you build

Claude Code's frontmatter fields, model aliases, hook schema, and the relationship between skills and commands are version-specific and change frequently. The single most valuable habit when building on top of the tool is to confirm the surface against your installed version before writing files, not to trust a template — including this one.

Run these checks at the start of any build:

  • claude --version — record it; every claim below is relative to a version.
  • /model — record the exact model identifiers selectable in your environment (for example opus, sonnet, haiku, fable, or full strings like claude-opus-4-8). Do not pin a model you have not seen in this list.
  • /hooks — confirm the SubagentStop configuration shape and, critically, how a hook blocks: exit code 2 with a message on stderr, or a JSON decision object on stdout. Blockability differs by event.
  • Skills vs commands — as of the 2026 unification, both .claude/skills/<name>/SKILL.md and .claude/commands/<name>.md produce a /<name> entry point, and a same-named skill takes precedence over a command. Pick one form; do not ship both under the same name.

Treat anything that disagrees with what you find as stale, patch it, and note the change. This "the live version wins" rule is what keeps the whole workflow from failing quietly six months from now.

3. Build the advocates

Advocates are read-only subagents. Each one reads the repository, then returns a plan — never an edit. The read-only guarantee comes from the tools allowlist in frontmatter, so it is enforced by Claude Code rather than by a sentence in the prompt that a model might rationalize away.

Create .claude/agents/advocate-opus.md:

---
name: advocate-opus
description: Read-only advocate. Produces an implementation PLAN for a suggestion, grounded strictly in discovered repo state. Never edits files or runs tests.
tools: Read, Grep, Glob
model: opus
---

You are an implementation advocate. You are given a suggestion and the current repo state.

Produce a PLAN ONLY. Do not edit files. Do not run tests. Do not assume the contents of any
file you have not read.

Return exactly this contract:

{
  "approach": "<one-paragraph strategy>",
  "files_to_change": ["<path>", ...],
  "rationale": "<why this approach>",
  "risks": ["<risk>", ...],
  "test_plan": ["<test or check that would validate this>", ...],
  "open_questions": ["<anything unresolved>", ...]
}

Bias toward the smallest sufficient change. Surface uncertainty in open_questions
rather than guessing.

Duplicate the file as advocate-sonnet.md and change only two lines: name: advocate-sonnet and model: sonnet. Identical instructions, different model — that difference is the entire point. When Opus and Sonnet reach the same plan independently, your confidence should rise; when they diverge, you have found a real decision worth a human's attention.

Two AI model engines side by side, each independently drafting a plan from the same brief
Two advocates, one brief: each model plans independently. Agreement raises your confidence; divergence surfaces a decision worth a human’s review.

Routing a stronger model for hard problems

For genuinely complex work — a multi-file refactor that requires reasoning over the whole module graph — you may want a stronger reasoning model as a third advocate. This is where Phase 0 pays off. Claude Code exposes premium Mythos-tier models (for example fable, the alias for claude-fable-5), but availability varies by install, so let /model decide:

---
name: advocate-superior
description: Read-only advocate for COMPLEX, multi-file refactors requiring reasoning over the full module graph. Produces a PLAN only. Never edits files or runs tests.
# MODEL: set to the premium identifier from /model IF it is available; otherwise pin the
# strongest reasoning model you do have (opus) and raise its effort. Never write an
# unverified identifier — a bad value can fail silently.
model: opus
effort: xhigh
tools: Read, Grep, Glob
---

You are the advocate reserved for genuinely complex tasks: multi-file refactors and
reasoning across the full module graph. Trace cross-file dependencies explicitly.
Return the same plan contract as the other advocates.

Two rules keep premium routing predictable. Pin by task shape, not by subject matter. Scope this advocate to "multi-file refactor over the module graph," never to a topic — some premium models apply their own routing safeguards, so a topic-based trigger can bounce to a different model than you expected. And never write a model identifier you have not seen in /model. An invalid value can fail without an obvious error, and you will not notice until a subagent quietly runs on the wrong model.

The effort field (values run low through max) is the lever that makes a same-model advocate stronger without a model swap — an Opus advocate at xhigh is a legitimate substitute when a premium tier is unavailable.

4. Build the adjudicator

The adjudicator is also read-only. It receives every advocate plan and returns exactly one — chosen or synthesized — by a rubric applied in order. The ordering matters: it removes the temptation to trade correctness for a smaller diff.

Create .claude/agents/adjudicator.md:

---
name: adjudicator
description: Selects or synthesizes ONE plan from the advocate outputs using a fixed rubric. Surfaces conflicts rather than averaging them. Read-only.
tools: Read, Grep, Glob
model: opus
---

You receive two or three advocate PLANS. Select or synthesize exactly ONE, using these
criteria IN ORDER:

1. Correctness against the suggestion and the actual repo state.
2. Smallest sufficient diff.
3. Test coverage of the risks[] each plan surfaced.
4. Fewest unresolved open_questions[].

Output the chosen plan in the advocate contract shape, plus one line of justification
per criterion.

If the advocates conflict on a LOAD-BEARING decision (one that changes the outcome, not
cosmetics), DO NOT average or split the difference. STOP and return:

{
  "status": "CONFLICT",
  "decision_point": "<what they disagree on>",
  "position_a": "<advocate A's stance + reasoning>",
  "position_b": "<advocate B's stance + reasoning>",
  "recommendation": "<your read, if any>"
}

The CONFLICT branch is the most valuable behavior in the whole workflow. Averaging two incompatible designs produces a third design nobody vetted. By stopping and presenting both positions, the adjudicator turns the moment where automation usually hides a decision into the moment it escalates one to you.

Multiple candidate plans converging into a single adjudication decision node
The adjudicator reconciles every advocate plan into one by an ordered rubric — and halts to surface a load-bearing conflict rather than averaging it away.

5. Wire the entry point as a skill

The orchestration logic — which advocates to invoke, in what order, and what to do with a conflict — belongs in a skill. Skills are judgment; keep them in Markdown. (Deterministic work like running tests belongs in scripts, which we get to next.)

Create .claude/skills/delegate/SKILL.md:

---
name: delegate
description: Delegate a suggestion to advocate/adjudicator subagents across models, then execute the winner behind a maker-checker gate. Use when applying a review finding, audit item, or change request that benefits from independent plans.
argument-hint: <suggestion or reference to it>
---

## Suggestion to apply
$ARGUMENTS

## Repo state (ground truth — do not assume beyond this)
- Status: !`git status --short`
- Recent history: !`git log --oneline -10`

## Phase 0 — Complexity gate
Classify the suggestion as TRIVIAL / STANDARD / COMPLEX:
- TRIVIAL or STANDARD -> invoke ONE advocate (advocate-sonnet). Skip the fan-out.
- COMPLEX -> invoke advocate-opus AND advocate-sonnet; if it is a multi-file refactor
  over the module graph, invoke advocate-superior instead of the pair.

## Phase 1 — Advocacy (read-only, PLAN only)
Dispatch the selected advocate(s). Each returns the plan contract. No files written.

## Phase 2 — Adjudication
Dispatch the adjudicator with all advocate plans. It returns ONE plan, or a CONFLICT
object. If CONFLICT: stop and present to the user.

## Phase 3 — Execution (only after a plan is chosen)
Implement the adjudicated plan. Run the test_plan via scripts/run-tests.sh. The
maker-checker hook enforces the gate; correct and retry a maximum of 2 times, then escalate.

Two features do real work here. $ARGUMENTS injects the suggestion you pass on the command line. And the !`command` syntax runs a shell command before the model sees the skill, splicing the live git status and recent history into the prompt — so every run is grounded in the actual repository state rather than the model's assumptions about it.

The complexity gate is a cost control. Fanning three models out on a one-line rename burns tokens to produce three near-identical plans. Reserve the fan-out for changes where two competent engineers would plausibly disagree on approach; when they would converge, one advocate is enough.

6. Make the test gate mandatory with a hook

Everything so far is judgment, and judgment can be argued with. "Did the tests pass?" should not be. Move it out of the prompt and into a hook, where it becomes a precondition the agent cannot skip.

An automated gate on a delivery pipeline blocking a change until tests pass
The maker-checker gate lives in a hook, not the prompt: completion stays blocked until the test suite passes, with at most two correction retries before escalation.

Create scripts/maker-checker-gate.sh:

#!/usr/bin/env bash
# Stop-class hook: blocks completion until the test suite passes.
# VERIFY the block mechanism for your version with /hooks:
#   - exit code 2 + stderr, OR
#   - a JSON decision object on stdout.
# This implements the exit-code-2 form.
set -uo pipefail

if [ ! -x "./scripts/run-tests.sh" ]; then
  exit 0   # nothing to test; allow the stop
fi

OUTPUT="$(./scripts/run-tests.sh 2>&1)"
STATUS=$?

if [ "$STATUS" -ne 0 ]; then
  echo "MAKER-CHECKER GATE: tests failed. Do not mark complete. Output:" >&2
  echo "$OUTPUT" >&2
  exit 2
fi
exit 0

Register it in .claude/settings.json:

{
  "hooks": {
    "SubagentStop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "./scripts/maker-checker-gate.sh" }
        ]
      }
    ]
  }
}

Then wire scripts/run-tests.sh to your real suite (npm test, pytest -q, and so on), exiting non-zero on failure. Make both scripts executable with chmod +x scripts/*.sh.

Version check — do not skip this. Whether a stop-class hook can actually block completion, and whether it does so via exit code 2 or a JSON {"decision":"block"} object on stdout, is version-specific. Run /hooks and confirm before you depend on it. If SubagentStop is not blockable in your version, attach the same script to a blockable event such as PostToolUse, or make the test run an explicit, non-skippable step of the skill. The principle is fixed — enforcement lives in a hook, not in prose — even where the exact event differs.

The distinction is the whole reason this step exists: a sentence in a prompt that says "make sure tests pass" is advisory, and models under pressure to finish will occasionally declare success anyway. A hook that returns a blocking signal is mandatory. Design the gate as the mandatory kind.

7. Use it

With the pieces in place, invoke the skill with the suggestion — or a reference to where the suggestion lives:

/delegate Apply the fix from the audit finding in section 3: the token refresh
path does not handle a revoked refresh token and loops. Ground every subagent in
the actual repo state, read-only plan mode first. Stop and surface conflicts
rather than silently picking one.

What happens next is deterministic in structure: the complexity gate decides how many advocates run, each advocate returns a plan against the contract without touching a file, the adjudicator returns one plan or a CONFLICT, and — only after a plan is chosen — execution proceeds behind the test gate, retrying at most twice before escalating to you. Every step is a checked-in file, so the same command produces the same workflow for every engineer on the team, and the reasoning is inspectable after the fact.

Troubleshooting

  • An advocate tried to edit a file. It cannot if tools: Read, Grep, Glob is set — the write is blocked by the allowlist. If an edit slipped through, the allowlist was loosened; restore it. Never enforce read-only by instruction alone.
  • A subagent ran on the wrong model. You likely pinned an identifier that /model does not expose in this install, and it failed silently or fell back. Re-run /model, use only listed identifiers, and prefer raising effort over guessing at a premium alias.
  • The gate never blocks a failing run. The hook event is not blockable in your version, or the mechanism is JSON rather than exit code 2. Confirm with /hooks and either switch the mechanism or move the gate to a blockable event such as PostToolUse.
  • Both a skill and a command exist with the same name. The skill wins. Delete the redundant form to avoid confusion about which body is running.
  • Three near-identical plans on a trivial change. The complexity gate is being skipped. Tighten it so TRIVIAL and STANDARD changes take a single advocate.
  • The adjudicator averaged two conflicting designs. Reinforce the CONFLICT contract and confirm it is stopping rather than merging; a blended design that nobody vetted is worse than either original.

Key Takeaways

  • Independent advocates on different models turn a guess into a reviewable decision — divergence between them is signal, not noise.
  • Enforce roles with tooling, not prose. Read-only advocates come from the tools allowlist; the test gate comes from a hook. Both are constraints the model cannot argue with.
  • Never average a load-bearing conflict. The adjudicator's job is to stop and surface incompatible designs so a human decides.
  • Route premium models by task shape, and only after /model confirms the identifier. An unverified model string can fail quietly.
  • Phase 0 verification is non-negotiable. Frontmatter, model aliases, and hook semantics drift between versions; a five-minute check against your install beats a silent failure later.
  • Keep judgment in Markdown and determinism in scripts. Routing and adjudication are prose; test execution and the gate are code.
  • Scale the ceremony to the change. A typo takes one advocate and no gate drama; an auth refactor earns the full fan-out.

Next Steps

  • Add two eval fixtures on a scratch branch: one trivial change that must take a single advocate, and one deliberately ambiguous refactor that must produce a surfaced CONFLICT with no edits. Run them after any change to the agents, skill, or hook.
  • Point scripts/run-tests.sh at your real suite and force a failure to confirm the gate actually blocks completion.
  • Package the four agents, the skill, and the hook as an installable Claude Code plugin (manifest at .claude-plugin/plugin.json) so teammates can adopt the workflow with one install command.
  • Extend the adjudicator rubric with a project-specific criterion — for example, "prefers the plan that adds no new dependency" — and version it alongside the code it governs.

Related Articles

  • What's New in Microsoft 365 Copilot: The Mid-2026 Roundup for IT Pros — how model choice, including Claude, is arriving inside mainstream productivity tooling, framed for governance.
  • Copilot for Microsoft 365: Rollout, Licensing, Data Readiness, and Governance — the control-and-governance mindset applied to AI rollouts, which the maker-checker gate mirrors for code changes.
  • Azure Bicep vs Terraform in 2026: Choosing Your Azure IaC Tool — another "make the decision explicit and reproducible" workflow, applied to infrastructure as code.
Thorsteinn Halldorsson Senior Cloud Engineer

Senior Cloud Engineer with 25+ years of hands-on experience across the datacenter-to-cloud stack: fiber SAN and disk storage, IBM/Lenovo blade and Dell/HP/Lenovo servers, Hyper-V and VMware clusters, and SQL and Remote Desktop Services (RDS) clusters. Deep in the Microsoft platform — Active Directory, PKI/certificate services, SQL, Power BI, Dynamics 365 Business Central (NAV) and AX (Axapta), Microsoft 365, Entra, and Intune — with a focus on Azure operations, FinOps, and applying AI tools like GitHub Copilot and Claude in real workflows. Writes practical, no-nonsense guides for IT professionals who need to ship real solutions.

Leave a Reply

Your email address will not be published. Required fields are marked *