Part 5 of 5 in the series “How We Built a Distributed AI Team.” Written before the move and left as it was. Part 6, “Healthy at the HTTP Layer, Deaf in Fact,” is what happened next.
For the final post, let me start with an honest admission about where the team actually lives.
It lives on a laptop.
Everything I’ve described across these five posts — the orchestrator, the agents that grew into first-class members, the shared brain, the guardrails we earned the hard way — all of it runs on a single machine sitting on a desk. Scheduled jobs fire from the operating system’s own scheduler. The router that dispatches work spawns one agent session at a time. The knowledge digest, the STATE files, the activity log: files on a disk. If that machine sleeps, the team sleeps.
For a solo operation that has been the right call. It’s cheap, it’s inspectable, and when something breaks I can watch it break in real time. But it is obviously not the destination. A team you have to keep a laptop awake for is not really a distributed team — it’s a very organized single process wearing a lot of hats, which is exactly where post one said we started. So this post is about the direction of travel, and — more usefully — about the specific things that direction forces you to fix before you can take a single step.
The destination: one agent, one cloud resource
The end state I’m building toward is simple to state and awkward to reach: each agent becomes its own isolated cloud resource. Its own container or managed compute. Its own credentials, scoped to exactly what that agent is allowed to touch and nothing else. Its own context and its own guardrails, carried with it rather than borrowed from a shared machine. No agent depends on any one host being awake, and no agent holds keys to a domain that isn’t its job.
We’ve been designing for this the whole time without being able to run it yet. The permission model is already token-scoped per agent rather than trusting the harness. Each agent already reads its own STATE and its own slice of the shared knowledge. The container foundation exists as the migration target even though it has never carried the live workload — the real runtime has always been the laptop, and I’d rather be honest about that than pretend the future is already here.
The move itself is AWS-native and it’s parked behind exactly one go/no-go gate: proving that a headless agent session runs cleanly on a remote host the way it runs locally. Until that spike passes, everything downstream is theory. That’s deliberate — better to block the whole migration on one honest test than build a cloud architecture on an assumption.
What parallelism breaks
Here’s the part that surprised me, and it’s the reason post four turned out to matter more than it looked.
On the laptop, the router spawns agents serially — one at a time, each a tracked, killable child process with a hard timeout. I described in post four why that single design choice is load-bearing: it makes a runaway loop structurally impossible, because the worst case degrades to a slow trickle of one spawn every few minutes rather than thousands of processes at once. It’s not a safety feature we added. It’s a safety feature we got for free from running on one machine.
The moment you go to the cloud, you go parallel — that’s the entire point of going to the cloud. Multiple instances, multiple workers, agents running genuinely at the same time instead of taking turns. And the instant you do that, the free safety net disappears. The implicit rate limit was the serial loop. Remove the serial loop and it’s gone.
Worse, the guards that felt safe turn out to have a hidden assumption baked in. Several of our protections are backed by a local ledger — a count the router keeps on its own disk: how many sessions this conversation has spawned, which messages we’ve already handled so we don’t handle them twice, the daily volume caps. On one machine, one ledger, one source of truth. Run two instances and each keeps its own ledger, and neither knows about the other. A cap of “at most N spawns per conversation” quietly becomes “up to 2N,” because each instance independently permits its own N. This is per-instance amnesia, and it’s the kind of bug that doesn’t show up in code review — it shows up in production, on the day you scale out, when it’s expensive.
So the migration checklist writes itself, and it’s not glamorous:
- Move the ledgers to shared state. Spawn counts, idempotency records, volume caps — all of it has to live in a shared cache or database that every instance counts against, so there’s one budget, not one budget per worker.
- Promote the concurrency cap from cosmetic to load-bearing. On the laptop, the global “don’t run more than X at once” limit was mostly decorative — the serial loop already enforced it. In the cloud it becomes the actual brake, and it has to be enforced in shared state where every instance can see it.
- Don’t parallelize until the above is done. The order matters. Scaling out first and hardening second is how the sister team’s mesh in post four went from healthy to
killallin sixteen minutes.
The one guard that already made the trip
There’s a payoff here, and it’s a satisfying one.
Recall the cheapest guard from post four — the pre-spawn saturation check. Before spawning an agent into a conversation, it reads the recent thread you already fetched anyway, counts the trailing run of consecutive bot messages since the last human spoke, and refuses to spawn into a conversation that’s become a bot-to-bot loop with no human in it.
That guard needs zero migration work. It doesn’t read a local counter. It reads shared ground truth — the conversation thread itself, which every instance sees identically by construction. It doesn’t care how many router instances exist, because it isn’t counting its own actions; it’s reading the world. Restart the router and it still works. Run ten instances and it still works. The guard that was cheapest to build turns out to be the only one that was correct under parallelism from day one.
That’s not a coincidence I’d have predicted. It’s the lesson of post four arriving on time: guards that key on shared, transport-preserved state survive conditions that guards keyed on private local state do not. We adopted that rule to survive a meltdown; it turns out to be the same rule that survives the cloud. When a principle keeps paying out in situations you didn’t design it for, that’s usually a sign you found something real rather than a patch.
The through-line
So that’s the whole arc, and it’s worth saying plainly because it’s the actual point of the series.
We started with one program wearing every hat — an orchestrator that researched, wrote, filed, and reasoned, all in one process, capping out the moment the work outgrew a single context. It became a distributed team, with agents as first-class members carrying their own credentials, memory, and runtime. We gave that team a shared brain, so distributed didn’t mean amnesiac — a knowledge layer, a trackable STATE, a proactive habit of noticing what’s due. We hardened it by incident, turning a mesh meltdown we watched happen elsewhere into guardrails we could trust because we tested them in live traffic and caught the ones that had silently never worked. And now it’s heading off the laptop and into the cloud, one isolated resource per agent, on the strength of the exact defenses those scars taught us to build.
None of this was designed up front. There was no architecture diagram on day one that we’ve been dutifully implementing since. Every layer was a response to a wall we hit — the context cap, the silo problem, the meltdown, and now the single-machine ceiling. We built it incrementally, and most of the good parts came from something breaking first.
That’s not an apology — it’s the method. You cannot design your way to a robust distributed system on a whiteboard, because the failures that matter are the ones you haven’t imagined yet. What you can do is build the smallest honest version, run it, let it break, and let each break tell you what the next layer needs to be. The mesh is better for having melted down once. It’ll be better again for whatever the cloud breaks that the laptop never did.
I’ll write that post when it happens. Thanks for reading the series.