agent-patterns

tool-grounded-review

flowchart TD
    Change([Code change]) --> Context[Gather context\nconventions, ADRs, git history]
    Change --> Tools[Run declared checks\ntests, lint, scans]
    Context --> Reviewer[Reviewer agent]
    Tools --> Evidence[(Evidence\nPASS / FAIL / UNVERIFIED / N/A)]
    Evidence --> Reviewer
    Reviewer --> Gate{Evidence}
    Gate -- any FAIL --> Changes([REQUEST_CHANGES\n+ failure output])
    Gate -- any UNVERIFIED --> Policy([Blocked or surfaced\nper policy])
    Gate -- all PASS / N/A --> Verdict([LLM verdict])
    Changes --> Human([Human gate])
    Policy --> Human
    Verdict --> Human

A reviewer agent judges a change from executed evidence, not from reading the diff alone. Before producing a verdict it gathers the context the diff does not show and runs the repository’s own checks; a failing check blocks approval regardless of what the model concludes.

How it works

  1. Gather context — bounded to the touched files: governing conventions (CLAUDE.md, ADRs), recent git history, callers of changed symbols.
  2. Run checks — tests, linters, type-checkers, scans. The commands are declared (config or repo docs), never invented by the model.
  3. Build evidence — each check yields PASS, FAIL, or UNVERIFIED (timeout, spawn error, stale commit, not executed), plus the tail of its output.
  4. Review — the evidence is injected into the reviewer’s context. The model analyzes failures and may loop to confirm a hypothesis.
  5. Gate in code — any FAIL forces REQUEST_CHANGES deterministically. UNVERIFIED never passes silently: it is surfaced, and the policy decides whether it blocks.
  6. Feedback — the failure output travels verbatim with the verdict, so a fixer agent gets a concrete target instead of a paraphrase.

Variants

  A — pushed evidence B — tool-use loop
Who chooses what to run Code (fixed list) The model, within a call budget
Cost / latency Predictable Variable
Determinism High Low
Model requirement Any Reliable tool calling
Can investigate No — sees only what it is given Yes — reads files, re-runs a check
Where tools execute Separate step, can be isolated from secrets Same process as the LLM client, unless proxied

Start with A. Move to B when reviews are missing context the fixed list cannot anticipate — and keep A’s evidence as B’s seed context.

Where it came from

When to use

When not to use

Trade-offs

   
Pro A FAIL blocks approval independently of model quality
Pro Failure output is concrete, verifiable feedback for a fixer
Pro The verdict states what was and was not executed — the human gate decides with full information
Con Review latency includes the check duration; checks often run twice (CI + review)
Con Grounding is only as good as the declared checks — an undocumented repo gets weak evidence
Con Executing the change is a trust boundary that must be designed, not assumed

Failure modes

Implementations

autonomous-dev-loop — variant A (ADR-0024)

ai-dev-tools — closer to variant B (ADR-009)

Reference implementation

impl.mjs — both variants on a local git repo, reviewing git diff <base>:

node impl.mjs <repo-path>                    # variant A: pushed evidence (base defaults to HEAD~1)
node impl.mjs <repo-path> --loop             # variant B: bounded tool-use loop
node impl.mjs <repo-path> --base=origin/main # review a branch against its base
node --test impl.test.mjs                    # the gate: verdict parsing and evidence policy (no dependency needed)

What it implements from the failure modes above:

Variant A gathers context only from a root CLAUDE.md / AGENTS.md; variant B reads what it needs.

Does not cover (handle these before relying on it): what a script does still comes from the reviewed tree — the change can rewrite "test" to true (self-neutering checks: run the commands from the base ref, or flag a modified package.json as non-authoritative); evidence is not bound to a commit SHA (stale evidence); checks run on the host, isolated only by env (run them in a container without secrets).