Nobody Wrote a Bug — Your Coding Agents Just Found a Terrible Equilibrium
Four coding agents, same monorepo, same git lock, all reaching for it within seconds of each other. Three back off. The fourth doesn't — it retries, exponentially, against a lock held by an agent that already crashed. Forever. Nobody shipped a bug here. The system just found its equilibrium, and the equilibrium was ugly.
That's the pattern I want to name: the moment you run more than one coding agent against shared infrastructure — CI runners, a model API's rate limit, a git lock, a staging database — you've stopped building a pipeline and started running a game. The agents aren't cooperating so much as Nash-equilibrating: each one picks its best response to what it expects the others to do, and the whole pack converges on a stable state that's stable precisely because it's collectively wasteful.
The game hiding inside your CI queue
Every shared, finite, contested resource your agents touch is a congestion game, whether anyone designed it that way or not:
- ▹CI runners — finite parallel slots, agents queue or race for them
- ▹Model API rate limits — a shared token/request budget across every agent hitting the same key
- ▹Git locks and branch protection — mutual exclusion on the same ref or working tree
- ▹Package registry pulls — npm/pip rate limits shared across concurrent installs
- ▹A shared staging or dev database — schema migrations, seed data, test fixtures all colliding
This is the same shape as the congestion games traffic theorists have studied for decades — Braess's paradox, where adding capacity can make throughput worse — and the tragedy of the commons. The payoff each agent is optimizing for ('finish my task fastest') is locally rational and globally corrosive the moment enough agents share it.
Nash equilibrium isn't cooperation, it's a truce
A Nash equilibrium is just the point where no single agent can do better by changing strategy alone, given what everyone else is doing. It says nothing about whether that point is any good. Four agents all retrying-with-backoff on a contended lock can settle into a rhythm that's perfectly stable and still starves the CI queue. That's the part most 'multi-agent orchestration' pitches gloss over: agents converging on a fixed point is not the same claim as agents converging on a good fixed point.
What actually fixes it: conventions, not smarter agents
There's a result from multi-agent RL research on Hanabi that's directly relevant — Hanabi is a game built entirely around partial observability and near-zero communication, structurally close to what happens when your agents can't see each other's internal state. A paper on augmenting the action space with conventions (arXiv:2412.06333) found that giving agents a shared, pre-agreed protocol for interpreting each other's actions improved both self-play and cross-play performance — and the gain came from the convention layer itself, not from making the individual agents smarter. Without a shared rule, agents fall back on expensive guesswork about what everyone else is probably doing. Which is exactly the failure mode in my opening story: two agents independently reasoning about lock state instead of just being told.
A broader survey of communication in multi-agent deep RL (arXiv:2203.08975) makes a related point: agents that send each other messages — broadcast or targeted, shaped by whatever constraints you build in — coordinate and perform better than agents that don't, and the survey lays out a set of dimensions for classifying how existing systems design that layer. Translate that to your infrastructure and the implication is identical to Hanabi: you have to design the channel on purpose. You can't assume agents will infer the game state from nothing and land somewhere good.
Translated into infrastructure you already have:
- ▹A lock-announcement channel — broadcast 'I'm about to grab this' before the acquire, not after
- ▹Leases with TTLs instead of indefinite holds, so a crashed agent's lock expires instead of orphaning the queue
- ▹Exponential backoff with jitter, not synchronized retry intervals — synchronized retries are exactly what turns four polite agents into a retry storm
- ▹Concurrency groups scoped per resource, so agents queue predictably instead of racing
# GitHub Actions: turn implicit racing into an explicit queue
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: falseThe equilibrium assumes agents can trust each other's signals — they often can't
Game-theoretic models like this usually assume common knowledge — every player can reliably observe the state of the game: who holds the lock, how many tokens are left. Multi-agent coding setups violate that constantly. Research on establishing shared query understanding in open multi-agent systems (arXiv:2305.09349) shows agents with no prior shared context need real protocol overhead just to confirm they're even interpreting the same shared context the same way. Cooperation isn't a free byproduct of putting agents in the same room — it's a negotiated, verified equilibrium, and different coordination policies get there at wildly different cost. A more recent proposal to give agents decentralized identifiers and verifiable credentials (arXiv:2511.02841) exists precisely because agents have no built-in way to authenticate each other's claims right now — and its own evaluation flags real limits once you hand an LLM sole responsibility for that trust decision. If your agent can't verify the lock-holder it's waiting on is still alive, or that another agent's reported rate-limit budget is honest, the 'game' stops being about splitting a scarce resource and becomes about whether the observed game state is even true. Most Nash-equilibrium framings quietly assume that problem doesn't exist.
Humans in the loop don't play the payoff-maximizing move either
Here's a wrinkle I want to state honestly instead of smoothing over: this next bit comes from human-agent cooperation research on social dilemmas (arXiv:2201.13448), not from CI infrastructure, so treat the transfer as a hypothesis, not a proven mechanism. But it's provocative enough to flag. In that study, humans deciding whether to keep working with a given agent — including the option to bail and play the next round solo — were driven more by how warm and competent the agent seemed than by its actual objective performance in Coins, a two-player social dilemma about collecting shared resources. If that holds even loosely for engineering teams, it means someone on your team might keep re-triggering the agent they 'trust,' long after a faster or more resource-efficient one is sitting right there — which breaks the clean assumption that the system converges on the payoff-maximizing equilibrium at all. Humans are unmodeled players, and they can walk out of the game whenever they want.
It's probably not one equilibrium — it's a cycle
The cleanest version of this story says the system settles into one stable point and stays there. Evolutionary game theory says otherwise. A study combining analytical dynamics, agent-based simulation, and human-subject experiments on a four-strategy game (arXiv:2203.14669) found consistent cyclical dynamics across all three methods — strategic populations don't always converge to a fixed point, they can just keep cycling. Again: general game theory, not a study of coding agents. But the pattern matches what teams describe anecdotally — an aggressive-retry strategy dominates while the queue is contested, gets throttled once it starves everyone else, a cautious backoff strategy takes over for a while, and then aggressive resurges once the queue clears. 'Nash-equilibrating' might be the wrong verb tense. It's less a destination than an oscillation you should be watching for, not a state you fix once and forget.
What to actually build this week
- ▹Instrument for retry storms and lock-wait time per agent, not just task success/failure — the equilibrium is invisible until you graph it
- ▹Add a lease/TTL to every lock your agents touch, so a crashed agent can't hold the whole game hostage
- ▹Give agents an intent-broadcast channel before they touch contended resources — cheap coordination beats expensive inference every time
- ▹Rate-limit and queue per resource explicitly (concurrency groups, semaphores) instead of letting agents discover contention by failing
- ▹Watch for oscillation, not just steady-state failure — a strategy that looks fine this week can be building toward next week's storm
That opening scenario wasn't a bug in any one agent. It was rational players finding a stable, terrible answer to a game nobody designed. You don't fix that by making the agents smarter — the Hanabi result says as much directly. You fix it by giving the game rules.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.