4/5 - When Done Actually Means Done: Loop Engineering for Long-Running Agents
Most agents are built around a loop. The model receives context, proposes a response or tool call, the runtime executes the tool, the result returns to context, and the model gets another turn. The loop is short. Knowing when to run it, what counts as progress, and when to stop is not.
Loop engineering begins when a human no longer writes every next prompt. A schedule, event, queue, or goal starts the run. The system can execute many turns and perhaps several helpers before a person returns. That is useful, but it changes the core contract. “The model said it was finished” is an observation. It is not proof that the work is finished.
The mental model: a finite control system
A long-running loop needs a goal, a definition of success, an action budget, a progress signal, and a stop policy. It also needs durable state so a restart resumes from a known checkpoint rather than redoing unknown work. This is closer to a workflow controller than to a chat transcript.
Description: The return path is deliberately conditional. If a turn produces no state change, repeating it should require a reason and a decreasing budget. Otherwise the loop can pay repeatedly for the same call while reporting activity as progress.
Define completion before autonomy
A goal should be written as a state transition, not a mood. “Improve the service” is not directly verifiable. “The test suite passes, the error budget is unchanged, the deployment has the target digest, and the smoke check returns 200” is much closer. The completion predicate may combine deterministic checks and a human approval. Record the predicate version with the run so a future policy change does not rewrite history.
There are at least three different stops: success, safe pause, and failure. Success means the goal predicate passed. Pause means the system lacks authority, budget, or information and can resume safely. Failure means continuing automatically is unsafe or unlikely to help. A cancelled run should be distinct from both. These states make the operator UI meaningful and prevent a timeout from being reported as a successful completion.
Budgets are control surfaces
Set a maximum number of turns, maximum wall time, maximum input and output tokens, maximum tool calls, maximum spend, and maximum parallel work. Use one overall deadline and pass the remaining time to each child operation. A per-call timeout of five minutes does not make a run safe if the loop can execute one hundred calls.
Use a progress ledger. Each iteration should record the state hash, planned action, observed effect, verifier result, and cost. A no-progress detector can flag repeated state hashes, identical tool arguments, alternating states, or an error class that never changes. A detector is not a substitute for verification, but it is a valuable brake against a doom loop.
Context rot and the doom loop
Long runs accumulate their own history. Earlier plans become stale, tool output crowds out the goal, and the model starts optimizing for the most recent text rather than the actual state. This is context rot. A doom loop is the operational form: the agent repeatedly calls the same tool, applies and reverts the same change, or keeps asking for information that cannot exist.
The remedy is not always another prompt. Compact state at checkpoints, keep durable artifacts outside the context window, re-read authoritative sources, and make the verifier’s result prominent. Use a fresh context when the working set is no longer legible, but preserve a structured handoff: goal, facts, decisions, open risks, changed artifacts, and last verification.
Failure modes and brakes
The tempting design is to add a larger turn cap whenever the agent fails to finish. That increases cost while hiding the actual defect. A better design has separate brakes for repeated calls, repeated state, rising failure rate, tool latency, budget exhaustion, and missing authority. Each brake should say whether to retry, pause for a person, switch to a fallback, or terminate.
Parallelism is not free
Parallel sub-agents can reduce elapsed time when work is independent, but they increase coordination, context assembly, cost, and failure surface. Give each child a scoped goal and output contract. The parent must reconcile outputs, detect contradictory evidence, and remain responsible for completion. “Five agents finished” is not the same as “the task passed.”
Use a work queue when the number of tasks is dynamic. Make leases, retries, cancellation, and ownership explicit. A worker that is alive but has lost its lease should not continue mutating shared state. A parent that has timed out should cancel or quarantine its children rather than leaving orphan work behind.
A production recipe
Start with a single loop and a deterministic completion check. Add pause and resume before adding more turns. Then test timeout after a side effect, worker restart between action and receipt, stale context, repeated tool errors, a verifier outage, and a goal that is impossible under the available tools. Observe queue age, turn count, cost, state changes, verifier outcomes, and stop reason. Make stop reason a first-class field in the run record.
For a code change, the run might stop when tests pass, the diff is clean, and a reviewer approves. For a data pipeline, it might stop when the expected partition exists, row counts match, and a quality check passes. For a research task, completion may require a human because “good enough” is a judgment. The engineering is to make that judgment explicit instead of hoping the model guesses it.
Verification patterns that scale
The verifier should inspect state, not merely restate the model response. For a code task, run the relevant tests and inspect the diff. For a data task, check schema, counts, constraints, and freshness. For an infrastructure task, query the actual deployment and health state. A model-generated checklist can be useful as a hint, but the hard completion predicate should be backed by a deterministic check or an explicitly assigned human decision.
Control parallel work with leases
Parallel agents are useful when tasks are independent, but a parent loop must own reconciliation. Give each child a scoped task, lease, deadline, and output contract. The lease prevents a disconnected worker from mutating state forever. The parent should reject duplicate or contradictory results and should cancel child work when the goal is complete or the run is paused. Otherwise “parallelism” becomes orphaned cost and an uncontrolled side-effect surface.
Use hysteresis, not nervous automation
A controller that reacts to one slow tool result can oscillate between strategies. Require a signal to persist for a small window, use cooldowns between expensive changes, and keep a cap on concurrent recovery attempts. This is the same reason production autoscalers use stabilization windows. The loop should be responsive enough to recover, but slow enough to distinguish noise from a state change.
Design cancellation as a real transition
Cancellation should stop new work, signal running children, preserve the last checkpoint, and record which effects completed. It should not simply drop the process and mark the run cancelled. A user who resumes later needs to know whether the previous tool call was never dispatched, is still running, or finished without a receipt. Cancellation is part of the run contract, not an exceptional afterthought.
Primary references
- OpenAI Agents SDK: running agents
- Anthropic: building effective agents
- Temporal workflow execution concepts
- Loop Engineering, Clearly Explained
The takeaway
Loop engineering is the discipline of making repeated work finite and accountable. Define success before the run, preserve state, spend a bounded budget, detect no progress, and verify completion independently. Autonomy is not measured by how long an agent keeps working. It is measured by how reliably the system reaches a true, observable state and stops there.
