Skip to main content
A builder implements changes; a validator with read-only tools checks the work. How it works:
  1. The builder agent has full tool access and implements changes
  2. The validator agent has only read-only tools (Read, Grep, Glob, Bash) — it cannot write or edit code
  3. When the validator flags issues, it creates a fix task for a new builder
  4. The cycle repeats with a hard cap of 3 iterations — after that, escalate to a human
Why read-only matters: if the validator can edit code, it silently “fixes” things instead of surfacing problems. Restricting tools forces it to articulate issues clearly, which produces better fixes. Agent definition for a validator:
---
name: validator
description: Reviews implementation against acceptance criteria. Read-only.
model: sonnet
tools: ["Read", "Grep", "Glob", "Bash"]
disallowedTools: ["Write", "Edit"]
---
Best for: any work where correctness matters more than speed — API changes, security-sensitive code, data migrations.

Iteration loop

Builder → Validator → (issues?) → Builder (revision) → Validator → ... (max 3)
Scope thresholds per agent:
Tasks per agentStatus
2-3Good
4Warning
5+Split required

Verification depth

Don’t stop at “file exists.” Most failures hide between Substantive and Wired — the file exists and looks real, but nothing calls it.
LevelQuestion
ExistsIs the file at the expected path?
SubstantiveReal implementation, not a stub?
WiredImported and called by the system?
FunctionalActually works when invoked?

Self-Validating Agents

A lighter variant: embed validation directly in agent definitions using PostToolUse hooks. Every file the agent writes gets checked automatically. Three tiers:
  • Micro — PostToolUse hook runs a linter/formatter after every write
  • Macro — Stop hook checks required files exist + tests pass before “done”
  • Team — Separate read-only validator (the standard pattern above)
Inline hooks catch syntax issues. They don’t replace a read-only validator for semantic correctness — a linter won’t catch a component that exists but is never imported.

Examples in the wild

ExampleWhat it shows
plan-challengerAdversarial validator: attacks a plan on 5 dimensions, then refutes each challenge before flagging it. Output: blockers, concerns, nitpicks.
gsd-plan-checkerGoal-backward plan verification before any code is written. Loops with the planner up to 3 times.