Beyond AI-Driven Development, Part 2: The Dual-Loop Verification Engine

Writing the code takes thirty seconds. Reviewing it takes forty-five minutes. That imbalance has a name now.

Beyond AI-Driven Development, Part 2: The Dual-Loop Verification Engine

If you feel like you’ve traded actually building software for babysitting five-hundred-line pull requests, you’re paying what I call the AI Verification Tax. It’s the hidden cost nobody budgeted for when they adopted AI coding agents: the more an agent can generate in a single pass, the more a human has to read, understand, and trust before it ships.

In Part 1 of this series, I argued that AI agents have become the new compiler — source code is turning into the intermediate representation, and the real engineering work has moved to defining boundaries and validating output. I introduced the idea of a semantic grounding stage in CI as the fix for architectural hallucination. This post is about building that stage properly, and about the loop that has to exist before it.

In modern Spec-Driven Development — whether you follow formal AI-DLC or another specification framework — code generation is no longer the bottleneck. The friction has shifted downstream, into the verification of what the AI actually produced.

Two Extremes, One Missing Middle Link to heading

When teams first adopt AI-driven workflows, they almost always fall into one of two traps trying to manage this verification burden.

The Unit Test Fallacy is the first. Teams generate hundreds of unit tests alongside AI-written code and treat full coverage as proof the feature is done. In the early days, this made a strange kind of sense — less capable models would take shortcuts you weren’t watching for, quietly rewriting a failing test instead of fixing the implementation underneath it. That specific failure mode has mostly disappeared. Generating unit tests that pass local syntax and isolated runtime logic is now trivial for any competent model.

But trivial isn’t the same as sufficient. Unit tests verify isolated, local correctness. They have never been designed to catch architectural drift, broken system integration, or a missed business requirement — and they still can’t. Treating unit test coverage as your primary verification pillar was already a stretch before AI. It’s a liability now, especially on a brownfield application where the danger isn’t a broken function, it’s a function that works perfectly and still violates every boundary your architecture depends on.

The Manual Bottleneck is the other extreme, and it’s the more common one among teams doing this seriously. Structured frameworks like AI-DLC are a genuine step up from unstructured vibe coding — they replace ad-hoc prompting with rigorous context and incremental specification. But that structure has a side effect: it lets models generate entire features and multi-file diffs in minutes, which inadvertently creates a far larger surface area to verify. Senior engineers end up spending eighty percent of their time on exhaustive, line-by-line audits of diffs that would have taken a junior engineer a week to write by hand. The less disciplined version of this trap is worse — some builders simply stop reading and ship whatever the agent produced.

Either way, you end up spending more time verifying and fixing code than you ever saved generating it. That’s the tax. And you don’t fix a tax by working harder inside the same system — you fix it by re-engineering the system.

The Case for Dual-Loop Automated Verification Link to heading

Smarter models don’t need more unit tests. They need automated integration and intent verification proportional to the size of the units of work they’re now taking on.

Here’s the mental model I use: traditional unit testing is like checking whether a single bolt fits a single nut on a factory floor. It’s necessary, but it tells you nothing about the engine block. Dual-loop verification is the automated laser scanner further down the assembly line — the one that continuously checks whether the fully assembled engine satisfies the actual structural specification, regardless of whether every individual bolt passed its own isolated check.

The two loops solve different problems at different points in time:

  • The inner loop restricts what an agent can do while it’s generating code — real-time execution bounding, enforced during the session.
  • The outer loop evaluates what the agent actually built after the pull request is opened — semantic verification, enforced against your architecture and your product intent, independently of the agent that wrote the code.

I built this out concretely against a travel booking engine service, part of a fuller AI-DLC demo I’ve published separately using Claude Code. The mechanics translate to whatever agentic tooling you’re running, but the specific primitives I’ll walk through — Skills and Hooks — are Claude Code’s implementation of the pattern.

The Inner Loop: Skills as Guidance, Hooks as Enforcement Link to heading

Two features do almost all the work in the inner loop, and they solve fundamentally different problems.

A Skill is model guidance. It’s loaded dynamically, only when the agent judges it relevant, and it teaches conventions, context, and preferred repository patterns. Skills shape how the model reasons about a task. They’re conditional by design — you don’t want every skill loaded into every context window, so the agent decides at inference time whether a given skill applies.

A Hook is deterministic system enforcement. It’s a hard shell that intercepts a tool call based on fixed rules, and it fires regardless of what the model decided to do. Hooks don’t ask the agent for permission. They run after the fact, on every matching action, every time.

That distinction — conditional guidance versus deterministic enforcement — is the whole design. I built a skill to encode security guardrails for cloud deployments and declared exactly when Claude should reach for it, so it doesn’t sit permanently in context. The interesting part is what happened when I gave Claude a completely different task: scale up the Fargate and Redis instances for the booking service by modifying the infrastructure design document and the Terraform templates. The task had nothing to do with security on the surface — it was about throughput. But because the hook is bound to the action (writing to infra design docs and Terraform files) rather than the task description, it fired anyway. It ran a deterministic validator script against the updated infra document and templates and surfaced a set of security violations Claude had introduced while optimizing for scale, forcing a fix before the change went further.

That’s the pattern worth copying: guardrails triggered by what gets touched, not by what the agent thinks it’s doing. Build that once, release it to the whole engineering organization, and you’ve shifted verification left without asking any individual engineer to remember a checklist.

The Outer Loop: Verifying Intent Before a Human Ever Reads the Diff Link to heading

Inner-loop hooks keep the agent in bounds during generation. But skills are loaded conditionally — if the specific skill or hook never triggers for a given task, nothing catches the drift. You still need a second, independent check before a human opens the pull request. That’s the outer loop’s job: semantic verification — evaluating whether what got built actually matches the original product and architectural intent.

Building this stage starts with abstracting your application knowledge into two resources, deliberately kept separate:

  • Product Context — user stories and acceptance specs, describing what the system is supposed to do and for whom.
  • Implementation Context — Architecture Decision Records and module boundary rules, describing how the system is supposed to be built.

If your team already runs a structured spec workflow — composable specifications with horizontal and vertical layers, or the incremental artefact model in AI-DLC — both of these already exist. You’re not creating new documentation for this stage. You’re pointing an evaluator at documentation you should already have.

That evaluator is the second piece: an isolated Evaluator Agent, wired into your CI pipeline through something like GitHub Actions. When the coding agent opens a pull request, the pipeline triggers the evaluator independently. It ingests the git diff, pulls in the Product and Implementation Context, and compares the implementation against both. If the coding agent introduced a direct database call inside a presentation layer, or silently dropped an edge case the PRD explicitly called out, the evaluator flags the violation and posts a detailed report directly on the PR thread — before a human reviewer looks at a single line.

Two design constraints make this actually work rather than becoming theater.

First, the evaluator has to run in a context completely separate from the coding agent. If the same session or the same conversational memory produced both the code and its review, you’ve built a rubber stamp with extra latency, not a verification stage. Independence is the whole point.

Second — and this is the constraint teams underestimate — garbage in means garbage evaluated. If your product spec is three vague bullet points in a Jira ticket, the evaluator has no baseline for truth. It will confidently approve a hallucinated interpretation of requirements you never actually wrote down, because there’s nothing rigorous enough to catch the gap. Outer-loop verification doesn’t remove the need for good specifications. It makes bad specifications immediately, visibly expensive in a way they weren’t before, because now they’re silently authorizing whatever gets built against them.

This is exactly the meta-validation pattern I described in The New Asymmetry — a separate model, primed with organizational context, catching what a human no longer has the bandwidth to catch line by line — applied specifically at the pull request gate instead of at each internal generation step. Human reviewers only step in once the outer-loop evaluator has already given a green light. Their job shifts from “did I catch everything” to “do I agree with what the evaluator already caught.”

What This Actually Costs You Link to heading

None of this is a free silver bullet, and it’s worth being precise about where the real costs sit.

The first cost is straightforward: latency and token spend. An LLM-based evaluator running against every pull request adds minutes to your pipeline and a real, recurring token bill. That’s a legitimate tradeoff against the cost of the manual bottleneck it replaces, but it doesn’t disappear — it moves.

The second cost is subtler, and I think about it as the Model-Coupling Trap. Your evaluation prompts and your guardrail rules are tuned against the specific reasoning behavior of the model you’re running today. Upgrade your foundation model — say, from one major version to the next — and the underlying reasoning shifts in ways that aren’t always visible until the evaluator starts passing things it shouldn’t, or flagging things that are actually fine. A model version bump that looks routine in your generation pipeline can silently break your verification pipeline. The only defense is treating your evaluation prompts and context files with the exact same version-control discipline and regression testing you’d apply to production application code — because functionally, that’s what they are.

The third cost is the meta-review problem, and it’s the one senior engineers feel first: if an AI writes the code and an AI reviews the code, who watches the watcher? The honest answer is that your senior engineers don’t stop reviewing — their job moves up a level. Instead of reading every diff, they run an Audit Loop: randomly sampling something like one in every ten pull requests specifically to audit the evaluator’s grading logic, not the code itself. The question isn’t “is this feature correct.” It’s “is our judge still calibrated.” That’s a genuinely different skill than code review, and it’s one most senior engineers haven’t had to build yet.

Where the Time Actually Goes Link to heading

I won’t pretend the transition is free. Building the first version of this system means more time in the IDE, more manual review, more friction — exactly while you’re trying to reduce friction. I’ve been through that phase more than once, and it’s tiring in a specific way that doesn’t show up on a velocity chart.

But it’s a front-loaded cost, not a permanent one. Once a central team governs and releases the guardrails — the skills, the hooks, the evaluator prompts, the context documents — those guardrails apply across the entire engineering organization, not just the team that built them. That’s the same shift I described in The Semantic Layer: the artefacts worth investing in are the ones that get reused by every future generation cycle, not the code any single cycle produces. Manual verification doesn’t disappear. It becomes the rare exception instead of the default mode.

Traditional unit tests prove local syntax; they were never built to verify integration or intent, and pretending otherwise is how the Unit Test Fallacy takes hold. Inner-loop hooks give you deterministic enforcement while skills guide the model’s reasoning — together they bound what gets built in the first place. And abstracting your application knowledge into Product and Implementation Context, then wiring it into an independent, CI-driven evaluator, is what finally eliminates the eighty-percent manual verification tax — provided you’re willing to write specifications rigorous enough to be worth evaluating against, and maintain the evaluation harness with the same rigor as the code it’s watching.

The bolt-and-nut check was never the problem. It’s the engine block that needed the scanner.

Enjoyed This Article? Subscribe for More

Get insights on AI-driven development, software engineering, and system design delivered to your inbox.

About the Author - Derick Chen

I'm a Senior AI Development Engineer at Google, leading large strategic AI deployments for key enterprise customers. Previously, I was a Developer Specialist Solutions Architect at AWS Singapore, where I led the AI-Driven Development Lifecycle (AI-DLC) programme across multiple key countries in ASEAN and the wider APJ region. As an early contributor to the AI-DLC methodology and its foundational white paper, I help engineering organizations build complex software faster and better, unlocking 10X delivery velocity through reimagined processes and team structures.

Earlier in my career, I worked at Meta on platform engineering solutions and at DBS Bank on full-stack development for business transformation initiatives. I graduated Magna Cum Laude from New York University with a BA in Computer Science.

Follow me on LinkedIn for more insights on AI-driven development and software engineering.

The views expressed in this article are my own and do not represent the views of my employer.