DEV Community

Cover image for The Coding Agent That Can't Grade Its Own Homework
Madhesh Vivekanandan
Madhesh Vivekanandan

Posted on AI-assisted

The Coding Agent That Can't Grade Its Own Homework

"All tests pass. ✅"

You've seen that message from a coding agent. And at least once, you've opened the file afterwards and found the tests were never run — or the test file was quietly edited until it agreed.

The problem isn't that the model writes bad code. It's that the same context window writes the code, reviews the code, and declares the code correct. One brain, grading its own homework — and it grades generously. That cycle — understand → change → check → repeat — is the agent loop, and every coding tool ships one that runs inside a single context window.

agent-loop is a small open-source project that restructures it: six explicit stages, file-based handoffs, an independent verifier, hard iteration caps. No framework, no daemon, no SDK — markdown plus one shell script, and it runs unchanged in Claude Code, Codex, Cursor, Gemini CLI, Copilot, and 30+ other tools.

The whole loop in 30 seconds

The agent loop: triage fans out into tiers S, M, and L — S goes straight to implement, M passes through analyze, L through analyze and plan. Everything converges on implement, then a verify gate: PASS reports done, FAIL enters a debug loop capped at 3 iterations, after which it escalates.

Each stage starts with a fresh context, reads only its declared inputs, and hands off through a markdown file in a gitignored .agent-loop/ directory:

.agent-loop/
  profile.md                    # cached project profile (commands, conventions)
  runs/2026-09-09-rate-limit/
    task.md
    analysis.md
    plan.md
    implementation.md
    test-report.md              # ends in PASS or FAIL — never "looks good"
Enter fullscreen mode Exit fullscreen mode

Those files aren't logs. They're the interface between stages — and an audit trail you can read afterwards to see exactly why the agent did what it did.

Not every task deserves a pipeline

Anthropic measured multi-agent systems at roughly 15× the tokens of a regular chat. For a typo fix, that's a five-course tasting menu because you wanted a glass of water.

So the first move is triage — one cheap classification, never delegated:

Tier The task What runs
S Diff fits in one sentence implement → verify
M Localized change analyze → implement → verify
L Multi-file or uncertain the full pipeline

One rule does more work than it looks like: torn between two tiers, pick the smaller one. Left alone, agents will write a design document for a null check — the bias has to be encoded.

Artifacts, not transcripts

Why fresh contexts? Because context poisoning is real. Exploring a codebase fills the window with dead ends — irrelevant files, abandoned hypotheses, stale test logs — and in one long session, all that noise quietly influences every later decision.

File handoff cuts the cord. The analyzer can burn its whole budget exploring, but only analysis.md survives into the next stage. What crosses the boundary is a document, not a transcript.

Left: one agent overwhelmed by a single long, tangled, scribbled-over transcript. Right: four agents cleanly handing small, focused documents from stage to stage.

One rule decides whether handoffs work at all:

The plan must carry rationale, not just conclusions. An implementer that gets only "use middleware, put it in upload.py" re-derives the reasoning, reaches a different answer, and contradicts the plan while believing it follows it. (Cognition's "Don't Build Multi-Agents" circles the same failure.)

The templates enforce it structurally: plan.md has required sections, and a plan without a named runnable check — the command that will decide pass or fail — is returned once, then the loop stops. No check, no run.

The verifier never sees the reasoning

This is the part of the design I'd defend in a fight. Verification starts in a completely fresh context, and its inputs are a closed list:

The verifier gets It never gets
The plan The implementer's summary
The diff The implementer's reasoning
A shell, to run the check implementation.md

Why so strict? An agent that reads "here's why my implementation is correct…" doesn't verify that claim — it confirms it. Sycophancy isn't a personality flaw you can prompt away; the fix is structural. The verifier learns what the code was supposed to do, sees what actually changed, runs the check named in the plan, and writes PASS or FAIL with command output as evidence. "Looks good to me" is not a verdict.

And my favorite detail in the whole project: you might think spawn the verifier without write tools and it can't cheat. But it must have a shell to run the tests — and a shell can edit files (sed, echo >, git apply…). So the real guarantee is a diff fingerprint: the working tree is fingerprinted before and after verification, and if it moved, the verdict is void. Tool restriction is defence in depth; detection is the guarantee — on every host, including those that can't restrict tools at all.

(Reviewers fail in the other direction too, so the contract counts only correctness-affecting gaps — no invented nitpicks.)

Three strikes, then a human

On FAIL, a debugger spins up — fresh context again, so failed attempts don't pile up as noise that poisons the next one. It must reproduce the failure before fixing it, and if it concludes the plan is wrong, that's an escalation, not a code change.

The debug loop caps at 3 iterations — with oscillation detection, and a full re-verification after every fix. On cap, you get a distilled summary of what was tried and what the evidence says, not a 400-line transcript.

An agent loop without a termination condition isn't autonomous. It's just unsupervised.

One folder, thirty-plus tools

The loop is a single Agent Skills folder — an open standard 30+ coding agents read — and the stage contracts name no vendor, tool, or product. Hosts differ wildly in what they offer, so every capability declares an explicit fallback:

Capability With it Without it
Subagents Mode A — one isolated context per stage Mode B — sequential stages, still reading only declared inputs
Tool restriction Verifier spawned without write tools The diff fingerprint alone — the real guarantee anyway
Turn caps Debugger bounded mechanically Iteration counting; the 3-cap holds either way
Per-stage models Strong models on plan/verify/debug One model throughout — costs more, quality holds

Every run announces which mode it got. One requirement is non-negotiable: shell execution. Verification that can't run real commands gives the loop no termination condition — so it refuses to run rather than emit confident, unverified output. That refusal is the product.

And because everything is markdown plus one POSIX installer — no daemon, no database — there's nothing racing the features agent hosts ship natively every month, and the skill costs zero context until you explicitly invoke it.

So what does this actually improve?

Each failure you've met on real work maps to a specific structural counter — that mapping is the design:

Failure you've seen What the loop does about it
Bad early output contaminates everything after it Fresh context per stage; artifacts cross the boundary, transcripts don't
"All tests pass" that nobody ran Independent verifier, diff fingerprint, evidence required in the verdict
The agent grinds on a fix forever Cap of 3, oscillation detection, escalation with a failure summary
It rebuilds a helper that already exists The analyzer's mandatory Found / Exemplars / Missing / Reuse-plan gate
A design doc for a one-line fix Triage tiers, biased toward the smaller tier
The implementer "follows the plan" into a different design Plans carry decisions and rationale, enforced by template sections

None of this makes the model smarter. It makes the process resistant to the specific ways coding agents fail.

Steal this, then run it

git clone https://github.com/Madheshvivekanandan/agent-loop && cd agent-loop
./install.sh claude-code     # or: codex · cursor · agents
# Claude Code, zero-install alternative:  claude --plugin-dir path/to/agent-loop
Enter fullscreen mode Exit fullscreen mode

Then, in any project:

/agent-loop add rate limiting to the upload endpoint
Enter fullscreen mode Exit fullscreen mode

First run profiles your project — it discovers your test/build/lint commands and executes them once before trusting them. Every run after that states its tier and mode up front, works the stages, and returns a verdict, a diff summary, and a folder of evidence you can actually read.

If you take nothing else from this post, take the two rules that travel to any agent workflow:

  1. Never let the agent that wrote the code declare it correct. The separation must be structural — fresh context, plan + diff only — not a prompt asking it to "be critical."
  2. Every loop needs a number where it stops and calls a human. Caps aren't a lack of faith in the model; they're what makes it safe to stop watching.

The repo is github.com/Madheshvivekanandan/agent-loop — MIT-licensed, one folder, readable in an evening. If you run it on a host I haven't tested, or find a hole in the verifier's guarantees, I genuinely want the issue.


Previously: The Dashboard That Builds Itself, on generative UI and letting a model order from a menu without getting into the kitchen.

All images in this post were generated with ChatGPT.

Top comments (0)