The Loop: How Coding Agents Actually Execute
By James Han
·Apr 2, 2026
·3 min read
The first agent I built could plan beautifully. It would read files, propose edits, even describe what tests to run. Then it would announce "Done!" and move on — leaving behind code that didn't compile.
The model thought it was done. The code disagreed.
The runtime controls the loop, not the model
The pattern that fixed this is deceptively simple: the model proposes actions, but the runtime decides when to stop. It's a state machine with three phases — context, action, verify — and the agent cycles through them until exit criteria are met.
while not done and iteration < MAX:
prompt = assemble_context(session)
response = model.generate(prompt, schema)
if response.type == "plan":
session.plan = response.plan
if response.type == "tool_calls":
for call in response.calls:
result = gateway.execute(call) // pattern 01
session.observations.append(result)
if response.type == "final":
verification = run_tests(session)
if verification.passed:
done = true
else:
session.observations.append(verification.failures)
// loop continues — model sees what failed
The critical detail: when the model says "final," the runtime doesn't trust it. It runs verification — tests, linting, diff review — and only marks the session complete if verification passes. If it fails, the failure observations go back into the prompt and the loop continues.
Flat beats fancy
I experimented with tree-based execution — branching plans, backtracking, parallel exploration paths. They were harder to debug, harder to trace, and didn't produce better results.
A flat, single-threaded loop with observations is more reliable. Each iteration: assemble context, ask the model, execute actions, verify. The model sees everything that happened before. Simple state, simple debugging.
The verify phase is non-negotiable
Skipping verification to save tokens is the most expensive optimization I've made. Without it, the model completes work based on its own assessment. Its assessment is often wrong — especially for code changes where a small edit can break a distant test.
The verification phase catches errors that compound. An unverified edit in iteration 2 creates confusion in iteration 5, which leads to a rollback in iteration 8. Verification in iteration 2 would have cost 200 tokens. The cascade cost 4,000.
The anti-pattern: model-controlled termination
When the model decides it's done, it optimizes for confidence, not correctness. It generates a plausible summary, marks the task complete, and moves on. The code might be broken. Tests might be failing. But the model said "Done!" so the session ended.
Runtime-controlled termination means the model can't lie its way out. Tests pass or they don't. The diff is scoped or it isn't. Exit criteria are checked by code, not by vibes.
The takeaway
The agent loop is the heartbeat of your system. Keep it flat. Keep it simple. And never let the model decide when it's done — that's the runtime's job.
I write about this when I have something worth saying.