Ask what the word forbids
A shirt is a compression. This is the thing it compresses — and a refund desk that shows why the usual framing misses what actually decides the architecture.
Loops underlie graphs
A graph executor is a while loop with a pointer into a node table. A ReAct-style loop is a graph — one where every tool is reachable from every state, so the topology is trivial and nobody bothers to draw it. They are duals, not rivals.
Which means the framework debate is not about which structure is more powerful. It is about ergonomics, and about a question that predates all of this: where does control flow live?
Design time, or runtime
In a loop, the next step is whatever the model emits. You never enumerated the transitions — they are implicit in the fact that any tool can follow any other. In a graph, you enumerated them in advance. The model may still choose an outgoing edge at a router, but it picks from three options you wrote down, not from the open set.
That difference cashes out in unglamorous engineering properties:
| LOOP — control flow at runtime | GRAPH — control flow at design time | |
|---|---|---|
| Guarantees | You can ask that validation precede a write. It will usually comply. | There is no path to the write that does not pass through validation. |
| Testability | One integration test with a nondeterministic path through it. | Nodes are functions with narrow inputs and a small tool set. |
| Durability | Checkpoints are ad hoc; resumption is something you build. | Node boundaries are natural resume and pause points. |
| Coverage | Handles situations nobody anticipated. This is the whole advantage. | Every edge you did not draw is a case it fails on. |
Structure buys auditability and invariants, and only handles the paths you imagined. Delegation buys generality, and cannot promise much about any individual run. That is the honest trade — but it is not yet the interesting part.
A refund desk
A support system handles refund requests. Five capabilities: look up the order, check it against policy, decide, move the money, tell the customer. Plus a human to escalate to.
A As a loop
B As a graph
As a loop
Register the five, write one prompt, let it run. It is genuinely good. A customer writes: I ordered two, one arrived cracked, and I have since moved house. Nobody drew that edge. The system composes a lookup, a partial refund, and an address change without ever being told that combination exists. That is not a small thing, and no enumerated topology gets it for free.
What you cannot do is promise that the policy check ran before the money moved. You can ask. It will usually comply. Usually is not a sentence you want to say to a finance team.
As a graph
Lookup, then policy, then a decision node with two outgoing edges: issue, or escalate. Now the invariant is structural rather than requested. There is no path to the refund that does not pass through the policy check — not because the model is well-behaved, but because the edge does not exist.
You can unit-test the policy node. You can resume from step four when the payment provider times out. You can hand an auditor the topology instead of a transcript. And the cracked-item-plus-moved-house case hits a node written for one thing and either mangles it or dumps it to a human.
Two properties, neither of them named on the shirt
The boundary in that system was not placed because someone chose graphs over loops. It was placed because of two facts about the refund desk that have nothing to do with either word:
Reversibility.
issue_refundmoves money.lookup_orderdoes not. One is replayable, the other is a commitment. That asymmetry is what made the guarantee need to be structural rather than requested — you can retry a lookup on a bad run, and you cannot un-refund.Continuation ownership.
check_policyreturns its result into context and the model decides again.escalate_to_humantransfers control and never comes back. That determines context growth, whether the model can course-correct, and where the trace ends.
Both are properties of the callee. Neither is visible in how the model’s output reached it. You could swap every one of these between a tool call, a JSON blob parsed by your harness, and a line of generated code, and not one of these properties would change.
Why "tool" does not survive
Add one more capability to the refund desk: classify_reason, a router whose body is another classifier. It is a tool. It is also pure control flow with no side effects. It also contains a decision that could itself have been a tool call. The label holds at all three levels while telling you nothing about any of them.
"Tool" is a transport word. It means: registered with the harness as a tool. That is the entire definition and it is circular. It describes how the model’s output got routed to a function, and says nothing about the function — not purity, not reversibility, not latency, not whether it recurses.
The same is true of the rest. "Agent" names a while loop. "Graph" names a dispatch table. Every term names a piece of plumbing and then gets used as though it named a property of the system’s behaviour. That is the mechanism behind the hype: not that anyone is lying, but that the vocabulary is mechanism-shaped, so sentences built from it feel like claims while asserting nothing checkable.
“If a word rules out nothing about the thing it names, it is not carrying information — and any argument leaning on it is doing so decoratively.”
Deflation is a corrective, not a theory
Mechanism-level definitions tell you what a thing is and nothing about why it works. "While loop" does not explain why looping over a model that picks its own next step behaves differently from looping over one that cannot. That difference is the only genuinely new thing here, and the deflated vocabulary is exactly the vocabulary that cannot express it.
So: reach for deflation when someone is selling you a framework. Put it down when you are trying to explain why these systems sometimes work. Both halves are needed. Only one of them fits on a shirt.