CONCEPT · 120 minutes
Why parallel multi-agent systems fall apart in production, and the linear, context-sharing architecture that actually holds up — grounded in the multi-agent SDLC harness Brandon runs every day.
Let me tell you about the version of my SDLC harness that I'm slightly embarrassed by.
When I first built the multi-agent pipeline that drives my orchestration framework, I did the thing everyone does. I had a task — implement a spec with eight files of changes — and I thought: obviously, fan it out. Spin up one agent per file, run them all at once, merge the results. Eight agents working in parallel had to be roughly eight times faster than one agent grinding through sequentially. The math was seductive.
What I got back was a pile of merge conflicts and, worse, silent incompatibilities. One agent renamed a function; another agent, working on a different file at the same moment, called the old name. One agent added a config field; another wrote the validator assuming it didn't exist. Each agent's work was locally correct and globally wrong. I spent more time reconciling the eight outputs than the whole task would have taken with a single agent.
Here's the uncomfortable lesson I took from that: adding agents didn't add capacity, it added entropy. Every agent I bolted on made the system less reliable, not more. And once I started paying attention, I saw the same pattern everywhere — the flashy multi-agent demos almost never survive contact with a real, interconnected task.
The core claim of this module: reliability comes from context, not from concurrency. A single agent that sees everything beats five agents that each see a slice. The rest of this lesson is about why that's true and how to build for it.
This isn't an argument against ever running work in parallel — my harness today runs agents in parallel all the time. It's an argument about when parallelism is safe and what you have to guarantee before you reach for it. By the end, you'll be able to look at a task and predict whether splitting it will speed you up or quietly corrupt your output.
After I tore the parallel version down and rebuilt it, I found that almost every failure I'd hit traced back to violating one of two principles. I didn't invent these — they fall out of the structure of the problem the moment you look closely — but I learned them the expensive way, so let me save you the tuition.
Every agent needs the full context of what came before. Not a summary. Not a filtered "here's what's relevant to you." The complete picture.
Think of it like a relay race. The baton isn't just permission to run — it carries the state of the whole race: why you're running, what the earlier legs did, what the strategy is, what went wrong on the last handoff. Take the baton away and you don't have a relay team, you have four people sprinting in vaguely the same direction. That's exactly what my eight parallel agents were.
This is the subtle one. When an agent acts on incomplete information, it doesn't stop and wait — it makes an assumption, and that assumption becomes a decision baked into its output. Those implicit decisions are invisible until they collide.
Key insight: Every action an agent takes encodes decisions about the problem. When two agents can't see each other's decisions, they make conflicting assumptions — and conflicting assumptions don't announce themselves. They surface later as a bug you can't explain.
Here's the architecture that violates both principles at once — the one I started with:
Read line 9. At that moment, three agents are making decisions simultaneously, and none of them can see the other two. combine_results is where the lie comes due: you're asking one function to reconcile three sets of assumptions that were never compatible to begin with.
Let's walk up the ladder, from the architecture that bit me to the one I actually ship.
This is the most tempting shape and the most fragile. Every worker agent (red) runs blind.
graph TD
A[Task Input] --> B[Main Agent]
B --> C[Subtask 1]
B --> D[Subtask 2]
B --> E[Subtask 3]
C --> F[Agent 1]
D --> G[Agent 2]
E --> H[Agent 3]
F --> I[Combine]
G --> I
H --> I
style F fill:#ff6b6b
style G fill:#ff6b6b
style H fill:#ff6b6bNow the version I rebuilt toward. Agents run one after another, and each one inherits everything its predecessors did and decided (green).
graph TD
A[Task Input] --> B[Main Agent: Plan]
B --> C[Context: Task + Plan]
C --> D[Agent 1]
D --> E[Context: + Agent 1 work]
E --> F[Agent 2]
F --> G[Context: + Agent 2 work]
G --> H[Main Agent: Finalize]
style D fill:#51cf66
style F fill:#51cf66The whole trick is the Context: boxes between agents. Each agent doesn't get a fresh subtask — it gets the accumulated state of everything that happened before. Let's build that. First, the setup: a context object that records not just what each step did but why.
Notice reasoning riding along on line 12. That single field is the difference between an agent that knows what its predecessor did and one that knows why. With the context seeded, each agent in the chain reads the whole history before it acts:
There are no conflicting assumptions here because there are no independent assumptions — by the time agent 2 runs, agent 1's decisions are already part of the world it reasons about.
That agent.execute step is a real model call. Here's the smallest version of one, so the chain above isn't hand-waving — this block runs as written if ANTHROPIC_API_KEY is set:
For a task that runs long enough, the history outgrows the context window. The fix is to compress — keep the decisions, drop the noise — without losing the trail.
When this bites: context compression is the single hardest part of a long-running agent system to get right. Compress too aggressively and you silently delete the one decision a later agent needed — and because the agent doesn't know it's missing, it confidently does the wrong thing. I keep the full, uncompressed history in a side channel (line 13) precisely so a bad compression is recoverable instead of fatal. Do not add compression until your context actually overflows; you're trading a guaranteed-correct system for a faster one that can be subtly wrong.
Everyone obsesses over prompt engineering — the wording of the instruction. The bigger lever, by a wide margin, is context engineering: what information each agent sees, in what shape, in what order.
The difference is concrete. Compare what you could hand an agent:
Thin context — just the facts:
Rich context — the facts and the decisions behind them:
Same task. The second one prevents an entire class of errors — the agent won't reach for a second database, won't break multi-tenancy, won't forget rate limiting — because the reasoning is on the table, not just the outcome. Thin context forces the next agent to re-derive decisions, and re-derived decisions are where conflicts are born.
The pattern that works: record reasoning alongside every result, the way the linear system above does. An agent that can read why a choice was made almost never overturns it by accident.
Close the examples and answer these from memory before moving on:
reasoning field carry that a plain result field doesn't — and why does it prevent conflicts?Reflective prompt: Think about a task you'd actually want to automate with agents. Where is the seam you'd be tempted to parallelize across? Now ask: do the two sides of that seam share any state or assumptions? If they do, parallelizing it is the trap from the introduction — and naming why is the skill this module is really teaching.
Strip away the branding and the agent systems that actually hold up in production share the same spine, the one my harness converged on after the parallel disaster:
My harness today does run agents in parallel — but only across tasks I've proven are independent, in dependency-ordered waves, with an ordered merge at the end. That's the whole reconciliation of "parallel is a trap" with "I run parallel agents daily": parallelism is safe exactly when the two principles still hold — when the parallel units share no context they need from each other. The moment they do, you're back to linear, and you'll be glad you are.
The decision framework I actually use: Can one agent do this? Then stop — ship one agent. Must these subtasks run at the same time? If not, go linear. Do the parallel units truly share nothing? If they share anything, serialize them. Simplicity first, complexity only when a limit makes you.
Validate your knowledge of reliable agent architectures
1. Why do parallel multi-agent systems often fail in production?
Correct Answer: B
Parallel agents working without seeing each other's decisions make conflicting assumptions, leading to incompatible outputs and system failures.
2. What does the 'Share Context' principle mean for agent architectures?
Correct Answer: B
The Share Context principle requires that every agent has complete visibility into all previous work, decisions, and reasoning - not summaries or filtered views.
3. When should you add context compression to your agent architecture?
Correct Answer: C
Context compression adds complexity and should only be used when necessary for long-running tasks that would otherwise exceed context window limits.
Time to feel the difference rather than take my word for it. You'll build a small linear agent system for a content-creation task — outline, then draft, then edit — and watch how context propagation keeps the three steps coherent.
The task: given a topic, an outline agent produces a structure, a draft agent writes from it, and an edit agent tightens the result. The point is that each agent must see what the previous ones decided.
Start here:
Work it in stages. Reveal a hint only after you've tried.
Hint 1 (nudge): run is just three _step calls in sequence. The only thing that makes it "linear with context" is that _step reads self.context["history"] before it calls the model and appends to it after. Don't pass the topic alone — pass the accumulated history.
Hint 2 (the key API): Inside _step, build the prompt from history the way the "One Agent Step, For Real" example did — "\n".join(h["reasoning"] for h in self.context["history"]) — then client.messages.create(model="claude-sonnet-4-6", ...). Append {"agent": agent_name, "result": text, "reasoning": text} before returning.
Worked solution: study it only after your own attempt runs (or refuses to).
Going further: once it runs, break it on purpose. Rewrite run to call all three steps with only the topic — no shared history — and compare the edit agent's output. The incoherence you'll see is the multi-agent trap, reproduced in fifteen lines.
Nice work. Here's what's next.
Back to this pathOr book the diagnostic call