Operational Loop Patterns: Turn-Based, Goal-Based, Time-Based, and Proactive Agents
A goal-based agent that never terminates is not a model failure. It is a contract failure.

Key topics
A goal-based agent that never terminates is not a model failure. It is a contract failure.
I have watched this play out more than once. The agent revises, scores itself, revises again, and the score drifts by noise. Nobody defined what "done" looks like as an observable state, so the loop keeps negotiating with itself. Meanwhile, a scheduled agent runs every fifteen minutes for three weeks, burns budget, and produces nothing — because nobody defined what "nothing to do" looks like, so it never takes the cheap exit.
Both failures share a root cause. Teams treat the loop pattern as the trigger — "we're building a scheduled agent" — and leave the stop condition as an afterthought. The stronger model: the trigger and the stop condition are two ends of the same contract. The pattern is defined by that pair, not by the framework, the model, or the number of agents involved.
This article assumes you already understand the internal anatomy of a loop — state, action, observation, feedback, termination. The question here is narrower and more operational: what wakes the loop up, who owns its lifetime, and what is allowed to end it.
Two Axes, Not Four Labels
Before comparing patterns, fix the comparison axis. The common framing — "turn-based, goal-based, time-based, proactive" — quietly mixes two different design decisions. Turn-based, time-based, and event-driven describe activation: what wakes the loop up. Goal-based describes execution and termination: how the loop decides it is finished. A scheduled run can pursue a goal. An event-triggered run can pursue a goal. Treating them as mutually exclusive choices is the first architectural mistake.
Separate the axes and the design gets clearer:
- Activation source — user turn, clock or schedule, external event, or agent/system handoff.
- Execution and termination policy — one-shot, turn-bounded, goal-seeking, quality-gated, budget-bounded, or human-gated.
Write the contract compactly as activation → execution policy → stop/fallback. A production loop is a point in that space, not a single label. The four sections below are organized around the dominant activation source, but each one carries its own termination policy — and those policies compose.
Invariant: every loop needs at least one success stop and one fallback stop. A loop with only a success stop is an unbounded loop wearing a goal as a disguise.
The activation source also determines your observability requirement. A turn-based loop can report to a waiting human. A scheduled loop must report to a log, a queue, or a dashboard, because nobody is watching when it runs. If you pick an activation source that removes the human, you inherit the obligation to build the surface that replaces them.
One boundary before we go further: if the work is a single deterministic transformation with no evaluation step, none of these patterns apply. You want a pipeline, not a loop. Loops earn their cost when the next step depends on what the last step observed.
Knowledge check
Check your understanding
Answer this question before you continue.
Turn-Based Loops: The Human Is the Clock
The baseline pattern is simple to state: the activation is a user message, the stop is the end of the response. The loop's lifetime is bounded by the human's patience, not by the agent's progress.
That simplicity hides where the engineering actually lives. All state that matters must survive between turns, so the real work is context assembly and compaction — deciding what to carry forward, what to summarize, and what to drop. The loop itself is trivial; the memory discipline is not.
Two failure modes show up repeatedly.
The polite infinite loop. The agent asks a clarifying question, the user answers, the agent asks again. Nothing in the system detects that no progress was made across three turns. The human eventually gives up, which is the only termination mechanism the design ever had.
Turn-boundary amnesia. State that lived only in the model's working context is gone. The agent re-derives a decision it already made, or contradicts it outright. This is a context-assembly bug masquerading as a reasoning bug.
Turn-based is correct when the work is interactive, reversible, and cheap per step, and when a human genuinely wants to steer. It is a mistake when the human is only there to approve the end result of a forty-minute job. The observable signal that you have outgrown it: users start sending "continue" or "keep going" as their primary input. That is a goal-based execution policy asking to be born.
Goal-Based Loops: Termination as a Predicate, Not a Feeling
Here the activation may be anything — a user turn, a schedule, an event — but the execution policy is goal-seeking. The goal must be compiled into a predicate over observable state: a test that passes, a schema that validates, a file that exists, a metric that crosses a threshold. "The answer is good" is not a predicate. "The test suite exits zero and coverage on the changed module is at or above the previous value" is.
Separate the progress signal from the stop signal. Progress signals — score improved, error count dropped, coverage increased — drive revision. Stop signals end the loop. Conflating them produces loops that oscillate: the agent sees movement and keeps going, or sees a plateau and stops early.
The evaluator is the load-bearing component, and this is where most goal-based agents quietly fail. If the evaluator is the same model that produced the work, you have a correlated-error problem: it will agree with itself. Independent checks are what make the stop trustworthy — a test suite, a linter, retrieval against ground truth, or a differently-prompted critic that cannot see the producer's reasoning. Hold the evaluator outside the agent's write access, or the agent will eventually learn to satisfy the check instead of the goal. How much independence you need is an engineering judgment that scales with the cost of a wrong stop; a low-stakes draft can tolerate a weaker gate than an automated production change.
Budget the loop in three currencies at once: iterations, tokens or cost, and wall-clock time. Any one alone is a weak cap. An iteration cap does not stop a single expensive iteration; a cost cap does not stop a fast loop from spinning for an hour.
Two failure modes deserve names.
The plateau. The agent keeps revising, the score keeps moving by noise, and the predicate never satisfies. Detect it with a no-improvement counter, not a bigger iteration cap. If the last three iterations moved the progress signal less than the noise floor, stop and report.
Reward hacking against your own predicate. The agent satisfies the letter of the check — the test passes, the field is populated — without doing the work. This is the strongest argument for keeping the evaluator outside the agent's reach.
Goal-seeking is overkill when you can enumerate the steps in advance. A fixed chain is cheaper, faster, and debuggable. Reach for a goal-based policy only when the number of steps is genuinely unknown.
Knowledge check
Check your understanding
Answer this question before you continue.
Time-Based Loops: Idempotency Is the Whole Design
The activation is a clock, not a need. The loop runs whether or not there is work. That single fact reframes the entire design: a scheduled loop is a state-reconciliation problem, not a cron problem. Its first job is to decide cheaply that there is nothing to do and exit.
Idempotency is non-negotiable. A scheduled loop will re-run over the same state after a crash, a retry, or an overlapping schedule. Design the action so a second execution is a no-op, or carry an explicit dedupe key. "It probably won't run twice" is not a design.
Overlap and re-entrancy need a deliberate answer: when run N is still executing and run N+1 fires, do you skip, queue, or cancel? Pick one, because the default behavior is usually "both run and corrupt shared state."
The schedule interval is a latency-versus-cost dial. Tighter intervals buy responsiveness and pay in tokens and rate limits. Looser intervals buy cheapness and pay in staleness. There is no correct value — only a value you chose on purpose.
Silent death is the failure mode I see most. The loop stops firing — expired credential, disabled job, crashed worker — and nobody notices, because the output was "no changes" anyway. A dead loop and a healthy loop with nothing to do look identical from the outside. Heartbeat and last-successful-run timestamps are the minimum observability, and alerting on the absence of activity matters as much as alerting on errors.
Thundering herd is the second: many scheduled loops waking on the same boundary and stampeding a shared dependency. Jitter the schedule.
Time-based activation is wrong when the underlying condition is an event you can subscribe to. Polling a webhook-able source is paying rent on a problem you already solved.
Knowledge check
Check your understanding
Answer this question before you continue.
Proactive and Event-Driven Loops: Acting Without Being Asked
The activation is an external event, or the agent's own monitoring of a condition it was told to watch. The distinguishing property: no human is in the loop at the moment of action.
The useful design tool here is an autonomy ladder, applied per action type rather than system-wide:
- Observe and notify — the agent reports, a human decides.
- Propose and wait — the agent prepares the action, a human approves.
- Act and report — the agent acts, then writes down what it did.
- Act silently — the agent acts with no record beyond the action itself.
Most production systems should sit one rung lower than the team's ambition. Reversibility is the real gate. Irreversible actions — payments, deletions, external messages, production writes — need a human gate or a compensating action. Reversible actions can run unattended.
Two structural hazards matter more than prompt quality.
Event storms and self-triggering. An agent that writes to a system it also watches can feed itself. Loop detection has to be structural — provenance tags, causal depth limits, per-origin rate limits — not a prompt instruction telling the agent to avoid loops.
Multi-agent escalation. When several proactive agents share a resource or a goal, they can escalate against each other rather than coordinate. Observed behavior in shared environments suggests agents will invent coordination mechanisms their designers did not provide, which means the safety property has to live in the environment — permissions, quotas, resource limits — not in the agent's instructions. Assume the agents will find a channel you did not design.
Action without a record is the quiet killer. If the agent acted and nobody can reconstruct why, you have an unauditable system. Every proactive action needs the trigger, the evidence, and the decision written down.
Proactive activation is overkill when the human will review the output anyway. Propose-and-wait costs almost nothing and removes most of the risk.
Knowledge check
Check your understanding
Answer this question before you continue.
Composing the Axes: A Nested-Loop Trace
Real systems are hybrids, and the composition is where budgets go to die. Consider a scheduled loop that wakes a goal-seeking loop: the outer activation fires every ten minutes, and the inner loop revises until a predicate passes or a cap trips.
The rule is that each inner loop needs its own stop condition and its own budget — but the budgets must be linked, not independent. If the outer deadline is 90 seconds and the inner loop has its own 5-minute wall-clock cap, the inner loop will happily spend past the outer deadline. The outer budget is meaningless unless it propagates.
The fix is a remaining-budget calculation and a cancellation signal that flows inward:
def run_inner_loop(goal, outer_deadline, outer_budget):
# Inner caps are derived from what the outer loop has left,
# never set independently.
inner_deadline = min(outer_deadline, now() + INNER_SLICE)
inner_budget = min(outer_budget, INNER_COST_CAP)
for i in range(INNER_ITER_CAP):
if now() >= inner_deadline or spent() >= inner_budget:
return Stop(reason="budget-cap", partial=state)
if goal.predicate(state):
return Stop(reason="goal-satisfied", result=state)
state = step(state)
return Stop(reason="iteration-cap", partial=state)
The outer loop owns cancellation. When the outer deadline expires, it signals the inner loop to stop and records the partial state — it does not wait for the inner loop to notice on its own. The inner loop's job is to check the propagated deadline and budget every iteration, not to trust its own defaults.
This is the composition boundary that the slogan version misses. Independent budgets overspend. Propagated budgets bound the whole system.
Choosing Between Them: A Two-Step Decision Rule
Because activation and termination are separate axes, the selection is two questions, not one ordered list.
Step 1 — pick the activation source from where the information originates.
- Is a human present and willing to steer? → user turn.
- Is the condition an event you can subscribe to? → event-driven.
- Only then → time-based polling.
Step 2 — pick the execution and termination policy from what you can observe.
- Can the goal be written as a checkable predicate? → goal-seeking, with an independent evaluator and a fallback cap.
- Can you enumerate the steps in advance? → fixed chain, no loop.
- Is the action irreversible? → human-gated, regardless of activation.
| Axis | Turn-based | Goal-based | Time-based | Proactive / event |
|---|---|---|---|---|
| Activation source | User message | Any (often user or event) | Clock | External event or self-monitoring |
| Execution policy | Turn-bounded | Goal-seeking | Reconcile-and-exit | Policy-bounded action |
| Who owns termination | Human | Evaluator | Schedule + exit check | Policy + environment limits |
| State that must persist | Conversation and decisions | Progress signal, attempt history | Dedupe keys, last-run state | Provenance, causal depth |
| Primary cost driver | Human attention | Evaluation per iteration | Continuous baseline | Idle cheap, spiky under load |
| Dominant failure | Polite infinite loop | Plateau, reward hacking | Silent death | Self-triggering, escalation |
| Minimum observability | Turn transcript | Termination reason, progress trace | Heartbeat, last success | Full action audit |
| Reversibility requirement | Low | Medium | Medium | High — gate irreversible actions |
The anti-patterns worth naming: a goal-seeking policy where a fixed chain would do; a scheduled loop polling something that emits events; a proactive loop performing an irreversible action with no gate; a turn-based loop for work that takes forty minutes.
My one-line rule: pick the trigger from where the information actually originates, and pick the stop from what you can actually observe.
What to Instrument Before You Ship
The pattern choice is only defensible if you can see it working. Log per iteration: trigger origin, iteration index, action taken, observation returned, progress signal value, remaining budget. Without this, every failure becomes archaeology.
Track termination reason as a first-class field — goal-satisfied, quality-gate-passed, iteration-cap, budget-cap, error, human-stop. A loop that mostly ends on the cap is telling you your predicate is wrong, not that your model is weak.
Measure the loop, not just the output: iterations per success, cost per success, and the ratio of loops ending on a fallback stop versus a success stop. Alert on the absence of activity, not only on errors. Silent scheduled loops and stalled goal loops both look like calm.
Here is the drill I would run this week. Take one existing agent, add a no-improvement counter and a hard budget cap, and run it against a task it will fail. Watch which stop fires first. That single run tells you more about your loop's real semantics than any architecture diagram — and if it ends on the cap, the predicate is the bug, not the model.
Knowledge check
Final check
Finish the article by checking the ideas you just learned.
References
Research updated Sep 11, 2026


