Loop Engineering — a deep technical explanation with realistic examples Loop engineering is a deterministic control loop around an AI coding agent — schedule, durable state, and verification — that lets developers move up from prompting to designing the machine. Boris Cherny, Head of Claude Code at Anthropic, said: "I don't prompt Claude anymore. I have loops running that prompt Claude and figure out what to do. My job is to write loops." The key principle is that a loop is only as trustworthy as the parts of its verification the agent cannot fake. The idea is often credited to Boris Cherny Head of Claude Code at Anthropic , who put it bluntly: "I don't prompt Claude anymore. I have loops running that prompt Claude and figure out what to do. My job is to write loops." This document goes past the slogan into the mechanisms: the control-theory framing, the exact algorithms that make a loop safe, the concurrency internals, the cost math, and the epistemics of why an agent cannot be trusted to check itself. Loop engineering is building a deterministic control loop around an AI coding agent — schedule + durable state + verification — so the agent drives the moment-to-moment cycle and you move up to designing the machine. The single load-bearing principle: A loop is only as trustworthy as the parts of its verification the agent cannot fake. "Am I stuck?" and "is this correct?" must be decided by deterministic code or a genuinely independent checker —neverthe agent's own self-report. Everything below is a consequence of that sentence. A control system regulates a process toward a setpoint using feedback: setpoint goal │ ▼ ┌──────────────┐ command ┌──────────────┐ │ controller │ ──────────► │ process │ │ your loop │ │ agent + repo │ └──────────────┘ └──────────────┘ ▲ │ │ measurement │ └─────────── sensor ◄─────┘ verifier / tests / metrics Setpoint = the goal "open PRs stay green", "no failing CI on main" . Process = the agent acting on the repo. Sensor = the verifier / test suite / numeric check — how you measure the gap between reality and setpoint. Controller = the loop logic that turns the measured gap into the next command. The critical distinction from control theory: | Open-loop | Closed-loop | | |---|---|---| | Definition | Act, never measure the result | Act, measure, correct | | Agent analogy | "Prompt → hope" | "Prompt → verify → decide → repeat" | | Failure mode | Drifts arbitrarily far from goal | Bounded by sensor quality | A raw agent call is open-loop. You prompt, it acts, and nothing measures whether the result matched intent — you do, manually. Loop engineering closes the loop by making the sensor verification and the controller decide/escalate part of the automated system. The corollary that governs everything: a closed loop is only as good as its sensor. A control loop with a broken thermometer will happily drive the room to 1000°C while reporting "72°F, all good." That broken thermometer is exactly what an LLM asked to grade its own work is see §4 . Three nested scopes. The jump from harness → loop is defined by adding schedule + state + verification . Context engineering → what goes in ONE prompt "one move" Harness engineering → ONE agent's environment: tools, "one game" permissions, rules — a single session Loop engineering → harness + SCHEDULE + STATE + "the tournament" VERIFICATION, running over TIME - Miss the schedule → it's a one-off script open-loop, runs once . - Miss the state → the agent is amnesiac every run and re-tries failed things forever no integral term — no memory of accumulated error . - Miss the verification → the sensor is missing; the loop is "closed" only in appearance. Three properties of language models make the naive "just let it run" approach fail. The loop machinery exists to counter each one. - Statelessness across sessions. A model has no memory between separate runs. Whatever isn't written to durable storage is gone. → Requires external state §5A . - Uncalibrated self-assessment. A model's confidence is not a reliable estimate of its correctness. The same reasoning that produced a bug produces the sincere belief that the bug is correct. Confidence and correctness are drawn from correlated distributions, not independent ones. → Requires an independent sensor §4 . - Autoregressive momentum / no natural stop. Given "keep trying until CI is green," a model will keep generating attempts — including plausible-looking non-fixes — indefinitely, because "produce the next token" has no built-in notion of "this is hopeless, stop." → Requires an external circuit breaker §5B . Loop engineering is, precisely, the set of external mechanisms that supply the memory, the sensor, and the stop condition that the model lacks. This is the load-bearing wall. Everything else is plumbing. Let M be the maker produces a solution and V the verifier judges it . Verification adds information only if V's errors are independent of M's errors. If V = M same model, same context, same prompt lineage , then any mistake M is blind to, V is equally blind to. The verification is a rubber stamp — it multiplies confidence without adding evidence. This is why "run the tests once and mark it done" is not verification if the same session wrote the tests and the code: a weak test written to pass weak code confirms nothing. Loops lean on a hopeful asymmetry: checking a solution is often cheaper and more reliable than producing one it's easy to verify a returned sort is ordered; harder to write the sort . When this holds, an independent checker is a cheap, strong sensor. But the asymmetry inverts when the artifact under review is itself the distortion. The canonical case: Someone ran a hedge fund as a fully autonomous loop with an LLM verifier judging backtests. It failed because a strategy's failure mode isoverfitting— the strategy looks brilliant on historical data precisely because it was accidentally shaped to fit that exact history. A second LLM reading the backtestcannotcatch the curve-fit, because the backtest it's reading isalreadythe overfit artifact. The lie and the evidence-of-the-lie are the same document. The fix was to replace the LLM verifier with a numerical, non-overridable check that measures something the maker structurally cannot fabricate: | Test | Sharpe | Verdict | |---|---|---| | In-sample training data | +2.54 | looks like a winner | | Out-of-sample held out | −4.33 | collapses | | Numerical checker | — | REJECT — no trade | The +2.54 is exactly the number that would make a human — or an LLM verifier — ship it. The out-of-sample split is data the maker never touched , so it cannot have been fit to. That untouchable measurement is the entire product. The maker/checker split is worthless unless the checker measures something the maker cannot fake. In code, that's an independent re-derivation of correctness a real test run, a type check, a compile, an EXPLAIN ; in statistics, it's out-of-sample, cost-aware, multiple-testing-penalized numbers — never a second model's vibe. | Layer | Mechanism | What it makes unfakeable | |---|---|---| Am I stuck? | Deterministic circuit breaker §5B | The agent can't claim "progress" — the numbers decide | Is it correct? | Separate verifier sub-agent, default REJECT | The maker can't approve its own diff | Is it real? | Non-LLM checks: tests run, EXPLAIN, compile, OOS split | The maker can't fabricate the measurement | read STATE ─► TRIAGE ─► IMPLEMENT maker sub-agent, isolated worktree situation? worth │ ▲ doing? ▼ │ VERIFY checker sub-agent / numeric check │ │ persist STATE ◄─ gate/escalate ◄─┘ write down safe? or human? State is a STATE.md or DB row / ticket board answering exactly three questions — and deliberately nothing more, to avoid rot: - What are we working on now? - What did we try last time, and what was the outcome? - What is waiting on a human? In control terms this is the integral term : it accumulates the history of error so the controller can act on trends , not just the latest instant. Without it, the loop is memoryless and will re-attempt the same failed fix on every run. State rot is the dominant failure here: the file references merged PRs, closed tickets, or dead branches, and the loop acts on ghosts. Mitigations: a prune step every run, a Last run timestamp, validating IDs against the live API, and one state file per loop pattern no shared unstructured file . This is the most technically interesting component. It answers "is this agent stuck?" with pure code — no LLM in the decision path , so it's cheap to run every iteration and impossible for the agent to argue past. Data model: interface Attempt { iteration: number; action: string; // short description of what was tried outcome: 'success' | 'failure' | 'noop'; error?: string; // raw error / stack trace if failed tokensUsed?: number; } interface Ledger { goal: string; attempts: Attempt ; } // oldest → newest Step 1 — normalize errors to a stable signature. Raw errors differ every run even when they're "the same" failure: "Error at db.ts:42 connection refused 0x7ff8a2 " ← attempt 1 "Error at db.ts:88 connection refused 0x3aa10b " ← attempt 2 errorSignature strips the volatile parts so the same failure maps to the same key: ISO timestamps →