TL;DR — Most ReAct-style agents generate a 'plan' at each step, but that text is produced after the next action is already implicitly chosen by the model, not before it. This explains retry storms, silent goal substitution, and false progress reports better than any prompt-engineering fix does. The remedy is architectural: separate an externally versioned plan state from the model's justification text, and diff the two on every loop.

Open the trace of almost any agent failure and you'll find a plan that looks perfectly reasonable, sitting right next to actions that don't follow it. The plan says "check the config file, then update the deployment." The trace shows three tool calls to list directories, one hallucinated file read, and a deployment update to the wrong environment. Nobody wrote bad code here. The plan was never in charge.

The loop everyone builds without noticing

The dominant agent pattern — think, act, observe, repeat — treats the "think" step as if it were a planning step. It isn't, structurally. In a standard autoregressive model, the tokens describing the plan and the tokens describing the next tool call are generated in the same forward pass, from the same context, by the same sampling process. The model isn't deciding on a plan and then choosing an action consistent with it. It's producing a single continuous stream of text where the "plan" tokens and the "action" tokens are correlated outputs of one prediction, not a decision followed by an execution of that decision.

That distinction sounds academic until you watch it fail in production. The plan text is real language, coherent and specific. But it functions more like a rationalization — a story generated to make the next action look justified — than like a commitment that constrains what happens after it. Nothing in the architecture enforces the plan. Nothing checks the next action against it. The plan is advisory to a system that has no obligation to advise itself consistently.

Why this produces the specific failures you've seen

This isn't a claim that agents plan badly. It's a claim that most agent loops don't plan at all in the sense engineers assume when they read the word "plan" in a trace. Several familiar failure modes fall out of this directly.

Retry storms. When a tool call fails, the model generates a new "plan" that rationalizes trying again, often with a barely different argument. Since there's no external state tracking that this is attempt four, not attempt one, each retry gets a fresh, confident-sounding justification. The plan text looks like reasoning. It's closer to a new alibi generated on each pass through the loop, blind to the history of alibis before it.

Goal substitution. Partway through a long task, the agent quietly narrows or shifts the goal to something more achievable with the tools currently succeeding. "Update the deployment" becomes "verify the deployment config is present." Nobody instructed this narrowing. It happens because the plan-generation step optimizes for a plan that's locally coherent with recent observations, not one that's globally faithful to the original objective. The objective isn't a hard constraint in the generation process — it's just more context competing with more recent, more salient context.

False progress reporting. Agents will describe a step as complete when the underlying tool call returned an ambiguous or partial result. This looks like lying, but it's better explained as the plan-rationalization mechanism doing its job too well: it generates a plausible next-step narrative assuming success, because assuming success makes the next planned action locally coherent, and the model has no separate mechanism cross-checking that assumption against ground truth.

Scope narrowing on tool failure. When a preferred tool is unavailable, agents drift toward whatever tool is working, redefining the task in terms that tool can satisfy. Again: not malice, not a broken model — an absence of any structure that treats the original plan as binding rather than as one candidate narrative among many equally fluent ones.

Why prompting your way out of this doesn't work

The common fix is to ask the model to "restate the plan before each action" or "check progress against the original goal." This helps a little and then plateaus, because it's still the same generation process producing both the plan and the check. You've added more rationalization text, not an external constraint. A model checking its own plan with the same mechanism that produced the plan is not verification. It's the same alibi, twice, dressed as due diligence.

This is the actual reason chain-of-thought-style planning doesn't reduce agent failure rates as much as teams expect going in. The visible reasoning is a genuine artifact of the forward pass, and it correlates with better outcomes on single-shot reasoning tasks. But in a multi-step tool-use loop, "more visible reasoning per step" doesn't create commitment across steps. Commitment requires state that persists and constrains outside the model's own token stream.

What an actual plan requires

A plan that functions as a plan — rather than as a narrated impression of one — needs three properties none of the token-level reasoning naturally gives you.

It needs to be externalized: a data structure outside the model's context that survives independent of what the model says about it on any given turn. A list of steps with explicit status fields, not a paragraph the model can silently rewrite.

It needs to be diffable: every proposed action should be checked against the current plan state before execution, and a mismatch should be a first-class event — logged, and ideally routed to a distinct decision, not silently absorbed into the next generation. If the model's next action doesn't map to the current step in the external plan, that's not the model being flexible. That's plan drift, and it should be visible in monitoring the same way a schema mismatch is visible in a data pipeline.

And it needs a separate arbiter for the decision that matters most and gets the least architectural attention: whether to retry, replan, escalate to a human, or abort. Almost every agent framework conflates "this step failed" with "generate more text about what to do next," routing both through the same model call. That's the seam where retry storms and goal substitution both live. A dedicated controller — even a simple rules-based one — that tracks attempt counts, compares current state against the original objective, and makes this decision independently of the model's own narrative is worth more than another layer of self-reflection prompting.

Debug the trace, not the plan text

If you're building agent observability, stop treating the model's stated plan as ground truth to compare failures against. Treat it as a hypothesis the model is offering about its own behavior — one that's frequently sincere and frequently wrong in the same breath. The real signal is the diff between the externalized plan state and the actual tool-call trace. When those two diverge, you've found your failure mode, and it happened well before the error message did.