Stop Conditions and Execution Budgets¶
Why explicit stop logic matters¶
An agentic loop could otherwise run indefinitely:
observe
↓
decide
↓
act
↓
observe
↓
repeat...
The runtime must therefore know not only how to continue a run, but also when it must stop.
The model may propose:
STOP_SUCCESS
STOP_FAILED
CONTINUE
but hard termination enforcement belongs to the runtime.
The model proposes termination. The runtime owns termination.
Terminal states¶
Useful explicit states include:
COMPLETED
FAILED
CANCELLED
BLOCKED
EXPIRED
BUDGET_EXHAUSTED
WAITING_FOR_HUMAN
WAITING_FOR_EXTERNAL_EVENT
The WAITING_* states are usually suspended rather than terminal.
For example:
RUNNING
↓
approval required
↓
WAITING_FOR_HUMAN
↓
approval arrives
↓
RUNNING
Success stop conditions¶
A strong success condition is not:
LLM says: "Done."
but explicit evidence.
For a coding agent:
Goal:
Fix failing retry test.
Success:
- patch exists
- target test passes 20 consecutive runs
- related regression suite passes
- repository remains clean except intended changes
The runtime can enforce:
model proposes STOP_SUCCESS
↓
check success contract
↓
all true? → COMPLETED
otherwise → continue / replan
Failure stop conditions¶
Not every failure is retryable.
Examples:
NOT_AUTHORIZED
FORBIDDEN_ACTION
INVALID_GOAL
REQUIRED_RESOURCE_PERMANENTLY_MISSING
UNSAFE_TO_CONTINUE
In these cases, stopping or escalating is often correct. The agent should not search for a “clever workaround” to a hard authorization or safety boundary.
Cancellation¶
A user or system may cancel a run.
RUNNING
↓ cancel requested
CANCELLING
↓ cleanup / settle in-flight work
CANCELLED
Cancellation raises an important question:
What happens to a side effect that has already started?
An email may already have been sent and a deployment may already be running. Cancellation is not magical rollback.
The runtime should distinguish:
requested cancellation
vs
in-flight operation outcome
vs
possible compensation
Execution budgets¶
A budget is an explicit limit on loop autonomy.
Common dimensions include:
- maximum iterations,
- maximum model calls,
- maximum tool calls,
- wall-clock deadline,
- maximum token usage,
- maximum estimated or actual cost,
- maximum retries,
- per-tool quotas,
- maximum nested sub-runs.
Example:
{
"max_iterations": 20,
"max_tool_calls": 40,
"deadline_seconds": 300,
"max_cost_usd": 2.50,
"max_retries_per_action": 2
}
Hard vs soft budgets¶
Hard limit¶
Cannot be exceeded.
Example:
max_cost_usd = 5
The runtime may reject the next expensive call before executing it.
Soft limit¶
Can trigger a behavior change.
For example, at 70% of the cost budget:
- switch to a cheaper model
- reduce exploration
- ask the human whether to continue
These should be modeled separately.
Check budgets before a step¶
Weak:
call expensive model
↓
cost becomes $8
↓
check $5 budget
Better:
estimate/reserve step cost
↓
within remaining budget?
├── yes → execute
└── no → stop/escalate
Exact cost may not always be known in advance, but conservative estimates are useful.
Iteration limits¶
The simplest safety net is:
iteration <= N
but it is not enough. Ten iterations can still consume excessive money or produce many side effects.
A multidimensional budget is stronger:
iterations + tool calls + time + cost
No-progress detection¶
A runtime can stop or trigger replanning even when budget remains if the run is not making progress.
Examples:
iteration 4: search same file
iteration 5: search same file
iteration 6: search same file
or:
same tool
same arguments
same error
repeated 3 times
This is loop thrashing.
Useful progress signals include:
- a new relevant observation,
- a completed subgoal,
- reduced uncertainty,
- a changed artifact,
- a passed verifier,
- a new plan path.
Action fingerprints¶
Repeated actions can be detected with a fingerprint such as:
fingerprint = action_type + normalized arguments + relevant state version
For example:
query_logs(service=payment, range=1h)
Repeated with the same state and same result, this is suspicious.
Not every repetition is wrong: polling can be legitimate, so policy must consider context.
No-progress counters¶
For example:
{
"consecutive_no_progress_steps": 3,
"max_no_progress_steps": 4
}
When the threshold is reached, the runtime can:
replan
ask human
or stop BLOCKED
Time budget and deadlines¶
Distinguish active execution time from wall-clock waiting time.
A run may wait 30 minutes for approval without consuming active compute.
Possible limits:
active_execution_deadline = 5 min
approval_expiry = 24 h
run_expiry = 48 h
Nested-loop budgets¶
If skills or sub-agents create additional loops:
Parent run
↓
Sub-run A
↓
Sub-run B
it is dangerous for every child to receive a fresh full budget.
Weak:
parent max cost: $5
sub-agent A: $5
sub-agent B: $5
which may become $15.
Better:
parent budget
↓ allocate
sub-run budget slice
Hierarchical budgets should aggregate into the parent.
Tool-specific budgets¶
Some actions deserve special limits.
Examples:
web_search: max 20
production_restart: max 1 + approval
email_send: max 5
Not all tool calls have the same risk or cost.
Liveness vs safety¶
Two goals must be balanced.
Liveness¶
The system should make progress toward its goal.
Safety¶
It should not run forever or perform unnecessary or forbidden actions.
Too strict a budget causes premature stopping. Too loose a budget permits runaway loops. Choosing the right limits is also an evaluation problem.
Example: incident diagnosis¶
Budget:
max_iterations: 12
max_log_queries: 5
max_cost: $1.50
active_time: 3 min
Run:
1. query error rate
2. inspect deployment
3. query logs
4. query same logs with same filter
5. query same logs again
The runtime detects repeated no-progress behavior and triggers REPLAN.
If a new strategy still yields no useful evidence:
BLOCKED: insufficient diagnostic evidence
This is better than random tool roulette until the budget is exhausted.
Budget-exhaustion result¶
Do not return only a generic error.
{
"status": "BUDGET_EXHAUSTED",
"goal_progress": "PARTIAL",
"completed": ["repository inspected", "root-cause candidates narrowed"],
"remaining": ["verify candidate fix"],
"reason": "maximum tool-call budget reached"
}
A human or later run can continue from this state.
Stop-condition ordering¶
At a step boundary, the runtime may check in an explicit order such as:
1. cancellation requested?
2. unsafe / forbidden state?
3. hard budget exhausted?
4. success conditions satisfied?
5. terminal failure?
6. no-progress threshold?
7. continue
The exact order is domain-specific; making it explicit is what matters.
Anti-pattern: budget enforcement through prose¶
Prompt:
"Please do not use more than 10 tool calls."
This is not a hard guarantee.
The runtime should count:
tool_calls_used += 1
and physically reject the 11th call.
Anti-pattern: only max iterations¶
max_iterations=100 says nothing about:
- cost,
- side effects,
- tool bursts,
- nested agents,
- wall-clock duration.
Use multidimensional budgets.
Anti-pattern: success hallucination¶
The model says:
"The fix should work now, so we're done."
If the success contract requires tests, then:
no test evidence → not complete
Completion evidence is stronger than model confidence.
Takeaways¶
- Stop logic and budget enforcement belong to the runtime.
COMPLETED,FAILED,BLOCKED,CANCELLED, andBUDGET_EXHAUSTEDshould be explicit outcomes.- The model can propose stopping, but application code verifies hard success/failure contracts.
- Budgets should cover iterations, time, tools, tokens, cost, and retries.
- Distinguish hard and soft limits.
- Check budgets before actions when possible.
- No-progress detection is better than blindly exhausting an iteration limit.
- Nested loops/sub-agents should consume slices of the parent budget.
- Cancellation is not automatic rollback; side effects and compensation are separate concerns.
- Completion evidence is more reliable than the model claiming it is done.