What it looks like in the wild
A team builds a customer-support bot with an agent framework: a model loop, a tool registry, a scratchpad it fills on one call and reads back on the next. The pitch deck calls it agentic. It ships, it works, and for months nobody looks any closer than the pass rate on a dashboard.
Pull ten real traces and lay them side by side and the picture changes. Every one classifies the query, calls the same lookup tool, drafts a reply from the same template, and stops. Feed it a case that does not fit any of the categories the team designed for, and the loop does not do something different: it does not flag the mismatch, does not loop back, does not ask a follow-up question. It runs the identical three calls anyway and returns an answer that reads exactly as confident as it does on the easy majority of queries.
The same shape turns up wearing other names. A “research agent” that always retrieves, then summarizes, then drafts, in that order, never revisiting an earlier step even when the summary contradicts what was retrieved. A “coding agent” that lints, greps, then edits, on every ticket regardless of what the ticket asked. None of these are demos gone wrong. They pass their evals, they satisfy users on the traffic that matters most, and a model call genuinely happens at every step, so nobody is lying about what they built. What none of them do is arrive at a sequence different from the one they arrived at yesterday. The autonomy is nominal. The system paid for a decision-maker and got a dispatcher that never redispatches.
Why it happens
None of this comes from cutting corners. Three ordinary pressures produce it every time.
An agent framework hands you a loop, a scratchpad, and a tool registry before anyone has decided whether the task needed any of them. That scaffold is the path of least resistance: declining it, writing a plain function that dispatches on a category instead, takes a deliberate act no sprint plan asks for, while accepting it is just what “getting started” means.
Autonomy is also easier to demo than to justify. One well-chosen example makes a loop look genuinely capable, because a demo only has to run once and land well. Justifying that the same flexibility earns its keep needs a comparable baseline and someone willing to run both and read the difference honestly. That comparison is unglamorous, it costs a sprint nobody schedules, and a working demo already answers the only question the room asked: whether the thing works, not whether it needed to be built this way.
And once the system ships, almost nobody reads the traces to check whether the branching ever branched. Dashboards report latency, cost, pass rate, uptime; none of them are built to answer “did this call decide something, or restate the last decision.” That is the same discipline gap Hallucinated Done names on the completion side: a system’s claim about itself, agent, decides, adapts, gets recorded as fact because nothing downstream of the claim ever checks it against what actually happened. There the claim is a single stop signal; here it is the framework’s own description of the architecture. Either way, nobody built the check, so nobody has it, and the claim stands unexamined until something forces the question.
None of this is new. Schluntz and Zhang’s account of building effective agents at Anthropic already draws the line this failure crosses: predefined code paths for well-defined tasks, autonomous agents reserved for where flexibility earns its cost. They stop at the distinction; they do not name what shipping looks like when a team reads it and builds the loop anyway. Workflow in an Agent Costume is this catalogue’s name for that miss, a naming act, not a discovery, credited to the account that drew the line first.
What it costs
Every cost an agent architecture carries shows up whether or not the traffic through it ever needed autonomy.
Latency is the most visible: a sequential model call at every step where a fixed condition resolves in microseconds, paid on every request. Token spend is measurable rather than merely felt: the worked example below puts a multiple on it, on a task the router’s own accuracy shows did not need the extra machinery. Nondeterminism is quieter: the same input produces differently worded output run to run even when the path never varies, turning regression testing into a moving target for behavior that was never meant to move. Debugging suffers for the same reason: a decision that could have been a named branch in code is instead an inference sitting inside a transcript.
The failure surface is easiest to miss, because it stays invisible on traffic that ships cleanly. Tool/Agent Registry treats the callable set as declared data a team can inspect; a costume system still declares the full set, whatever the framework wired up, whether or not a given query reaches most of it. Every unused path is still one a reviewer must reason about and an injected instruction might try to reach, whether or not the last thousand traces touched it. An agent exercising three of its eleven declared tools has not reduced its risk to those three; the surface a security review covers is the declared set, not the observed one.
None of this is a matter of taste. There is a diagnostic that makes it checkable rather than argued about: pull a representative sample of real traffic and diff the traces, tools called, order, arguments, retries. If that comparison turns up the identical sequence on input after input, the autonomy is decorative, and that is a measurement, not an opinion. A single pass rate cannot surface this; it takes the breakdown Failure Buckets asks a team to build before it is needed, a bucket for queries whose path never varied held apart from queries that took a genuinely different one, because an aggregate score has no way to show a zero in that second bucket for what it is.
Worked example
Lab-001 measured a version of this cost directly, on 100 real customer-support queries filtered to ones resolving within five turns, without setting out to measure this anti-pattern by name. One system was a workflow router: a switch statement mapping four known categories to one prompt each, a single agent call per query. The other was a three-agent hierarchy: a classifier agent choosing among the identical four categories, a worker agent answering, a verifier agent checking the answer before it returned.
The classifier is the costume, measured, though it wears a different shape than the section above: not a path that never varied, but a live decision that never needed to be live. Its entire job, on every one of the 100 queries, was to select one of the same four branches the router’s switch statement already encodes for free, in code, at zero inference cost, and no query in the run needed a fifth. Handing that selection to a model call did not add a capability the switch statement lacked; it added a new way to fail with no equivalent in the router at all. The classifier misrouted 9 percent of queries to the wrong worker, an error that exists only because a decision was made live that could equally have been fixed in advance. Measured single run, one model family, one domain: router accuracy 87 percent against the hierarchy’s 74, cost $0.024 against $0.057 per query, 2.4 times more, latency 1.8 seconds against 4.0 at the median, 2.2 times slower.
The worker’s hallucination rate on grounding held steady across both systems, 4 percent, whether or not a deciding agent sat in front of it choosing which prompt to hand it. On that axis, the extra layer changed nothing about the answer once it was written, only how the query got there and at what cost, plus a nine-percent failure mode a plain switch statement has no way to produce. Sample its decisions across the run and every one lands where a fixed condition would have, at a price a switch statement does not charge.
The pattern that replaces it
Workflow First is not a smarter agent grafted onto the same loop. It is the decision made before the loop exists, applied at each step rather than once for the whole system: a build can keep a fixed skeleton around a single genuine judgment call and still pass, provided that call is the one step the branches could not be written down for. Default to the deterministic workflow, and require every increment of autonomy to clear the enumerability test before the first run, not after thousands of queries have already shown which flexibility the loop never used.
Run that test honestly and one of two things is true. Either the branches can be listed today, in which case the fix is not a better prompt for the classifier; it is deleting the loop and writing the switch statement the traces already show the system was executing anyway. Or the test genuinely fails: the next step depends on something not knowable until the run is underway, in which case the agent was never a costume, and its traces would show real branches, a path that differs on the hard case.
Keeping the autonomy that survives that test does not end the accounting. Earn the Complexity is the bar every later increment still has to clear, a measured delta on a named axis, not a plausible story about what a fourth tool or a longer loop might help with someday. Workflow First decides where a design starts. Earn the Complexity decides whether it is allowed to move again. Passing the first check once does not exempt everything built after it.
Related patterns
Workflow First is the default this anti-pattern describes the absence of: assume the deterministic path, and make autonomy earn its place with the enumerability test. Earn the Complexity is the standing bar for every increment of autonomy kept after that first decision, so one enumerability check does not become a blank check for the next tool or hop. Tool/Agent Registry makes the callable surface a declared, inspectable list, often the first place the mismatch shows: a wide declared surface next to a narrow observed one. Failure Buckets turns “look at the traces” from a one-off audit into a standing measurement, a bucket for an unvarying path held apart from genuine failures, rather than one aggregate score that cannot tell the two apart. Hallucinated Done is the closest sibling failure: both let a system’s claim about itself stand in for a check on what happened, invisible until somebody checks the claim against it.