# AgentReview Phase 3: Three Agents, One Base Class, and Why Lane Discipline Is an Engineering Problem

Published: 2026-08-08 · Tag: AI
Canonical: https://vondraysanford.com/writing/2026-08-08-agentreview-phase-3-three-agents-one-base-class-and-why-lane.html
Author: Vondray Sanford (https://vondraysanford.com)

> Once the review pipeline moved into a shared base class, the Security Agent came together in under a day and the Docs Agent went even faster. The proof: 55 tests, 15 committed sample reviews, and a security agent that stays quiet on all four non-security diffs.

A multi-agent system earns its name when you can add a second and third agent without rebuilding the first one from scratch. This was the focus for Phase 3. I went from one working Code Review Agent to three by adding a Security Agent and a Docs Agent, and the thing that made it fast was simply adding a base class.

## What the Base Class Does

By the end of Phase 2, the review pipeline had some serious machinery in it. It had guardrails that keep the LLM from hallucinating files that don't exist in the diff, grounding logic that anchors every finding to an actual line range, a deduplication pass so the same issue doesn't get reported twice with slightly different wording, and an ordering step so findings come back prioritized instead of in whatever sequence the model felt like. I WAS NOT going to copy-paste it into every new agent I built.

So I extracted all of it into a shared `BaseReviewAgent`. Every agent that inherits from it gets the full pipeline for free. Each subclass owns the static tool it runs before the LLM sees anything, and the prompt that tells the model what lane it's in. That's the whole surface area of a new agent, and it shows in the build times.

The Security Agent took a part of a day to reach a working state and the Docs Agent took even less. When adding an agent means writing a prompt and wiring a tool rather than re-implementing a pipeline, you stop dreading the next one.

## Two Agents, Two Very Different Philosophies

The Security Agent and the Docs Agent turned out to be a useful contrast in how much you can rely on static analysis versus how much you have to hand off to the model.

For security, I integrated [Semgrep](https://semgrep.dev/). It runs first, its output gets injected into the prompt as grounding context, and then the model hunts for hardcoded credentials that rule-based scanners provably miss, something Semgrep can't do. It finds things like a password shoved into a config dictionary with a non-obvious key name, or an API token assigned to a variable that doesn't pattern-match to anything a scanner knows to look for. The prompt is explicit about what Semgrep already covered and what the model is being asked to find on top of that. The grounding code makes sure every finding maps back to a real line in the diff.

The Docs Agent has no static tool at all. I looked for one and there simply isn't a deterministic tool that can tell you whether the README should have been updated given a set of code changes. This is ultimately a judgement call that requires understanding what the code does, what the README claims the code does, and whether the gap between those two things matters so, the Docs Agent is pure prompt plus grounding. The model gets the diff, it gets the current docs content, and the prompt asks it to reason about whether anything in the diff changes user-facing behavior that isn't reflected in the documentation. No Semgrep, no linter, no AST needed, just a well-scoped question and a grounded context window.

I expected to feel worse about the Docs Agent not having a static tool, but I don't. Some problems are LLM-native, and forcing a deterministic tool in front of them just adds noise without adding signal.

## The Number That Actually Matters

I ran every agent over every sample diff, five diffs total covering a range of different change types, and committed all 15 reviews. The Security Agent returned zero findings on four of those five diffs, and on the fifth, the one with the planted security issues, it caught exactly what I put there.

A code quality agent that also reports security issues is a noisy one that will fill your output window with false flags. When the Security Agent looks at a refactor diff and says nothing, that's the correct answer. Getting an LLM to say nothing when nothing applies is actually harder than getting it to say something, and it's where most prompt engineering breaks down. The combination of explicit grounding, a tightly scoped system prompt, and the guardrail layer in the base class is what holds that boundary.

The test suite is at 55 tests now, all running against a locked schema and the schema ended up mattering more than I expected. Having a contract that every agent's output validates against means I catch regressions immediately when a prompt change causes a finding to come back in a shape the downstream consumer doesn't expect.

## No Framework Yet, and That's Still Intentional

A few people have asked why I haven't reached for LangChain or a similar orchestration framework at this point. The honest answer is that the system is still small enough that I understand every line of it, and I want to keep it that way for now. The base class is doing what a framework would do for agent composition, but it's 200-something lines of code I wrote and can read. When I need cross-agent orchestration, running all three in parallel and merging their outputs into a single review, I'll evaluate what a framework buys me at that point. Right now it would add more surface area than it removes.

Phase 4 is going to be about orchestration, so I'll have to face this issue soon. The goal is a single entrypoint that fans out to the Code, Security, and Docs agents, collects their findings, deduplicates across agent boundaries, and returns a unified review. There are some interesting problems in that merge step, specifically around what happens when two agents flag the same line for different reasons, and I'm looking forward to working through them.

For now, I have three agents, one base class, 55 tests, and a security agent that knows when to stay quiet. That's a good outcome from Phase 3.
