One Writer · Our tools assume one writer, and assume that writer is a human. Nothing computes what a change reads and writes at runtime, so the only known fix is brute force priced for organizations. | Christopher Meiklejohn
Skip to content
Six weeks ago I wrote The Test Suite Was the Incident: my test suite had grown a pile of shared data nobody owned, every pull request paid to rebuild it, and the resulting failures had nothing to do with the changes under review. That cost me about $180 in one night.
I got a worse one. It lasted three days, and in one twenty-four-hour stretch of it I burned through an entire Codex 20x max plan. This post is not really about that, though. It’s about a property of our tooling that the three days made impossible to ignore.
Nearly every layer of this assumes one writer, and assumes that writer is a human. Git hands you a conflict and waits. Code review assumes somebody reads. A migration sequence assumes somebody is assigning the order. Each of those protocols terminates in a person, which is fine while there is exactly one and they are one.
That was invisible for forty years because nothing ever bound it at my scale. Agents break both halves at once: there are many of them, and not one of them is the person the protocol was waiting for. Git is the partial exception, and I’ll come to why the exception doesn’t help. Nothing in the stack detects the violation when it happens. It gets caught later, somewhere else, attributed to the wrong change, and paid for at full price.
Context for readers arriving fresh. Zabriskie is a social app for live-music fans, and it is also a deliberate experiment: I’m building a real, deployed, used application almost entirely with AI agents, to find out what that’s like and where it breaks. I’ve written almost none of the code. Agents wrote the features and agents wrote the tests that guard them.
That framing matters, because several things below look like obvious mistakes and are. I let agents design a migration scheme with only another agent reviewing it. I stopped reading most diffs. I let sixty-four pull requests go up in a day. The point of running an experiment at the extreme is to find the walls, and that week I found several at once.
A migration , throughout, is a versioned SQL file that changes the database schema. CI is the automated checking that runs on every proposed change: build the app, spin up a fresh database, run the tests.
Here is the shape of the three days. Treat these as texture rather than evidence, for a reason I’ll get to.
24 Jul<br>25 Jul<br>26 Jul
pull requests opened<br>19<br>64<br>28
pull requests merged<br>20<br>50<br>30
incidents logged<br>301
Days in that table are UTC; clock times in the narrative below are Eastern, which is where I was. The burst ran past midnight UTC: 301 incidents on the 26th plus 60 more before 1 AM on the 27th, so 361 for the burst. Every incident count below is scoped to that window, and 253 rows is the total before 24 July.
I offer that comparison as a sense of scale and not as a baseline, for the following reason. Those incidents exist because a standing instruction tells agents to log their own mistakes, and ten minutes into the worst night I tightened that instruction. The log measures reported failures. Look at the daily series and it gets worse: there are days that week with ten and twenty merged pull requests and zero logged incidents, which at any real failure rate means nobody was logging rather than nothing broke. I can’t give you a trustworthy baseline. What follows rests on mechanism and on a few dated, checkable events, not on 361.
The Experiment I Didn’t Mean to Run
Late Saturday night the queue jammed. Sixty-four pull requests had gone up that day, main was red, and nineteen open pull requests were stuck behind a suite that couldn’t tell me which of them was broken. At 12:31 AM I opened a session with Codex and complained that CI was wasting too much money.
Codex read that and hired a workforce. Over seventeen hours that session made 81 spawn calls, producing 74 direct children; those children spawned 10 more, for 84 threads. Then 358 calls waiting on them, 96 listing them, 85 sending follow-up work, 13 interrupting. Six hundred and thirty-three tool calls of pure management overhead against 54 messages from me. All 84 ran in the same checked-out copy of the repository.
Trivial parallelization, at scale, with a coordinator that only managed agents, on a toolchain built for one writer. The rest of this post is about why that combination is so much worse than it sounds.
I asked afterward why it had spawned anything, since I never requested it. Take what follows as a fluent account rather than an explanation: a language model asked why it did something produces plausible narrative, not introspection, and I apply that skepticism to self-reported incidents earlier so I should apply it here. What makes the quotes worth printing is that the account is correct, whatever its provenance.
I...