← All lessons

The Human Is the Race Condition

Everyone is watching the wrong failure

The conversation about AI coding agents is mostly about the agents. Do they hallucinate APIs? Do they write code that looks right and isn’t? Do they need a reviewer in the merge gate? All fair questions, and I’ve written about some of them.

What nobody warned me about was me.

Once I was running agents in parallel (Claude Code in one terminal, Cursor in another, Copilot in a third, each in a different repo or a different branch of the same one) the weakest component in the system stopped being the model. It was the person in the middle, holding the only copy of which session was doing what, and answering their questions from memory.

At that scale I started giving the wrong agents the wrong answers. The output of the work now included human error, not just potential AI slop. And the human error was harder to see.

Why it happens

None of this is a character flaw. It falls straight out of how these sessions look and behave:

  • Every session looks the same. A terminal with a prompt and a question. A chat pane with a prompt and a question. Nothing on screen says “this is the billing migration on feature/invoices” in a way your eye catches when you tab over in a hurry.
  • Each one is waiting on you. Agents run until they need a decision, then they stop. With six of them, something is always blocked, and the pressure is to unblock it fast.
  • The context lives only in your head. The agent knows its own task. You’re the only one who knows all of them, and which one is which.
  • Switching costs are invisible until they aren’t. Each switch feels free. You don’t notice the cost until the time you answer a question about the API contract in the session that was asking about the database index.

That’s a race condition in the textbook sense: shared state (your attention), multiple writers (the agents), no lock, and correctness that depends on timing you don’t control.

The answer was right. The agent was wrong.

Here’s what makes this worse than slop. An agent can’t tell that your answer was meant for someone else. It asked a question, it got a reply from the person it trusts, and the reply was coherent. So it does exactly what a good agent should: it takes the instruction seriously and executes it well.

The result is good code built on the wrong premise. It compiles. The tests it wrote pass. It reads cleanly. Review is built to catch bad code, and it does that reasonably well, especially with a machine gate in front of it. What review doesn’t catch is a well-made change that answers a question from a different task. The reviewer sees a coherent diff, not the conversation that produced it.

Slop at least looks like slop sometimes. A misrouted answer looks like intent.

Discipline doesn’t scale

Every one of those fixes has the same shape: it asks the person to be more careful. That works at three sessions and quietly stops working at eight, which is exactly when you need it. You can’t solve a concurrency bug by asking one thread to try harder.

How to find yours

Count the places an answer can go. If you have N agents open, you have N inboxes, and you are the router. Every one of them is a place to type the right thing into the wrong box.

Look at what a question carries. When an agent stops and asks you something, could you answer it correctly with no memory of the session? If the question doesn’t say which repo, which branch, what it’s trying to do and what the options are, it’s relying on your head for the rest.

Check whether decisions leave a trace. If you answered something important an hour ago, can you find what you said and which agent you said it to? If it only exists in a scrolled-off terminal, nobody (you included) can audit the routing later.

Look for overlap you’re preventing by memory. Two agents touching the same files is fine right up until it isn’t. If the only thing keeping them apart is that you remember not to send them the same task, that’s a lock held in your head.

The fix is structural

What finally worked was taking the routing out of my head and putting it in the system:

  • Every question carries its own context. Which repo, which branch, what it’s asking, why, and the options, with a recommendation. A question you can answer cold is a question you can answer correctly.
  • One front door for answers. Not N terminals. One place where questions arrive and answers leave, so there is exactly one box to type into.
  • A question that needs the human becomes a recorded decision. It’s held, answered once, and routed back to the agent that asked by the system, not by your memory of which tab it was in.
  • Each workstream owns a scope. An agent working in one area can’t silently edit another’s. Collisions become a refusal you see, not a merge conflict you find tomorrow.

What I built

That list is roughly the design of uBixShepherd, which I’ve been building and now use daily. It’s a coordinator over a workspace of repos. Work happens in lanes, each with a branch and a scope it’s allowed to change. Agents (Claude Code, Copilot, Cursor, OpenCode) get started inside a lane. When one needs something that’s my call, it doesn’t wait in a terminal for me to wander by; it raises a decision with its context attached, Shepherd holds it, and I answer it in one conversation with a front desk instead of hunting for the right window.

This lesson was drafted that way, in a lane, by an agent that could only touch this one file.

The routing layer can fail too

It would be a neat story if building the tool made the problem go away. It didn’t, and the day this was written proved it. Shepherd took an agent’s own question and echoed it back to that agent as my “answer.” The agent, reasonably, acted on it.

Same failure, new location: a reply delivered to an agent that couldn’t tell it wasn’t from me. What was different is how it got caught. Answers are recorded and visible, so the mismatch was right there to see: the “answer” on record was the question. Nobody had to remember anything.

That’s the real point. The goal was never a router that can’t make mistakes. It’s routing you can check, instead of routing you have to trust, whether the thing you’re trusting is your memory or the tool’s.

The principle

Once you run agents in parallel, the human is the shared resource, and an unlocked shared resource is a race condition. Don’t fix it with discipline. Make every question carry its own context, give answers one front door, record every decision, and make the routing something you can check after the fact.

← All lessons