Run Dozens of AI Agents on One Machine — No Meltdown

Lorenzo Wynberg7 min readEngineering
A queue of AI agents waiting for permits from a single machine-wide compute budget

Picture the worst thing you can do to a laptop: open five terminals and run pnpm test in all of them at once. Now do it again for every project you have open. Now let each of those runs spawn three more.

That's not a stress test. That's a quiet Tuesday for a fleet of AI agents in Trinity — and the machine doesn't even get warm. Here's the small, unglamorous piece of plumbing that makes that true.

The math that should melt your laptop

Trinity's parallel execution runs up to five agents at once — each working a different story in its own isolated git worktree. That number sounds modest until you multiply it out:

  • It's five per project. Open three projects and you have three fleets.
  • It's five per person. Every teammate runs their own coordinator on their own machine, claiming jobs from the shared plan.
  • And each agent is encouraged to spawn sub-agents of its own to parallelize the work inside a single story.

So on one developer's machine, at one moment, you can easily have a dozen-plus agents live — and every one of them eventually wants to do the same thing: run the full build, the type-checker, and the test suite to prove its work is green before it opens a PR.

That's where a naive parallel system dies.

Why parallel agents murder your machine

The agents themselves are cheap. They spend most of their lives waiting on a model — a few hundred megabytes and almost no CPU. The killer isn't the agents. It's the checks.

A whole-repo pnpm gate — format, lint, typecheck, and the full test suite — pins every core, eats gigabytes of RAM, and runs for minutes. That's fine once. Fire ten of them at the same instant and the machine doesn't fail loudly. It does something worse: it thrashes. The scheduler thrashes, memory starts swapping, every run that used to take ninety seconds now takes eight minutes, timeouts cascade, and agents start abandoning work that was actually correct — they just never got compute to confirm it.

warning

"More agents" sounds like "more throughput." Past your machine's real compute budget, more agents means less — every extra heavy run makes every other one slower, and the whole fleet grinds to a crawl together.

The fix isn't smarter per-agent scheduling. It's the opposite: one rule that every agent has to obey.

One machine, one budget

Meet run_queued.

run_queued is the way an agent runs a heavy check in Trinity. Agents don't shell out to pnpm test in Bash — they can't. The tool policy that defines what each agent may do only grants them run_queued for builds, type-checks, and tests. There is exactly one throttled path to the CPU, and it can't be bypassed.

One path, enforced

Even if an agent tries to run the suite in Bash, the permission layer denies it. Every test, build, typecheck, and gate routes through run_queued — so the throttle is the only door to the machine's compute, not a suggestion an agent can route around.

Underneath, run_queued is a thin client. It hands the logical scope of what to check — a repo, a target, or the whole project — to the sidecar, which owns the worktree and runs the suite through a single, machine-wide permit pool. Every run takes a weighted cost out of one fixed budget and holds it until the run finishes, then releases it automatically.

Weighted, not counted

A flat "max three at a time" cap would waste the machine. Three one-second prettier runs shouldn't be able to block a real test suite, and one heavy build shouldn't count the same as a trivial format.

So the budget is weighted by how expensive the work actually is:

lib/constants.ts
// One machine-wide pool. Every heavy check takes a weighted permit.
const HEAVY_RUN_COST = 4; // a whole-repo build / test / typecheck / gate
const LIGHT_RUN_COST = 1; // a format or a single-file check
const RUN_BUDGET = 6; // default permits per machine (configurable)
 
// run_queued routes here instead of shelling out to Bash:
await withRunPermit(cost, () => runCheck(scope));
// Blocks FIFO until `cost` permits free, then releases on completion.
 

With a default budget of six, one heavy run (4) leaves just enough headroom for two light runs (1 + 1) to slip in beside it — but a second heavy run (another 4) has to wait for the first to free. The machine stays fully busy and never buried. Quick checks never starve behind slow ones, and the heavy suites take turns.

Shell out to Bashrun_queued
Who runs the suiteEach agent, on its ownOne machine-wide permit pool
ConcurrencyUnbounded — all at onceWeighted budget (heavy 4, light 1, cap 6)
Under loadThrash, swap, cascading timeoutsRuns queue; machine stays responsive
TimeoutsEach agent's problemOwned by the tool, run on the sidecar
FairnessWhoever starts first winsFIFO — light runs slip into the headroom

The part that makes it pleasant

run_queued blocks. When the machine is at capacity, the call waits right there for a slot to free. That sounds like a tax on the agent — but it's free.

The wait is off the agent's time budget, exactly like a permission prompt. The agent's turn stays open, it does nothing else while it waits, and it's never charged for sitting in line. The suite itself runs on the sidecar, in the correct worktree, so a slow or queued run is never the agent's timeout to manage — the tool owns timeout policy end to end. The agent passes logical scope ids (a repo, a target, the whole project), never paths, and submits a whole set of scopes in one call instead of looping, so they run cap-many at a time.

The agents never fight over the machine. They take a number.

One budget, every coordinator

The budget isn't per-agent or per-project — it's a single pool for the whole sidecar, shared across every project, every release, and every coordinator on the device. Two agents working in completely different projects draw from the same well, so the cap actually means something no matter how many fleets you have running.

It's read once from your machine's config at boot and frozen for the life of the process — resizing a live budget mid-run is a footgun, so it isn't allowed — and it floors to at least one heavy run, so the smallest machine can always make progress. On a beefy workstation, turn it up. On a laptop on battery, turn it down.

The unglamorous thing that makes the rest possible

The headline is "dozens of AI agents, in parallel." The thing that makes that headline true instead of merely aspirational is a boring permit pool that stops them from trampling each other on the way to the CPU.

It's the same instinct as everything else in Trinity. Per-story git worktrees so agents never collide in the filesystem. run_queued so they never collide on compute. Isolation all the way down — and a fleet that scales with your team instead of fighting it. You can watch it pay off on the metrics dashboard: cycle times stay flat as you add agents, instead of ballooning the moment the machine gets crowded.

This is the throttle behind the parallel execution we wrote about in The Ralph Wiggum Loop, evolved — the difference between a loop that runs one task and a system that runs your whole team's work at once.

tip

Want to see a fleet run without the meltdown? Download Trinity — available for macOS and Linux — and open three projects at once.