“Let the agent keep working until it finishes” is not autonomy. It is an unpriced loop with an optimistic exit condition.

Real tasks contain ambiguous success criteria, unavailable tools, contradictory instructions, flaky environments, and actions that cannot be retried safely. A production agent needs more than persistence. It needs a harness that can distinguish progress from motion and completion from exhaustion.

The loop is the product

A single model call proposes an answer. An agent harness repeatedly assembles context, calls the model, validates proposed actions, executes tools, records observations, and decides whether another step is justified.

while budget.allows(next_step):
    request = build_request(task, state, history)
    proposal = model(request)

    if proposal.is_complete():
        return verify_completion(proposal, state)

    action = policy.authorize(proposal.action)
    observation = tools.execute(action)
    state = record(state, action, observation)

return stop_with_reason(state, budget.exhaustion_reason)

That outer loop owns the operational behavior. Current agent-harness documentation describes the same responsibilities: tool execution, history management, policy, observability, compaction, and completion conditions. A stronger model may improve each proposal, but it does not remove the need for the loop to govern the run.

Use a budget vector, not one step limit

A maximum number of steps is necessary and too crude. Different operations have different costs and risks. Represent the budget as several independent limits:

  • model budget: tokens, calls, and monetary spend,
  • time budget: wall-clock deadline and per-tool timeout,
  • action budget: total tool calls and calls by risk class,
  • failure budget: consecutive errors and repeated identical errors,
  • change budget: files modified, records written, or external mutations,
  • attention budget: approvals or clarifications requested from a human.

Budget checks belong before a step begins. Discovering after an expensive tool call that the run exceeded its limit is accounting, not control.

Define progress in the environment

Agents are excellent at producing plausible activity. Logs grow, plans become more detailed, and the same failed hypothesis returns with different wording. None of those observations proves the task is closer to completion.

Progress should be tied to state that matters:

  • a previously failing test now passes,
  • the set of unresolved diagnostics shrank,
  • a required artifact was created and validated,
  • new evidence eliminated at least one hypothesis,
  • a human supplied information that unlocks a blocked branch.

Store a compact progress fingerprint after each meaningful step. If the agent repeats the same action against the same state or produces no state change across several attempts, the harness should interrupt the pattern rather than hoping for inspiration.

Motion is not progressA retry is justified by changed input, changed strategy, or a transient-failure policy. “Try again” is not a strategy.

Make every stop explicit

“Stopped” should never be the only outcome. A useful run ends with a typed reason:

Stop reasonMeaningNext action
completedSuccess criteria were verifiedDeliver artifacts and evidence
blockedA named dependency is unavailableAsk for the minimum missing input
budget_exhaustedA hard resource limit was reachedResume with a deliberate new budget
policy_deniedThe proposed action was not authorizedChoose a safer path or request approval
no_progressThe state stopped improvingChange strategy before resuming

These reasons are product behavior. They determine what the user sees, what automation may retry, and which events deserve an alert.

Design for resume instead of pretending runs never die

Long agent runs will encounter deploys, crashes, expired credentials, and user interruptions. Resumability requires durable facts rather than a giant transcript.

Persist at least:

  • the task and acceptance criteria,
  • the latest validated environment state,
  • completed and pending actions with idempotency keys,
  • the active budget and consumption ledger,
  • the stop reason and the evidence behind it.

On resume, reload the environment before deciding what to do next. The world may have changed while the agent was asleep. Replaying the last model response without revalidation is how duplicate messages, duplicate pull requests, and duplicate payments happen.

The harness contract

The model proposes. The harness owns time, tools, permissions, state, and termination. Keep those responsibilities outside the prompt so they remain deterministic, inspectable, and enforceable.

A good agent is not the one that runs longest. It is the one that can explain what changed, why the next step is worth its cost, and exactly why the run ended.

Further reading: Microsoft Agent Framework: Agent Harnesses and Cloudflare Agents: Harnesses.