Every AI agent system I have seen in production — ours included — is built one of three ways. Most teams don’t choose consciously. They inherit the first way from the framework they installed, discover its ceiling, graduate to the second, and only hit the third when the second one fails them in a way they can name.
Last week one of our own agents died mid-thought because part of our system was still secretly living in the first way. This post is the taxonomy, and that story.
The first system everyone builds is a chain. Agent A analyzes, hands off to Agent B, which reasons, hands off to Agent C, which writes. Done. Every framework tutorial teaches this shape because it is the easiest thing to draw.
The problem is not that chains are simple. The problem is that the dependency chain becomes the architecture. Every new capability adds another handoff, another wait, another failure point. Want a reviewer? Insert it between B and C, and now everything downstream of B waits on it. Want a researcher? Another link, another serialization point.
Four costs show up on every invoice:
We call this the Agentic Waterfall, and like its namesake it isn’t wrong for small, known, linear work. It is wrong the moment the work stops being linear — which is the moment the work becomes interesting.
The obvious fix is to fan out. Run the backend agent, the frontend agent, and the test agent at the same time; join their results; continue. This is the DAG stage — workflow graphs, fan-out/fan-in, Promise.all with extra steps.
It genuinely helps. It is also where most mature teams are stuck today, because of a subtle property that took us a long time to articulate:
The branches are parallel. The system is not.
Every join is a wait-for-all barrier. The slow branch sets the pace; the fast branches sit idle at the merge; and nothing that happens inside a branch can influence a sibling, because the only communication channel is the join point at the end. A test agent that spots a fatal problem in the first ten seconds has no way to tell the backend agent to stop wasting tokens — they will meet at the barrier, later, when the money is already spent.
The graph also has to be drawn before the work starts. That is fine when you know the work. It fails precisely on the work that gets discovered while doing — and discovered work is what agents are for.
The third way stops modeling execution paths and starts modeling the environment where agents operate. Agents become participants in a shared runtime. They emit semantic events — “story S1 merged”, “verification failed”, “I discovered work nobody planned” — and they subscribe to what they care about. Nobody hands off to anybody. Progress continues while agents think, and the system waits only where waiting is real: a story that needs another story’s files genuinely waits for the merge; everything else flows.
Coordination logic stops being branches hardcoded into a workflow and becomes rules about situations: when a user message arrives and capacity is available, then run inference. Budgets, failures, and surprises become reusable system rules instead of special cases sprinkled through a graph.
Like an operating system running many programs at once — that is the mental model. Not a diagram of steps; a place where work happens.
Here is why I am writing this now instead of any other week.
Our agent, baro, runs a collective of coding agents against real repositories. The executors have lived in way three for months: they run concurrently, exchange events, learn about each other’s merges while working. But the planner — the agent that decomposes the goal into stories — was still architecturally a waterfall citizen: a separate process at the end of a one-way pipe, publishing plan fragments downstream, hearing nothing back.
Two things followed, and we measured both.
First, the fragile-recovery cost. Because the planner was outside the runtime, the host supervised it the only way you can supervise a black box: a wall-clock timeout. Eight minutes into a productive planning session — moments after it had published a fragment, the clearest possible proof of life — the watchdog killed it. The timeout had already been raised once, for the same reason, and the comment in the code admitted it. That is what “fragile recovery” looks like from the inside: when a component can’t tell you it is alive, you end up guessing, and every guess eventually kills someone innocent.
Second, the communication cost. The planner’s publish call returned a receipt computed by its own process — a polite local echo, not the runtime’s actual admission. When the host pruned a dependency edge the planner had imagined, that fact went to a log file nobody reads. Stories ran, merged, failed; the planner planned on, blind, against a repository state that no longer existed.
So we moved it into the collective. The planner is now a participant on the same event bus as everything it plans for. When it publishes a fragment, the receipt it gets is the runtime’s real answer — graph version, admitted stories, and exactly which dependency edges were pruned and why. While it plans the next fragment, execution news streams to it live: this story merged, that one failed twice, this one is blocked. And its supervision changed from “how long have you been running” to “how long have you been silent” — output resets the clock, so an agent that is visibly working can never be killed by an arbitrary stopwatch, while a genuinely hung one still dies fast.
The planner did not get smarter. Its situation got smarter. That is the whole thesis of way three in one sentence.
One thing this taxonomy is not: three competing tools where you pick a side.
A chain is a degenerate collective — one where every agent subscribes to exactly one event, “my predecessor finished,” and nothing else. A graph is a collective where the joins happen to be real and known in advance. An event-driven runtime expresses both, literally, as rules: when S1 merges, start S2 is a waterfall; when all three branches report, synthesize is a fan-in. Nothing about way three forbids sequence. It removes the obligation to sequence — the assumption, baked into ways one and two, that everything must block because some things must.
We see this daily in our own runs. Inside a single execution, a foundation story and its dependent run as a pure chain, six independent module migrations run as a graph, and the agents narrating discoveries to each other are the full collective — one runtime, one set of rules, three shapes coexisting because each part of the work got exactly the coupling it needed.
So if you are on way one or way two today, the migration is not a rewrite. Your workflow is already a valid program in the third model — just an implicit one. Making it explicit is what buys you the option to stop blocking, one barrier at a time, wherever the work turns out not to need it.
The honest version of the advice is not “always build way three.” It is:
And here is the claim we are prepared to defend: we will never reach truly intelligent systems without real concurrency — a runtime where agents discover, react, communicate, and adapt while the work is happening, not between predefined steps. Intelligence is not a property of a single model call; it is a property of a system that can change its mind mid-flight. A chain cannot change its mind. A graph can only change its mind at a barrier. Every intelligent system we know of — a team, a market, an organism — runs concurrently, notices things while acting, and re-plans without stopping the world. Agent systems will be no different.
We built Mozaik, our open-source TypeScript runtime, because we kept hitting that third case. The planner story above is just the latest component to make the move — and the pattern of the migration was the same as every one before it: find the one-way pipe, find the stopwatch, replace both with participation.
The dependency chain is not the architecture. The environment is.
With tools and technology we already have, we can build much more valuable systems than most projects today. We can write software that is a pleasure to use and a pleasure to work on; software that doesn't box us in as it grows, but creates new opportunities and continues to add value for its owners.
Newsletter
For developers who want to learn how to build self-organizing agents.
We're organizing a hackathon
We're organizing a hackathon — compete using the Mozaik framework.