We Put Claude Code Inside a Deterministic Verify → Solve Loop. It Caught Its Own Bug





We Put Claude Code Inside a Deterministic Verify → Solve Loop. It Caught Its Own Bug.


AI coding agents can generate code remarkably quickly.

The problem is that generation speed is now growing faster than our ability to verify what was generated.

That changes the software-development problem.

Can we put an AI agent inside a deterministic engineering system that tells it when it is wrong—and lets it correct itself?

That’s the idea behind Sonar’s Agent Centric Development Cycle (AC/DC): guide the agent with context, verify its output continuously, and solve the problems discovered by verification.

We didn’t want to take the idea on faith. So we built the smallest practical version we could on a real Java codebase.

The loop caught a bug in the AI-generated code that we hadn’t deliberately planted.
Then we discovered something even more important: our verification harness had a bug of its own.

The experiment

We built a small proof of concept around Spring Petclinic.

Repository: github.com/DevelopersCoffee/acdc-petclinic-poc

Every significant stage is represented by a real, readable commit.

Technical write-up: AC/DC Agentic Verify → Solve POC

GUIDE
Context · Architecture · Constraints
GENERATE
Claude Code implements the feature
VERIFY
Maven · Tests · ArchUnit · SonarQube
SOLVE
Findings go back to the agent
VERIFY AGAIN
Repeat until the quality gate passes

The important part is that Verify isn’t another prompt. It is a deterministic execution step.

The feature we gave the agent

We asked Claude Code to implement:

GET /api/orders?customerId={id}
  • pagination
  • input validation
  • service layer for business logic
  • repository layer for persistence
  • tests required
  • existing endpoints must not be modified
  • no unnecessary dependencies

Guide: Turn engineering expectations into constraints

We put the engineering expectations into CLAUDE.md.

The desired architecture was:

Controller
Service
Repository

We also specified input validation, safe SQL practices, correct HTTP status codes, tests, scope boundaries and dependency constraints.

A markdown file is guidance. It isn’t enforcement.

Making architecture executable with ArchUnit

We added an ArchUnit rule that enforces the dependency boundary.

classes()
    .that().resideInAPackage("..order..")
    .and().areAnnotatedWith(RestController.class)
    .should().notDependOnClassesThat()
    .resideInAPackage("..repository..");

If an architectural rule matters, make it executable.

One complication: legacy architecture

Upstream Spring Petclinic does not consistently use the Controller → Service → Repository structure we wanted for the new feature. Applying the rule repo-wide would fail on existing code.

So we scoped the rule to the new order package.

Existing code: existing architectural debt remains.

New order feature: must satisfy the new architecture rule.

This lets teams freeze legacy debt while enforcing a better standard for new code.

Before the loop: What did Claude generate?

We first generated the feature without relying on the verification loop so we could see what the loop had to catch.

1. The controller bypassed the service

Controller
    ↓
Repository

That violated our architecture rule.

2. It created an unnecessary endpoint

/api/orders/raw-lookup

The endpoint built SQL using string concatenation from request input. More importantly, the endpoint was never requested.

Our preferred fix was deletion, not patching an unnecessary feature.

3. Invalid input returned 200

Missing customerId returned an empty response instead of:

400 Bad Request

4. Test coverage was incomplete

The implementation lacked the controller and service coverage required by the task.

The first Verify run caught something we didn’t plant

We expected ArchUnit to fail. It did eventually.

But the first failure was a compilation error.

Claude generated a plausible-looking Spring Boot import that was incorrect for the version used by the project.

A deterministic compiler rejected a plausible AI-generated mistake before it reached human review.
Claude generates code
Maven compilation
FAIL
Incorrect import
Finding returned to Claude → import fixed

The second Verify run: architecture fires

After fixing the compilation issue, ArchUnit reported:

OrderController
    ↓
OrderRepository

Direct repository dependency is not allowed.

The important point: this wasn’t another instruction to Claude. It was a build-enforced constraint.

Solve: fix the root cause, not the symptom

Claude extracted OrderService:

OrderController
       ↓
OrderService
       ↓
OrderRepository

We didn’t suppress or weaken the ArchUnit rule. We fixed the underlying design.

The unnecessary endpoint disappeared

The /api/orders/raw-lookup endpoint was deleted.

Unrequested feature → DELETE

AI agents can implement things you didn’t ask for. Verification therefore needs to consider scope, not only correctness.

Validation and tests were added

Missing or invalid customerId now produces 400 Bad Request, with service and controller tests covering expected behavior and edge cases.

Then the verification system itself failed

On the third Verify run:

Build: PASS
Sonar scan: PASS
Quality gate: FAIL

But the quality gate reported zero relevant findings.

We investigated the verification harness instead of immediately changing application code.

Our findings collector was wrong

Our custom findings-collection script queried the SonarQube issues API using a parameter whose behavior had changed in SonarQube 26.x. It was no longer correctly scoping results to new code.

The agent could have been sent to solve pre-existing legacy findings that had nothing to do with the current task.

A broken verification loop doesn’t make AI safer. It makes AI confidently optimize against the wrong objective.

Fix the verifier before fixing the application

We stopped the application loop, fixed the findings collector, and ran verification again.

The Verify stage must itself be tested and observable.

A red/green result isn’t enough. We need to know what was checked, which findings belong to the current task, which are pre-existing, and why the gate passed or failed.

The real failure was new-code coverage

Once the harness was fixed, the legitimate remaining problems were new-code coverage below the gate’s 80% threshold plus a couple of code smells.

Claude fixed them and we ran the complete verification sequence again.

Final result

  • 55/55 tests passing
  • 46 baseline tests
  • 9 new tests
  • 0 new quality violations
  • 100% new-code coverage
  • 0% new-code duplication

The complete loop

GUIDE
CLAUDE.md · architecture · coding rules
GENERATE
Claude Code
VERIFY
Compile · tests · ArchUnit · SonarQube
SOLVE
Agent fixes root causes
VERIFY AGAIN
Repeat until green

What surprised us

We expected deterministic verification to catch AI-generated mistakes. It did.

The surprising part was that the verification layer itself became part of the system under test.

Category What happened What caught it
AI implementation error Plausible but incorrect Spring import Maven compilation
AI architecture error Controller bypassed service ArchUnit
AI scope error Unrequested raw-lookup endpoint Task constraints / review
Verification infrastructure error Legacy Sonar findings incorrectly included Harness investigation

Every layer of the verification system is capable of being wrong.

Why deterministic verification matters

LLMs are probabilistic. A model can produce plausible but incorrect code, violate architecture, or satisfy a prompt while breaking an unstated constraint.

A prompt says:

Don’t do X.

A deterministic test says:

If X happens, the build fails.

Those are fundamentally different mechanisms.

Markdown rules are not enough

CLAUDE.md
    +
ArchUnit
    +
Tests
    +
Static analysis
    +
Quality gate

The agent receives context and consequences.

The verification loop changes the role of the developer

This doesn’t mean developers disappear. It changes where engineering effort goes.

  • architecture
  • quality standards
  • security constraints
  • test requirements
  • acceptance criteria
  • quality gates
  • boundaries around autonomous changes

The developer becomes responsible for designing the system that determines what “done” means.

But this experiment does NOT prove that AI is now safe

There are important limitations.

  • one repository
  • one feature
  • one coding agent
  • one controlled experiment
  • one verification workflow

We did not run a randomized before/after study or establish a statistically meaningful defect-rate reduction.

We did not prove that every coding task can be safely handled without human review.

We did not prove that a quality gate catches every functional or architectural defect.

A deterministic Verify → Solve loop can catch plausible AI-generated mistakes before they reach a human review stage, while executable architecture rules can turn subjective expectations into enforceable constraints.

The most important lesson: don’t blindly trust the verifier

The most memorable failure wasn’t Claude’s. It was ours.

The verifier returned a result that looked authoritative but was based on incorrectly scoped data.

Automation doesn’t eliminate trust. It moves the trust boundary.

If a human reviewer is replaced by an automated quality gate, the quality gate becomes infrastructure that needs its own tests, monitoring and validation.

The important question becomes:

What exactly did the verifier verify?

Where this gets interesting

Read architecture and coding standards
Implement feature
Compile · tests · architecture · security · quality
Fix failures
Repeat
Open PR → Human review

The agent doesn’t get to redefine the acceptance criteria while it is working. The acceptance criteria live outside the model.

Try the experiment yourself

Repository:
github.com/DevelopersCoffee/acdc-petclinic-poc

git clone git@github.com:DevelopersCoffee/acdc-petclinic-poc.git
cd acdc-petclinic-poc
docker compose -f docker-compose.sonar.yml up -d
./verify.sh

The commit history is intentionally part of the experiment. Inspect the initial implementation, compilation failure, architecture violation, fixes, broken findings collector, harness correction and final passing state.

Final takeaway

The interesting story isn’t:

“Claude Code wrote a Spring Boot feature.”

Agents can already do that.

The interesting story is:

“We gave the agent a deterministic definition of correct, let it fail against that definition, fed the findings back into the agent, and repeated until the gate passed.”

The verifier can be wrong too.
Human defines constraints
          ↓
       AI agent
          ↓
Deterministic verification
          ↓
       Findings
          ↓
       AI solves
          ↓
Deterministic verification
          ↓
        Repeat
          ↓
     Quality gate
          ↓
     Human review

The goal isn’t to remove engineering judgment. It’s to make the machine do more of the repetitive generate → verify → fix → verify work while keeping the engineering constraints outside the model.

If AI can generate code faster than humans can review it, the bottleneck moves from generation to verification.

References and further reading

About this experiment

This article documents a DevelopersCoffee proof of concept inspired by Sonar’s AC/DC concept. It is not an endorsement or reproduction of Sonar’s proprietary implementation.


Leave a Reply

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