Skip to content
Back to Blog

12 August 2026 · 16 min read · By Mark Laursen

Zombie Loops: The Long-Horizon Bug Nobody Codes For

Editor’s note (2026-06-14): Maestro has since moved to a Frontier-led model. The headline is now the Frontier Engine, a local multi-CLI fusion engine that fans a prompt across Opus 4.8, GPT-5.5, and Gemini 3.1 Pro and synthesizes one grounded answer; the discipline layer this piece describes is its proven foundation. The argument below stands as written; only the positioning around it has changed.

The run was supposed to take four iterations. By iteration four it had produced a clean output: a refactored migration plan with the constraints I had listed, the failure modes I had asked it to surface, and a recommendation I would have accepted in a meeting. The agent logged the iteration, paused for half a second, and started iteration five. Then six. By the time I checked back, the counter sat at forty-seven, and the loop had spent the last forty-three iterations polishing prose. The plan in iteration four was the plan in iteration forty-seven, with three sentences moved around. No crash. No error. Dashboard green. Budget bleeding.

That was the first zombie loop I had to clean up by hand, and it was the moment I understood that long-horizon autonomy has a failure mode nobody codes for. Crashes are easy. Hallucinations are visible. A zombie loop is a system functioning exactly as designed, doing meaningful-looking work, billing the meter, and missing the point that it was done forty iterations ago. The dashboard cannot tell you the loop is dead because the loop is alive. Only a contract written before the run starts can tell you the work is finished. We did not have one.

This post is what I wish I had written down before I shipped the first long-horizon agent. What a zombie loop is. Why nobody codes for it. Three disciplines that make a long-running loop terminable: pre-compaction checkpoints, a dual termination contract, a verifier subagent that grades in a fresh context. And the rule that prevents the whole class from compounding: one orchestrator loop, bounded specialists inside.

What a zombie loop actually is

A zombie loop is an autonomous agent run that keeps iterating after the success condition has been met. The run is technically alive. The artifact is changing between iterations. The cost graph climbs in the same shape it climbs during a successful run. The only thing missing is the recognition that the output from iteration four was the answer, and the next forty-three iterations are theater the model performs because the loop told it to iterate until it was sure. The model is never sure. The certainty was supposed to come from outside.

The distinguishing feature is not failure. It is the absence of a clean termination signal. A loop that errors out and stops is not a zombie. A loop that hits a max-iteration cap and exits is not a zombie either, because the cap is the contract. A zombie is a loop that has no external grader and no honest internal grader, so it runs until something else stops it. That something else is usually a token budget, a wall-clock cap, or a human noticing the dashboard. None of those are the right exit. They are the cleanup after the loop has already failed at knowing when to stop.

I covered the slow-motion version in eighteen months of production AI agents, where the most expensive failures were silent ones. A zombie loop is silent failure with a runtime. The fix is a contract the model can read and the system can enforce, written before the loop runs.

Why nobody codes for this

Success feels obvious until it isn’t. When you sketch a long-horizon agent on a whiteboard, the success criterion looks self-evident: the agent does the task, the artifact looks right, the loop ends. The shape of the failure mode is invisible because the example you are imagining is the happy path, and “the work is done” is grounded in your judgment as the person at the whiteboard.

The loop you ship does not have you at the whiteboard. It has a model in a context window, asked to grade its own output, told to be thorough and that the task is important. The honest answer to “are you done” from a model evaluating its own draft is almost always “I could do another pass.” That answer keeps the loop alive.

The failure mode also does not register on the metrics most teams already have. Token-per-iteration looks normal. Latency per iteration looks normal. The artifact does change between iterations, so “no progress” is hard to detect with anything cheaper than a separate model. The single signal that would catch a zombie loop, a count of iterations after the success condition was first met, only exists if the success condition was named at the start.

The third reason is cultural. The literature on long-horizon autonomy frames the open problem as “can the model sustain intent over many steps.” Sustaining intent is what makes a loop run. Recognizing satisfaction is what makes it end. The papers have been very loud about the first and very quiet about the second, so most production loops ship with carefully tuned intent-sustaining prompts and no satisfaction-recognition mechanism at all. Half the problem gets attention. The other half bills the meter.

A ZOMBIE LOOP IN PROGRESS REAL WORK iter 1 to 4 ZOMBIE STRETCH iter 5 to 47, no new value FORCED EXIT budget cap trips Cumulative cost Iteration number 0 4 25 47 50 success met no one is listening budget cap The artifact does not improve between iteration four and iteration forty-seven. Only the bill does.

Pre-compaction checkpoints

The first discipline that makes long-horizon work survivable is a checkpoint that exists before the context window fills. The context window is not durable memory; whatever lives only in the running conversation evaporates the moment compaction triggers or the next session opens. If you want the loop to remember why it started, what it has finished, and what it has decided, the memory has to be on disk, written before the model loses access to the live state.

The Maestro S10 specification names the checkpoint as a single durable artifact, gitignored, holding phase status, findings with sources, and decisions with rationale. The gitignore matters: the checkpoint is operational state, not source. The convention is to prefix it with an underscore (_<task>.md) so the repo’s gitignore can match _* without ambiguity. The artifact lives next to the work, gets read first on every resume, and never gets pushed.

The content is what makes it useful. Phase status answers “where in the run am I” without re-reading the conversation. Findings carry sources so the next iteration does not re-derive what is already known. Decisions carry rationale so the next iteration does not re-litigate a settled choice. The part I missed for too long: the verbatim terminal objective is written into the checkpoint at every pre-compaction write, so the next iteration cannot drift from the goal the operator originally set. Without it, a long-running loop slowly rewrites its own goal, and by iteration twenty the agent is optimizing for a target the human never asked for.

The timing rule is precise. The checkpoint is written before the context limit, not after. Once compaction has happened, the agent has already lost some of the state the checkpoint was supposed to preserve. The loop body looks like: read checkpoint, do work, write checkpoint, exit if termination met, sleep.

Checkpoint findings graduate. A failure observed once is a failure note. The next iteration investigates and the note becomes an investigated cause. The iteration after that verifies against the live system and it becomes a verified fact. When the same fact has been confirmed enough times to be load-bearing, it gets distilled into a rule the loop consults before deciding. The checkpoint accumulates institutional memory without inflating into a transcript.

The dual termination contract

The second discipline is a termination contract declared at the moment the loop starts, not midway through when the operator notices the run is misbehaving. The contract has two parts, and both have to be in place before the first iteration. One alone is not enough.

The first part is a checkable success condition. Not a vibe, not “when the artifact looks ready,” not “when the model is confident.” A condition you could grade with a script if you had to: the test suite passes, the migration plan covers the listed constraints, the document has at most three open questions remaining. The success condition has to be inspectable from outside the loop. The loop’s own internal sense of done is the problem; the contract has to be grounded somewhere the loop cannot reach into.

The second part is a hard cap on iterations, wall-clock time, or token spend, whichever fits the workload. The cap is a guard against the success condition being unreachable, the success condition being wrong, or the loop satisfying the letter of the condition without satisfying its intent. The cap is not the goal; the goal is the success condition. The cap is what stops the run from billing forever when the goal is unreachable. Without it, every zombie loop runs until the cloud bill cuts off.

The two parts together are what make the contract durable. A success condition without a cap is a recipe for a zombie loop. A cap without a success condition is a recipe for a system that always runs to its cap. Together, they form the exit conditions: the loop ends because the success condition was met, or because the cap was hit. The end condition set at the start wins over anything encountered mid-run; if the loop discovers three iterations in that “actually this is more interesting if I keep going,” the cap still trips. Agents are not allowed to re-scope themselves into a longer run because they think they are doing well.

A related rule prevents a failure mode I have lived: autonomous runs never block on the user. If a long-running loop hits a decision point and stops to ask, the human is not there, and the loop sits idle while the meter still runs. The contract has to say: decide, record the reason in the checkpoint, surface the decision in the final report. The decision can be wrong; the wrongness is recoverable. The block-and-wait is not, because the agent has no idea how long the wait will be.

THE DUAL TERMINATION CONTRACT declared before iter 1 Success condition checkable from outside the loop tests pass / constraints met artifact covers required fields graded by verifier, not loop Hard cap guard for unreachable / wrong success max iterations max wall-clock max token spend either fires; whichever comes first wins; mid-run renegotiation is forbidden loop exits, reports why

The verifier subagent in a fresh context

The third discipline took me longest to accept, because it sounds redundant until you have seen the failure mode it prevents. Success is graded by a verifier subagent in a fresh context, not by the loop that did the work.

The loop cannot grade the work honestly. Not because the model is dishonest; because the model is calibrated by its own context. By iteration fifteen, the loop has read its own output fifteen times, accumulated reasons each version was an improvement, and built a story about why the work matters. Ask the loop “is this done” and the answer is filtered through every justification it has produced. The model is doing what models do when you ask them to self-report from inside their own context.

The verifier is structurally separate. It opens with no memory of how hard the task was. It has not been promising itself for ten iterations that the next pass will nail the introduction. It has the success criteria, the artifact, and a prompt that asks: does this artifact meet these criteria, yes or no, name the gaps if no. The smallness of the task is what protects it from inheriting the loop’s biases. It does not get the loop’s reasoning trace, the iteration history, or the model’s self-assessments. Just the criteria the operator wrote at the start and the artifact the loop produced.

If the criteria were vague, the verifier will say so; vague criteria are the second-worst failure mode after no criteria. If the artifact does not meet the criteria, the verifier names which one and stops. The loop can then iterate against that named gap, which is a different task than “polish until you are sure.”

I covered the structural shape in done means done. Status tokens live at the boundary of a single agent run; the verifier lives at the boundary of an autonomous loop. They share the principle that grades belong outside the system being graded. A loop that grades itself ships zombies. A loop that hands the grading to a fresh-context verifier ships at the right moment.

The verifier runs in the cheapest model that can read the criteria and the artifact. Verification is a comparison task, not a frontier-class one. Running it small and fast keeps per-iteration overhead low enough to afford every iteration. The expensive model does the work. The cheap model says when the work is done.

VERIFIER IN A FRESH CONTEXT Loop context (long, dirty) accumulated state, biased toward "almost done" terminal objective (verbatim) iteration history (15+ passes) accumulated justifications model confidence scores "yes I think it is done now" self-graded loop ships zombies artifact + criteria Verifier context (fresh) no history, no investment, no story success criteria (verbatim) artifact only (no history) no reasoning trace no iteration count "meets 3 of 4 criteria; gap: X" named gap, loop knows what to do next The verifier is the smallest possible task. The smallness is what protects it from inheriting the loop's biases.

One orchestrator loop, bounded specialists inside

The last discipline is structural. Loops never spawn loops. One orchestrator loop handles the long-horizon re-entry; specialist groups inside are bounded, run-once, and report up. The temptation, once you have a working autonomous loop, is to use it as a building block for a bigger one. Resist it. The failure modes compound, and the cost of debugging a loop-of-loops grows faster than the value of the architecture.

The termination contract has to live in one place. If the outer loop has a clean contract and the inner specialist has a sloppy one, the system inherits the worst exit behavior of any layer. A bounded specialist group can spin for a few iterations and report back, and the orchestrator decides whether to re-run, refine, or accept. A specialist group that spawns its own autonomous loop with no contract is a hidden zombie waiting for an excuse.

One autonomous loop per system. Everything inside is bounded: planner runs and exits, specialist runs and exits, verifier runs and exits, integrator runs and exits. The orchestrator decides what runs next and is the only thing that re-enters itself. That single re-entry point is also the single termination point. When the contract fires, the orchestrator stops, and because nothing else was looping autonomously, nothing else has to be stopped.

I covered the cost side in the /loop pattern, where the recurring rule is: never poll harness-tracked work. If the runtime tells you when a tool finishes or when a specialist returns, do not write a loop to ask. The orchestrator listens for events; events become the heartbeat of the autonomous run. The loop only wakes when something has happened.

Harness mutations are the worst case of self-grading. If a long-horizon loop modifies the instructions, tests, evals, or runners that grade its own work, every iteration changes what success looks like. The S10 doctrine is precise: a harness mutation names the component, the targeted failure mode, the predicted improvement, the falsifying check, and the rollback path, and the iteration reports PENDING_REVIEW. The loop never counts a harness change as green evidence. Same rule inside an orchestrator: changes to the contract require human review, because anything else is the loop renegotiating with itself.

What the three disciplines look like together

A long-horizon agent that does not zombie has a specific shape. The orchestrator opens by reading the checkpoint if one exists, or writing the initial checkpoint with the verbatim terminal objective and the dual termination contract. It runs the first specialist group, accepts the artifact, and hands it to a verifier subagent in a fresh context. The verifier returns yes-with-evidence or no-with-named-gaps. If yes, the orchestrator records the success, writes the final report, and stops. If no, it records the named gap, decides whether to spawn another specialist group against it, checks the iteration cap, and either continues or exits. The exit is always a recorded decision, not a silence.

That shape survives compaction, context loss, and operator absence. The checkpoint is the durable memory. The contract is the durable definition of done. The verifier is the durable external grader. The single orchestrator loop is the durable exit point. None of them ask the model to be smart about termination. The model does the work. The system decides when the work is finished.

This is also the architecture that lets a heterogeneous fleet share a discipline, the way a portable doctrine layer is meant to work. The model improving at long-horizon work is a gift; the doctrine layer still owns the contract, the checkpoint, and the verifier, because those rules are portable across models and survive provider changes. The model is substitutable. The discipline is not.

What I am still wrong about

I want to flag where my conviction is thinner than this post suggests, because anyone who claims to have long-horizon autonomy figured out is selling something they have not lived with long enough.

The granularity of the success condition is the part I am worst at. Too vague and the verifier returns “yes” too easily, which is the zombie failure mode in a different dress. Too specific and the verifier returns “no” on artifacts a human reviewer would accept, sending the loop back into iteration on details no one cares about. The honest fix is to read your last ten runs, notice which criteria the verifier graded incorrectly, and tighten or loosen.

The verifier itself can drift. The prompt is durable, the model behind it is not. A new release that changes how the verifier interprets the criteria can quietly start grading more or less leniently. The check is to run the verifier against held-out known-good and known-bad artifacts on every model change. I keep meaning to formalize this and I have not.

The fully autonomous case is the one I have the least signal on. My systems still have a human reading the final report. The cap should trip when no operator is watching, but the corrective signal from a human noticing “the cap should be different” does not exist, and the system silently runs against a misconfigured contract. I expect that to bite teams running fleets of fully autonomous agents in the next year.

The first time I caught a zombie loop, I let it run because I was curious how long it would go. It went nine more iterations before I cut it. Each one looked productive from inside. Each one was paid for in real money. If your agent is doing long-horizon work and you have not written the success condition, the cap, the checkpoint shape, and the verifier prompt before the first iteration, you are running a zombie loop. You just have not noticed yet. Write the contract first. Let the loop end when the contract says it should.

Mark Laursen
Mark Laursen

Advisor, founder, and executive producer with 25+ years building technology companies, gaming platforms, and entertainment products. Based in Portugal.