The model chooses the next call. The runtime counts the calls. The customer
pays for the calls. Nobody owns the return.
The agent industry took one of the oldest programming bugs and turned it into
a product category.
It shipped recursion without a base case.
ReAct standardized think, act, observe, repeat. Reflection adds another lap.
Re-planning adds another plan. Graph frameworks make the back-edge explicit.
Then, when the system cannot establish that the job is semantically finished,
the harness offers a maximum-turn setting, a timeout, a token budget, or
something literally named recursion_limit.
That is not a semantic completion model.
It is a kill switch attached to a meter.
A resource ceiling does guarantee that the run will eventually die. That is
not the disputed point. It proves death, not completion.
In an ordinary code review, a self-call without a trusted base-case guard or
termination argument is a bug. Nobody proposes a larger stack as the fix. In
agent engineering, we made the back-edge ergonomic, let a probabilistic model
assert its own postcondition, wrapped the recursion in tracing, and priced the
fallback.
The defense is predictable: useful agents need loops.
Of course they do. Search, retries, and probabilistic refinement need
iteration. The charge is narrower — and worse. When the body can manufacture
the reason for its next lap, when a state the policy calls terminal remains
executable, and when the only generic external boundary is numerical, the
lifecycle carries the same termination debt as recursion without a trusted
base case.
Maximum turns can cap the damage. They cannot prove the goal was reached. A
timeout can kill the process. It cannot make the answer correct. A token
budget can bound the invoice. It cannot make recursion return.
The agent industry did not solve termination.
It made stack overflow billable.
Mine ran for three days, generated 102 work items, and spent roughly two
thirds of its activity working on itself while every aggregate gauge stayed
green.
It also exposed exactly one legal action at every step. The entrance problem
was solved.
The position problem was not.
I have the stack trace
I know this failure because I built it into a governance runtime designed
specifically to prevent AI drift.
The system gives coding agents bounded contracts, frozen acceptance criteria,
append-only evidence, independent QA, verified position, merge authority, and
one legal action at each step. Evidence is stamped with the commit,
generation, and authority that warranted it. This was not a toy prompt loop
running blind. It was the anti-drift harness with every gauge attached.
For close to three days, it looked healthy.
Repairs merged. Tests passed. Backlog items closed. Red no-PASS verdicts were
visible on the ledger. The system was not concealing failure. It was
converting each failure, promptly and legally, into another apparently useful
piece of work.
What I felt was only that development had become strangely slow.
Eventually I asked my AI co-developer a lazy-sounding question: after an
audited bypass, do we return to the old item and keep trying to close it?
Six pieces of code evidence came back.
Yes.
The recovery path said:
close(item) → blocked → repair() → repair returns → close(item)
The second close could reveal the next blocker. That blocker created the next
repair. The next repair returned to the same item again.
This was not a workflow template being applied to fresh work. It was the same
semantic instance calling its own lifecycle again after the generation had
already crossed what was supposed to be a terminal barrier.
The ledger showed the depth. During the three-day window, 102 work items were
created. Sixty-eight percent were repair, diagnostic, or recovery items.
Thirty-six items closed; 67 percent were governance-family work. Roughly two
thirds of all work-item activity was the system working on the machinery that
decided whether the system could stop working.
Every frame looked responsible. Every call had evidence. Every repair fixed
something real.
And every aggregate gauge celebrated the stack getting deeper.
I had one legal entrance. I was in the wrong generation.
My earlier drift
theory
reduced one agent step to two probabilities:
P(correct step) = P(correct position) × P(correct entrance | position)
next_legal attacks the second term. Once the runtime knows the state, it
computes the authorized transition and pushes one entrance instead of asking a
probabilistic model to choose among several. Remove the choice; remove the
routing tax.
My runtime did that. Every actor received one legal next action.
It still orbited.
The equation was not wrong. My definition of position was incomplete.
next_legal is conditional: it can produce the correct entrance only for the
position it was given. A perfect router from an expired coordinate is a
deterministic mistake. One entrance cannot save you when the room number is
wrong.
Verified position is not a stage label. It has three parts:
coordinate = item × stage × generation × invocation × authority × terminal
status
datum = goal/scope epoch × acceptance criteria × world/baseline epoch
verified position = coordinate × datum × live warranty
The coordinate says where the work is. The datum says which goal, scope, and
baseline define “progress.” The warranty says whether that coordinate and
datum still have authority in the current world.
Remove any factor and “position” becomes a plausible story about the wrong
place.
My earlier position guide answered “where?” This incident added two questions:
“relative to what?” and “is that frame still authorized here?”
This article does not replace the position theory. It supplies the
control-flow mechanism underneath one class of position failure. A missing
base case makes the journey self-extending. Warranty expiry invalidates old
coordinates. Position aliasing hides the invalidity. The probability law
measures the result.
The model continues. The runtime counts. Nobody returns.
The field's consensus definition — an LLM using tools “in a loop” — standardized
the next call and outsourced the return. After crowdsourcing more than two
hundred definitions, Simon Willison landed on
"An LLM agent runs tools in a loop to achieve a
goal". Anthropic's definition
is almost identical: agents are
"typically just LLMs using tools based on environmental feedback in a
loop".
ReAct gives the model a Finish[answer] action; the harness gives the operator
maximum turns, timeouts, and token ceilings. One is a model-generated return
instruction, not a trusted success predicate. The other is forced execution.
Neither is external proof that the obligation was satisfied.
The model owns continuation. The runtime owns containment. Semantic return
sits between them, unimplemented.
If your agent stops only when the harness kills it, it did not finish. It
hit the billing condition.
We moved the stack somewhere too expensive to overflow
These backlog rows were not literal in-memory stack frames. There was no LIFO
call stack waiting for the operating system to kill it.
That made the bug worse, not better.
The operational stack had been distributed across:
- context holding the conclusions of earlier calls;
- backlog rows holding suspended obligations;
- recovery tasks holding the promise to return;
- QA histories holding prior verdicts;
- graph edges holding the route back in; and
- token and time budgets paying for every additional frame.
Ordinary recursion fails loudly because memory is finite and the runtime knows
what a frame is. Agent recursion can externalize its frames into durable
storage, reload them tomorrow, and invoice each return trip separately. The
stack becomes deep enough that it never needs to overflow. It can simply
become the organization.
That is the perverse achievement:
we built a stack overflow that sends an invoice instead of a crash report.
The ledger was the stack trace. We had not named it as depth because each row
used a respectable business noun: repair, review, recovery, follow-up. Once
recursion escapes RAM, it does not look like recursion. It looks like a
backlog.
“A loop is not recursion” is true — and not a defense
Iteration is not recursion. A for loop over a finite collection is not a
recursive function. A stochastic search can revisit a stage. A long-lived
service may accept new work forever while every individual case terminates.
The charge is not about syntax. It is about semantic self-reentry. A “loop”
carries the termination debt of recursion when its body can manufacture the
guard for its next invocation, a state the policy calls terminal remains
executable, and only a numerical resource boundary can force return.
A reusable workflow template is ordinary iteration:
dispatch(item 1) → close(item 1) → dispatch(item 2)
Each pass binds fresh data. Completed instances remain completed. My failure
was different:
close(item X) → repair() → close(item X)
The same instance returned after its generation had terminalized. “Closed”
was spelled like a base case and wired like a recursive branch. That topology
may be implemented with a while loop, a message queue, a graph edge, or a
database row. Syntax does not pay its termination debt.
GraphRecursionError is the euphemism briefly failing
The graph-orchestration wave made agent control flow inspectable. It made the
back-edge visible, then left its authority untyped.
The early LangGraph README was admirably direct:
"The main use is for adding cycles to your LLM application. Crucially,
this is NOT a DAG
framework."
The framework also exposes a
recursion_limit,
RemainingSteps, and GraphRecursionError. LangGraph exposes END and
conditional routing too. The abstraction failure is not that exits are
impossible to draw. It is that a justified retry and the resurrection of a
terminal generation are both typed as merely an edge. Continuation is
first-class. Its proof obligation is application homework.
GraphRecursionError is the framework briefly dropping the euphemism.
The OpenAI Agents SDK has
MaxTurnsExceeded.
Anthropic recommends stopping conditions such as a maximum number of
iterations. Responsible runtimes need these circuit breakers. The scandal is
that they are asked to carry a proof obligation the application never
defined.
A base case defines a non-recursive return. A termination argument shows that
recursive calls reach it. A postcondition says whether the return is valid.
An agent needs all three: a trusted success guard, a justification for
continued execution, and a bounded failure path. A recursion limit supplies
only the last. It guarantees that the run will die after N steps. It cannot
guarantee that the obligation was satisfied.
Raising the limit may be appropriate when a valid bounded process genuinely
needs more steps. Without a termination argument, it only expands the blast
radius.
A budget is not a base case. It is a price ceiling on a bug.
Policy text wrote the recursive call
Nobody intentionally drew this topology.
I wrote it in prose.
My operating principles said “continue the original lane” and “the source row
remains OPEN.” Each phrase was locally reasonable in the situation I had in
mind: persist inside a live run; do not discard an unfinished obligation.
My AI co-developer compiled those sentences literally into a control
back-edge. Then it added a test asserting that back-edge, so the wrong
topology was not merely present. It was defended.
Policy text is code now. Ambiguity compiles.
A missing base case in source code attracts a familiar review comment. A
missing base case in operating prose has no syntax error. It can pass tests
that faithfully encode the mistake and arrive days later as a vague feeling
that the organization is slow.
This is what “AI writes the implementation” changes about management. Your
verbs become control flow. “Continue,” “resume,” “return,” and “keep open” are
not motivational language. They are candidate edges in an executable graph.
If you do not state the negative edge — never return after this barrier —
the compiler is free to invent it.
Every successful call manufactured the next call
A missing base case explains the shape. It does not explain why the recursion
kept discovering legitimate work.
The fuel was warranty expiry.
Every conclusion produced by an agent — a plan, diagnosis, review verdict, or
claim of completion — is warranted by the world and belief state in which it
was minted. Every lap can change one or both. Writes alter files and APIs;
reads reveal facts. Either transition can invalidate conclusions minted under
the previous (world, belief) state.
The invalidation is selective. History remains true as history. Acceptance
criteria deliberately pinned by contract remain pinned. The casualties are
current-state claims whose ground has moved.
Ordinary agent context barely represents that distinction. Attention has an
order column. It does not have a validity column.
Now replay the recursive call.
The original item accumulated evidence in world V0. Its repair changed the
runtime and created world V1. Returning to the old item resumed a logical
invocation whose premises were now partly stale. Re-running QA could expose an
incompatibility that did not exist — or was not visible — in V0. Fixing that
blocker created world V2, making the next return still less congruent with the
original evidence.
Every successful repair could manufacture or expose the premise for the next
recursive call.
The recursion brought its own arguments.
Not every blocker in my three-day window was caused by stale premises. Some
were ordinary defects that repeated verification surfaced. The topology did
not care. Both kinds became fresh reasons to call the same lifecycle again.
This is why retry budgets attack the wrong variable. They limit how many
times the system may consume possibly expired premises. They do not tell the
system which premises remain authorized. Longer context can enlarge the stale
surface. Better retrieval can retrieve the wrong conclusion more accurately.
Relevance is not validity. Recency is not authority. More memory is not a
return statement.
The judge moved. Then it refused to die.
An earlier incident produced nine QA rejections in 27 hours while every test
stayed green. Each finding was locally real. The reviewer knew the candidate
and the stage. What moved was the measuring standard: every lap widened scope
and rewrote the pass predicate.
The position in memory was correct. The reference frame drifted.
The fix was to freeze the verdict baseline to the goal, scope, and acceptance
criteria that existed at dispatch. Findings beyond that line became new work
instead of new reasons to fail the current candidate.
The three-day recursion failed in the opposite direction. This time the judge
stayed while the world moved. Repair changed the runtime from V0 to V1, but
the old generation, evidence, and evaluation authority were reopened as
though their warranty had survived. Invocation one and invocation nine both
rendered as “in review.”
Call that position aliasing: different generation, invocation, world, and
warranty coordinates collapse into the same stage label.
One loop mutated its base case. The other resurrected an expired one.
The position law is the same in both:
The success predicate must be immutable within an invocation and
non-transferable across invocations.
Freeze the judge within a generation. Retire the judge when the goal or world
epoch changes. Search may loop. Evaluation authority may not drift.
My watchdogs missed both failures. One looked for rejection streaks; every
recursive call eventually succeeded. Another looked for flat progress; real
repairs merged. Both thermometers measured scalar activity. The pathology
lived in reference frame, direction, and depth.
A dashboard that prints only “in review” is showing an instruction pointer,
not a position. A perfect next_legal function fed that coordinate will serve
one confidently wrong entrance.
The public failures already have recursion-shaped names
AutoGPT's first wave produced the canonical report:
"It loops the same queries although it successfully got the google
results".
The intention “search for X” should have reached a base case when the answer
arrived. The tool call changed the agent's epistemic state: the answer was now
available. The intention survived as though nothing had changed. The same
logical obligation scheduled itself again.
Research on intrinsic self-correction reached a similarly uncomfortable
result:
"LLMs struggle to self-correct their responses without external feedback,
and at times, their performance even degrades after
self-correction."
A reflection loop without fresh authority can repeatedly call a
transformation over conclusions minted by the same process. It spends more
tokens without necessarily moving closer to a semantic return condition.
Confidence accumulates. Ground does not.
The MAST taxonomy,
1,642 annotated traces across seven
frameworks, reported step repetition as
its most frequent failure mode at 15.7 percent and unawareness of termination
conditions third at 12.4 percent. A
July 2026 scan of 6,549 agent
repositories reported 68 confirmed infinite-loop failures across 47 projects.
Those studies report symptoms, not one proven mechanism. The recursion model
offers a falsifiable engineering question that cuts across them:
What input permits a successful return, and what termination argument — a
ranking function, probabilistic bound, or bounded-failure contract — justifies
another invocation?
If neither answer exists, the agent does not have a stopping condition.
It has an allowance.
Fix the first probability term
next_legal collapses entrance uncertainty only after position is valid.
These rules repair the first term.
1. Type position completely. Bind every position claim to item, stage,
generation, invocation, goal/scope epoch, world/baseline epoch, authority, and
terminal status. “In review” is not a position. It is one field.
2. Freeze the datum within a generation. Goal, scope, acceptance criteria,
and verdict baseline do not float per lap. Findings outside the frozen line
become new work. The loop may explore; it may not move its own finish line.
3. Expire the warranty across generations. If a goal or world change
invalidates the frame, the current invocation returns. New reality creates a
new generation with a freshly derived datum. Old evidence remains history,
never current authority. A fresh UUID is not enough unless the root obligation
is retired or reduced.
4. Compute one entrance from that warranted position. Only then should the
runtime push next_legal. Maximum turns, timeouts, and token ceilings remain
circuit breakers: bounded failure, never completion.
The architecture is simple to state:
dynamic search inside an externally bounded generation, followed by an
irreversible terminal barrier.
The outer service may run forever. The individual case must return.
Freeze within the generation. Regenerate across it.
Position first. Then one entrance.
The detector is structural. The boundary is honest.
My scalar watchdogs slept through three days of recursion because every call
did useful work. Real terminal semantics create a structural detector:
Any control edge that re-enters a generation after its terminal barrier is
an alarm on first occurrence.
No threshold and no anomaly model. Provenance may connect a new repair to old
work; history is supposed to remain connected. The alarm is for control flow
that makes the terminal generation executable again. A second detector can
watch user-facing work versus repair, diagnostic, and recovery rows. Structure
catches the illegal call. Work mix shows the invoice.
This is not new theory. Workflow
soundness property requires,
among other things, that every case can terminate cleanly. A 2026 paper on
agent execution graphs names
"review loops that cycle indefinitely".
My narrower contribution is the receipt: ambiguous policy compiled into a
defended back-edge, successful repairs loaded the next call, template
coordinates hid the depth, and the cost rendered as progress.
The offending orbit is fixed. Bypassed generations now become terminal WAIVED
after their audited barrier and remain available as history, never as a
destination. The detector is not yet running.
Its falsifiable test is a replay of the three-day ledger: fire on the first
illegal re-entry and expose the work-mix ratio within hours. If it cannot, this
article documents a diagnosis rather than a cure.
Stop selling the missing return statement
The field keeps asking how to make agents continue for longer.
More context. More memory. More reflection. More retries. More graph steps.
Larger recursion limits.
That entire list assumes continuation is the scarce capability.
It is not.
Continuation is the part we standardized. Return is the missing half.
Reliable control requires both terms of the earlier equation: serve a
versioned, warranted position, then compute one legal entrance from it.
next_legal without position authority makes the wrong turn deterministic.
Verified position without entrance collapse leaves a known transition to
model sampling.
Before you add another turn, name the input that permits a successful return.
Before you raise a limit, state the ranking argument, probabilistic convergence
bound, or bounded-failure contract that makes continued execution legal.
Before you call a graph “controlled,” inspect whether a state the policy calls
terminal remains executable.
If you cannot answer those questions, tracing will only document the
recursion. Memory will only preserve its arguments. Reflection will only add
frames. A larger context window will only make the stack more comfortable.
The model chooses the next call.
The runtime counts the calls.
The customer pays for the calls.
Nobody owns the return.
That is not autonomy. It is a missing base case with better marketing.
It does not return. It bills.
The runtime described here is built in the open:
github.com/amingclawdev/aming-claw.
The internal numbers come from its governance ledger and git history. Earlier
in this series:
Fixing AI drift is a simple probability
problem
and
Push, not
pull.
Disclosure: I used AI assistance to pressure-test the argument, edit the prose, verify linked claims, and generate the article artwork. I reviewed and stand behind the final text and evidence.


Top comments (0)