What "loop engineering" means in agentic coding
A single LLM call is a function: prompt in, text out. It cannot recover from its own mistakes because it never sees them. An agent is what you get when you wrap that call in a while-loop with tools and feedback:
Those four lines hide all the engineering. Loop engineering is the deliberate design of each line:
- gather( ) — context engineering: what enters the window, what gets summarized, what gets retrieved on demand instead of preloaded.
- model( ) — the only part you mostly don't control. Prompts and tool definitions are your steering inputs.
- execute( ) — tool design: legible outputs, loud failures, sandboxed side effects.
- verify( ) + stop — the part most people under-build, and the part this guide is mostly about. An agent without a real verifier isn't iterating; it's free-associating with side effects.
An agent's intelligence is bounded by its model, but its reliability is bounded by its loop — specifically by the speed, coverage, and honesty of the feedback the loop returns to the model.
Anatomy of the agent loop
Here is the loop you actually ship, including the two places where the real world injects trouble: hallucination entering at the model (a confident wrong claim, an API that doesn't exist) and environment noise entering at the verifier (flaky tests, dirty workspace, rate-limited services).
The vocabulary, in plain words
| Term | Plain meaning | Example |
|---|---|---|
| Agent loop / harness | The while-loop and plumbing around the model | Claude Code's gather→act→verify cycle |
| Turn / iteration | One trip around the loop | One edit + one test run |
| Trajectory | The full sequence of turns for a task | Everything from prompt to merged diff |
| Verifier | Anything that can say "good" or "not yet" without trusting the model | pytest, tsc, clang, a screenshot diff |
| Grounding | Forcing claims to come from the environment, not memory | "Read the file before editing it" |
| Context rot | Quality decay as the window fills with stale noise | Old stack traces drowning the current one |
| Compaction | Summarizing history to reclaim the window | Replace 60k tokens of logs with a 1k summary |
| Stop condition | An explicit, checkable definition of done | "All tests green AND lint clean AND diff < 300 lines" |
You've seen this loop before
If you come from control engineering, none of this is new — only the names changed. An agentic coding loop is a feedback control system where the controller happens to be a language model and the plant happens to be a codebase:
| Control systems | Agentic coding | Same failure mode |
|---|---|---|
| Setpoint r | Task spec / acceptance criteria | Vague setpoint → loop converges to the wrong place |
| Controller C(s) | The model + system prompt | Over-aggressive gains → thrashing rewrites |
| Actuator (with limits) | Tools (with permissions/sandbox) | Saturation → agent blocked, keeps "winding up" plans |
| Plant P(s) | Repo + build + runtime | Unmodeled dynamics → "it worked on the agent's branch" |
| Sensor H(s) | Verifiers: tests, types, lint, screenshots | Poor sensing → confidently shipping broken code |
| Sensor noise n | Flaky tests, nondeterministic env | Loop chases ghosts, oscillates between two "fixes" |
| Disturbance d | Merge conflicts, dep upgrades, prod drift | Yesterday's green is today's red |
| Bandwidth ωc | Iteration speed (test latency!) | Slow feedback → few iterations per budget → poor convergence |
| Phase margin | Headroom before thrash: small diffs, one change per turn | Big multi-file edits per turn ≈ delay in the loop → instability |
| Anti-windup | Stop conditions, iteration caps, "ask the human" escape | Without it: 400 turns of escalating heroics |
Control engineers learn to distrust the controller and invest in the sensor. The same instinct wins here: a mid model with excellent verifiers beats a frontier model flying blind. Lab A makes that quantitative.
Lab A — The verifier is your loop gain
A Monte-Carlo toy model of one edit-verify loop, 4,000 simulated runs per slider move. Each iteration the agent attempts a fix that truly works with probability p (model capability). The verifier catches a broken attempt with probability v (test coverage / verifier strength). Context rot decays p a little every iteration. Three ways a run can end:
- True success — fix works and the verifier confirms it.
- False success — fix is broken but slipped past the verifier. The worst outcome: it looks identical to success until production.
- Budget exhausted — the stop gate fired. Annoying, but honest.
Move the sliders. Watch where the risk goes.
What the model teaches
- Weak verifiers don't make agents fail — they make agents lie. Drop v from 0.95 to 0.45 with a strong model: the loop "finishes faster," and a huge red mass of broken-but-accepted code appears. Iterations went down; risk went up.
- Verifier strength converts failure into retries. With v ≈ 1, the only bad outcome left is "exhausted," which is recoverable: raise the budget or improve context.
- Context rot punishes long loops. At high rot, late iterations are nearly worthless — which is the quantitative argument for compaction, fresh-start retries, and splitting work into subtasks rather than raising N.
- Flaky tests tax the honest path. Toggle flakiness: true successes get rejected, runs lengthen, exhaustion grows. Determinism in the verifier is a performance feature, not hygiene.
- A writable verifier is no verifier. Enable "agent may edit tests": every caught failure now has a 20% chance of becoming a green-but-broken ship, because the optimizer was allowed to optimize the metric itself. Watch true success barely move while false success explodes — Goodhart's law, rendered as a bar chart.
Feedback quality — making the loop see
The model only knows what the loop shows it. Feedback has three qualities, and you should engineer each deliberately:
- Coverage — how much of "wrong" the verifier can detect. Layer cheap, broad signals first: compiler/type-checker (free, instant, near-total coverage of one error class) → linter → unit tests → integration tests → visual diff/screenshot → human rubric. Each layer catches what the previous one can't see.
- Latency — iterations per budget is your loop bandwidth. A 90-second test suite means an agent gets ~6 meaningful turns in 10 minutes; a 3-second targeted run gives it ~60. Give the agent a way to run just the relevant tests and you've multiplied its effective intelligence by 10.
- Legibility — error output is a prompt.
AssertionErroralone is a terrible prompt; "expected 3.14 got 2.71 at resolver.py:88 in wrap_angle()" is a great one. Truncate the irrelevant, keep the failing assertion, the locals, and the file:line. Tools that fail should fail loudly, specifically, and with a suggestion.
Worse than a noisy failure is a quiet lie: a tool that swallows an exception and returns "ok", a test suite that skips on missing deps and reports green, a mock that always matches. Every silent success teaches the model a false world-model that it will confidently build on for the next twenty turns.
Before adding a smarter prompt, ask: "could the agent detect by itself that this output is wrong?" If not, you don't have a prompting problem — you have a sensing problem. Add a verifier (a test, an assertion script, a screenshot check), then watch the same prompt start working.
Lab B — Anatomy of a real run
Below is a replay of a realistic agent trajectory: fixing two failing tests in a resolver angle-decoding module. Press play and watch four things at once: the event log (what the model and tools do), the context meter (the window filling with tool results until compaction reclaims it), the iteration counter (each verify-fail closes one loop), and the moment the stop gate finally opens.
One bug, three iterations, one compaction
What to notice in the replay
- Reading before writing. The agent's first three actions gather ground truth — failing output, the test file, the source. Grounding is cheap insurance against hallucinated APIs.
- One hypothesis per iteration. Each loop changes one thing and re-verifies. When iteration 1 only half-works (2 failures → 1), that partial signal is exactly what steers iteration 2.
- The context meter mostly fills with tool results, not reasoning. Raw pytest output and file dumps dominate. That's why compaction summarizes results, and why tools should return the minimum that's still legible.
- The run ends because a gate said so — tests green AND lint clean — not because the model felt finished. "Feels done" is not a stop condition.
A failure-mode bestiary (DTCs for agents)
Vehicle diagnostics has trouble codes; agent loops deserve the same. Every fault below is observable from the trajectory log alone — which is the point. You debug a loop by reading its trajectory, the way you debug a drive by reading its signals, not by re-prompting harder and hoping.
| Code | Failure & symptom | Root cause | Fix |
|---|---|---|---|
| L-01 | Thrashing. The diff alternates A→B→A→B across turns. | Two constraints conflict, and the feedback doesn't disambiguate which one to satisfy. | Fresh context with a sharpened spec; attack one failing test at a time; surface both constraints to a human. |
| L-02 | Doom loop. The same failing command reruns verbatim, turn after turn. | The error text is illegible, truncated, or never reaches the context. | Legible errors (file:line + actual assertion); auto-escalate after k identical failures. |
| L-03 | Silent success. Agent declares victory; production disagrees. | Weak or missing verifier — swallowed exceptions, always-matching mocks, skipped tests reporting green. | Layered verifiers; fail-loud tools; "ok" must be earned, never a default return value. |
| L-04 | Reward hacking. Tests pass because the tests changed. | The verifier is writable by the optimizer it's supposed to constrain. | Read-only spec/test files; diff-check that assertions weren't weakened; review test diffs separately. |
| L-05 | Context poisoning. An early hallucinated "fact" (a config flag, an API) steers every later turn. | An ungrounded claim entered the window and was never falsified by the environment. | Read-before-write discipline; when the log shows confident nonsense, restart fresh rather than argue with it. |
| L-06 | Lost in the middle. A constraint stated early is ignored late. | The constraint is buried under 80k tokens of tool output. | Restate acceptance criteria inside the stop gate; keep them in a memory file, not in chat history. |
| L-07 | Plan windup. The TODO list grows while nothing lands. | A blocked actuator — missing permission, broken environment — with no escalation path, so the planner keeps "integrating." | Iteration caps plus an explicit blocked→escalate rule; fix the environment before resuming. |
| L-08 | Scope creep. "Fix the typo" becomes a 40-file refactor. | No diff budget; vague task framing invites ambition. | Diff-size and file-count caps; an allowlist of touchable paths in the task spec. |
| L-09 | Zombie environment. Green in the agent's sandbox, red everywhere else. | Stale dependencies or leftover state; a non-hermetic workspace. | Disposable, reproducible workspaces; the stop gate verifies from a clean checkout. |
| L-10 | Judge collusion. An LLM evaluator approves anything fluent and plausible. | Grader and generator share the same blind spots and the same taste for plausibility. | Prefer execution-based judgment (run it) over opinion-based; spot-check graders against humans. |
Control engineers will recognize most of these on sight: L-01 is a limit cycle, L-02 is a stuck sensor reading, L-03 is a sensor fault accepted as truth, L-04 is the plant rewiring its own feedback path, and L-07 is integrator windup wearing a project-management costume. Sixty years of fault diagnosis transfers almost verbatim.
Loops inside loops
Mature agentic systems are cascades, exactly like servo drives: a fast inner loop wrapped by slower outer loops, each with its own verifier and its own stop condition.
- The separation rule transfers intact: each inner loop should be ~an order of magnitude faster than the loop around it. If your "inner" test cycle takes as long as a human review cycle, the cascade collapses into one slow, unsteerable loop.
- Inner loops protect outer ones. The inner loop's caps (iterations, diff size, no force-push) mean the outer loop can delegate aggressively without existential risk — the same reason current limits live in a drive's innermost loop.
- Escalation is the cascade's anti-windup. When an inner loop hits its cap, the right move is up, not harder: report what was tried, surface the blocker, let the middle loop re-plan or the human re-spec. Agents that never escalate are agents that thrash.
Loop topologies & workflow patterns
Anthropic's "Building Effective Agents" catalogues the standard topologies. They are all arrangements of the same gather→act→verify cell:
| Pattern | Shape | Use when |
|---|---|---|
| Prompt chaining | Fixed sequence of calls, gates between steps | The decomposition is known in advance (spec → code → docs) |
| Routing | Classifier picks one of several specialized loops | Inputs cluster into types (bug fix vs. refactor vs. question) |
| Parallelization | Same/different loops run concurrently, results merged or voted | Independent files; or N attempts + a judge picks best |
| Orchestrator–workers | A planner loop spawns worker loops dynamically | Decomposition unknown until the repo is explored |
| Evaluator–optimizer | Generator loop + critic loop alternating | You can state quality criteria but not generate perfectly (writing, design, perf tuning) |
| Autonomous agent | Open-ended loop, tools + verifiers, budget gate | Long tasks with strong verifiers (the SWE-bench setting) |
Reach for the simplest topology that can verify its own output. A single well-instrumented loop with great tests routinely beats an elaborate multi-agent system with weak ones — added agents add coordination surface, and every coordination surface is a place feedback can silently rot.
Lab C — Best-of-N and the judge problem
The parallelization pattern promises: sample N independent attempts, have a judge pick the best, ship it. It works exactly as well as the judge. The model below makes that precise: each of N candidates is truly correct with probability p; the judge approves a correct candidate with probability j and wrongly approves a broken one with probability 1−j; one randomly chosen approved candidate ships; if nothing is approved, the run escalates honestly.
Fan out, then pick — but who picks?
What the model teaches
- With a perfect judge, fan-out is pure win. Broken shipments stay at zero and correctness climbs toward 1 − (1−p)^N. You pay only compute — which is why "generate N, run the tests on each, ship the green one" is such a strong pattern.
- With a weak judge, fan-out manufactures plausible wrong answers. Drop j to 0.55 and raise N: the red curve climbs nearly as fast as the green one. You bought more lottery tickets for a judge who can't read the numbers.
- The judge is just another verifier. Everything from Lab A applies: execution beats opinion, determinism beats vibes, and an LLM grading LLM output inherits the generator's blind spots (bestiary L-10).
- Returns diminish fast. Most of the achievable gain arrives by N ≈ 5–8; beyond that you're spending linearly for asymptotic crumbs. Spend the surplus on a better judge instead.
Security — the loop as attack surface
Everything that enters the context window can steer the model, and an agent's tools pour the outside world into that window all day: web pages, issue comments, commit messages, log lines, the README of a dependency. Prompt injection is what happens when that content contains instructions — and the model, which cannot reliably tell data from directives, follows them. The attacker never touches your prompt; they publish a web page and wait for your agent to read it.
An agent becomes a data-theft machine the moment one loop combines all three of: (1) access to private data, (2) exposure to untrusted content, and (3) a channel that can send data out (HTTP, email, commits to public repos). Break any one leg and the exfiltration attack collapses. Audit every loop you run against this checklist — including the MCP servers it mounts.
Engineering the loop to fail safe
- Least privilege per task. Mount only the tools this task needs. A bug-fix loop needs read/edit/test — it does not need email, cloud credentials, or unrestricted HTTP.
- Egress allowlists. Network access limited to known package registries and APIs turns most exfiltration payloads into harmless text. (The sandbox this page was built in works exactly this way.)
- Sandbox execution, no ambient secrets. Run in a container without production credentials in the environment; secrets the loop can't see are secrets it can't leak.
- Gate by reversibility, not confidence. Local edits: autonomous. Pushes, deletions, migrations, outbound messages: human approval. The model's confidence is not an input to this decision — injected models are confident too.
- Quarantine untrusted content. The dual-loop pattern: a sandboxed reader loop with no privileges summarizes the untrusted page/issue; the privileged loop only ever sees the summary, never the raw payload.
- Treat MCP servers as dependencies. They execute inside your trust boundary: pin versions, review what they mount, prefer audited registries — a malicious tool description is a prompt injection with a handshake.
- Keep forensics. Full trajectory logs of tool calls and arguments are your flight recorder; injection incidents are diagnosed from trajectories, like everything else in this guide.
- Eval with canaries. Plant injection payloads in your replay suite (a booby-trapped README, a malicious issue) and assert the loop ignores them. Security regressions are regressions; test them like any other.
Practical notes from the trenches
- Write the stop condition before the prompt. "Done =
pytest -qgreen +ruffclean + diff under N lines + no new deps." If you can't express done as a command exit code plus a checklist, the task isn't ready for an agent. - Cap everything: iterations, tokens, wall-clock, diff size, files touched. Caps are anti-windup; hitting one is information, not failure.
- Make the workspace disposable. Run in a branch, a worktree, or a container; commit checkpoints every green state. Cheap rollback is what makes bold iteration safe —
gitis the agent's undo button and your sleep aid. - Sandbox by default, gate the irreversible. Reads and local edits can be autonomous;
git push, migrations, deletions, anything touching prod goes through a human gate. Sort actions by reversibility, not by how confident the model sounds. - One concern per iteration. Encourage small diffs and a single hypothesis per turn. Giant multi-file edits are delay in the loop: by the time feedback arrives, you can't attribute the failure.
- Give the agent a fast lane. A
make check-fastthat runs the focused tests in seconds, with the full suite reserved for the stop gate. Loop bandwidth is test latency. - Treat tool output as prompt engineering. Trim noise, keep file:line, surface the actual assertion. Return errors the way you'd want a junior engineer to report them.
- Persist memory outside the window. A
CLAUDE.md/AGENTS.mdwith build commands, conventions, and known traps is the loop's long-term memory; a scratchpad / todo file survives compaction and keeps long runs on-plan. - Compact on results, restart on confusion. Summarize old tool outputs aggressively; but when a loop is visibly thrashing (same two edits alternating), a fresh context with a better-written task beats iteration 14 of a rotten one.
- Fight reward hacking at the verifier. Agents will sometimes "fix" a failing test by deleting or weakening it. Protect the spec: make test files read-only to the agent where possible, or diff-check that assertions weren't loosened. The metric you verify is the metric the loop will optimize — exactly Goodhart's law.
- Eval the loop, not just the model. Keep a small suite of past real tasks and replay them when you change prompts, tools, or models. Loop changes regress silently; trajectories don't lie.
- Log trajectories. r, y, u, e for agents = task, diff, tool calls, verifier output. Most "the agent is dumb" reports dissolve when you read the trajectory and find a tool that lied or a test that flaked.
A loop engineering checklist
References & further reading
- Anthropic, Building Effective Agents, Dec 2024 — the workflow-pattern taxonomy of §08 and the "start simple" doctrine. anthropic.com/research/building-effective-agents
- Anthropic, Claude Code: Best Practices for Agentic Coding, 2025 — CLAUDE.md memory files, targeted test commands, plan-then-execute, git checkpointing. anthropic.com/engineering
- Anthropic, Writing Effective Tools for AI Agents and Effective Context Engineering for AI Agents, 2025 — tool legibility, token-efficient results, compaction, and just-in-time context retrieval. anthropic.com/engineering
- S. Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models," ICLR 2023 — the canonical interleaved think/act/observe loop. arXiv:2210.03629
- N. Shinn et al., "Reflexion: Language Agents with Verbal Reinforcement Learning," NeurIPS 2023 — feeding verbal self-critique back as loop feedback. arXiv:2303.11366
- C. Jimenez et al., "SWE-bench: Can Language Models Resolve Real-World GitHub Issues?" ICLR 2024 — the benchmark that made test-verified agent loops the standard of evidence. arXiv:2310.06770
- J. Yang et al., "SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering," NeurIPS 2024 — evidence that interface/tool design (the loop, not the model) drives large performance gains. arXiv:2405.15793
- Model Context Protocol specification — the standard plumbing for the tool layer of Fig. 1. modelcontextprotocol.io
- B. Brown et al., "Large Language Monkeys: Scaling Inference Compute with Repeated Sampling," 2024 — the empirical case behind Lab C: coverage grows with samples, but only verifiable domains can cash it in. arXiv:2407.21787
- K. Greshake et al., "Not What You've Signed Up For: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection," 2023 — the paper that named the attack of §11. arXiv:2302.12173
- S. Willison, "The Lethal Trifecta for AI Agents," 2025 — the three-legged exfiltration checklist. simonwillison.net
- OWASP, Top 10 for LLM Applications, 2025 — LLM01 (prompt injection) and friends, as a reviewable checklist for agent deployments. genai.owasp.org
- K. J. Åström & R. M. Murray, Feedback Systems, 2nd ed., Princeton, 2021 — for the §03 mapping; sixty years of loop theory your agent harness quietly re-derives. fbswiki.org