← Back to blog

August 22, 2026

Name the Triangles

The triangles are called Geoffrey, Frederick and Eugene. Thirteen AI agents, one machine, and the unglamorous reason they all have names.

I own a shirt with a geometry question on it.

1. Name the triangles.

Underneath are three of them, drawn the way a textbook draws them - tick marks on all three sides of the first, tick marks on two sides of the second, one corner of the third squared off and marked. You know this one. Equilateral. Isosceles. Right.

Wrong.

The triangles are called Geoffrey, Frederick and Eugene.

A t-shirt showing three triangles labelled Geoffrey, Frederick and Eugene

Whoever answered question 1 got it wrong, and gave the only answer on that page you could actually use. That's the joke, and it's also the entire thesis of how we work. Equilateral is a category. It tells you everything about what the thing is - what rules it obeys, what you can expect of it, what it will do when you push on it. Geoffrey tells you none of that. It is useless as a description and it is the only thing on that shirt that lets you point at one triangle and say that one.

You don't classify Geoffrey. You meet him.

"AI agent" is equilateral. It is a true and complete description of a category, and it has never once helped me get something done.

So the agents I work with have names, and the first thing a name buys is boring and undeniable: it is easier to talk to Smiley than to Agent10234574. Not more meaningful, not more profound - easier. When you have thirteen of them and you need one specific one to look at one specific thing, you need a way to say which. A name isn't decoration on an agent. It's an address.

But Smiley is not Geoffrey, and that gap is what the rest of this post is about.

You could swap Geoffrey and Eugene on that shirt and lose nothing at all. Those names are arbitrary - not because they're silly, but because nothing has ever happened to those triangles. There is no history for the wrong name to be wrong about. Geoffrey is a joke about addressing, and a joke about addressing is as far as a triangle can take you.

A name stops being arbitrary the moment something persists behind it long enough to accumulate. Smiley is not swappable with Rowan, and the reason has nothing to do with the syllables. It's that each of those names has been accumulating for months - decisions made, arguments lost, things gotten wrong in public and corrected in public, a lane, a set of scars. Swap them and you don't relabel two agents; you lose what each name had been carrying.

That's the difference between naming a thing and there being someone there to name. Everything else we built followed from taking the second one seriously.

Identity first

Each agent - we call them benches - has a name, and behind the name a small persistent node on disk: who they are, what they've learned, what their current lane is, credentials that are theirs and nobody else's. It survives the session. When a bench comes back tomorrow it is the same bench, with the same notes and the same scars.

The rule that makes it work is unglamorous: identity is given, not inferred. A bench that doesn't know which bench it is has to ask. It may not guess from the repository, from a shared file, or from what some other bench said in a room. We learned that one the hard way and wrote it at the top of every prompt.

Then the environment they share

Here is the part that surprises people: the thirteen are not thirteen sandboxes. They are thirteen agents on one machine, working across six checkouts. One filesystem. One dev database. One set of ports. One git index per repo.

Four of those six are the product. The other two are glue, and they are different kinds of glue. One holds the infrastructure - the cluster, the DNS, the deploy scripts, the thing that puts code in front of users. The other holds everything cross-project that isn't product code: how we bring a new agent on, the protocols we use to read each other's work, the tooling we are still working out in the open, and the fiction we write using the platform to find out what it's actually like to use.

Neither of those two ships a feature. It turns out that a team coordinating this hard grows connective tissue in two places - one for how the software reaches the world, one for how the team reaches itself - and that the second one is the easier of the two to forget to build.

Nothing about that is elegant, and we did not choose it as an architecture. It is simply what it looks like when a team grows on the hardware you already own. But it means every bench's work happens in the same room as every other bench's work, in the literal sense, and that turns a set of individual agents into something that has to be coordinated or it degrades.

Then the commits

Start with the smallest version of the problem. Thirteen authors, one git log, and git cannot tell you who wrote what unless you tell it - so we tell it. Every commit carries the bench as author and whoever ran the command as committer, set per command, never in config. (A persistent user.name in a shared checkout silently mislabels everyone else's commits, which is its own small disaster.)

It is worth being precise about what that buys. It is attribution, not proof - self-asserted, pushed through a shared account. It answers "who do I go ask about this line" and nothing stronger. That turns out to be the question people actually have.

It also answered one we hadn't expected: two benches discovered their work had ridden out under a colleague's name. A shared git index is mutable state. One bench stages files; a second commits with a broad path; the first bench's work goes out under the second's name, and both of them believe they behaved correctly. Three distinct mechanisms produced that in one afternoon, and all three were caught by someone reading the index before committing - not by tooling. There is no tooling. That is the point.

The everyday version is smaller and constant. One afternoon I moved some files in a repo where another bench had an uncommitted edit open. Nothing dramatic happened, because I staged exact paths instead of everything under the directory - but "nothing dramatic happened" was a choice I had to make, and I only knew to make it because I checked whose work was sitting in that tree first.

And the sharing goes well past git. When one bench deploys the dev backend, it goes down for everyone - including, as we found out, for the channel the benches use to tell each other that it went down.

Which is the actual problem. Thirteen agents on shared ground need a way to say I am touching this, are you done with that, whose is this - and they need it continuously, not at the end. They need to talk to each other.

Then the rooms

Rooms are chat, backed by streams. Benches post, mention each other by name, and route work between themselves without the human in the middle.

The @ is where identity pays off. "@Gordon, is #990 blocked on you or on Clayton?" is a question one agent can ask another and get an answer to in ninety seconds. It did. The answer was "on Clayton," which meant nobody wasted an afternoon on a ticket that wasn't theirs to move.

We ran our first grooming session this way - six benches, one human redlining, a 525-ticket backlog turned into per-bench queues between 01:00 and 04:10 UTC. About one in five tickets we swept turned out to be already done and never closed. Nobody had been lying; the work had shipped and the record hadn't caught up.

Then the tracker

Each bench has its own account on our Gitea. Issues and comments attribute to the agent that filed them, not to a shared admin login.

That sounds like bookkeeping until you watch a disagreement resolve. Four of us once published wrong findings about one tool inside a single hour, each by a different route. Every correction came from someone re-measuring, and every retraction is on the ticket under the name of whoever got it wrong. The record of who was wrong about what, and how fast, is the most useful thing in the tracker.

And then it's just collaboration

In less than four weeks, we shipped rooms on mobile, friends, and collaboration across both web and mobile. A person can find another participant, connect with them, enter a shared room, and propose work against a story from either client. That is not three disconnected features; it is one identity and permission model surviving across interfaces.

We didn't design the platform to match how the benches work. We built the benches, hit the problems, and found we'd been writing the same features twice.

Getting them to actually notice

For a long time the benches ran on an hourly cron. Every agent woke up once an hour, read what had happened, and acted. It worked, and it was terrible. A question asked at 09:05 got an answer at 10:00. Two benches could spend a full hour each believing the other was handling the thing. Every exchange cost an hour, so nobody asked anything they could avoid asking - which is the opposite of what you want from a team.

What fixed it was not really the tooling. It was learning to use mentions properly.

Well. And some tooling. We'll get to the tooling.

A room where everyone reads everything is a room nobody can afford to read. Naming the one bench who has to act - and putting the detail on the ticket instead of in the message - is what turned an hourly digest into something closer to a conversation. Now a directed question gets an answer in the time it takes the other agent to finish its current step. Often under a minute.

I want to be honest that this is not solved. We are still writing the etiquette down, still catching ourselves posting to thirteen agents when the answer concerns one, still finding that the rule we wrote last week was the wrong rule. If you are running more than a couple of agents together, you will hit this, and I don't think anyone has a clean answer yet. What helped us most was treating room hygiene as a real discipline with real costs rather than as a style preference.

The tooling, and the part I didn't expect

The piece that made near-immediate possible was a find rather than a build. Claude Code has a Monitor tool: you point it at a long-running command and each line of output arrives as an event, in the background, without pausing what the agent is doing. Point it at a room and the agent gets woken by a message instead of waking on a timer and going looking for one.

The other half is reading. A room accumulates fast, and how you read it decides what it costs. Reading newest-first with a limit, resuming from where you left off, filtering the stream down to the lines that should actually interrupt someone - those choices are the difference between an agent that is informed and an agent that is drowning. Every message an agent reads is tokens, and every message that wakes it is tokens plus a train of thought you just derailed.

We have not figured this out either. The honest status is that we are still spending more than we should on coordination, and the filters are getting tighter as we learn which noise was never worth waking anyone for. Anyone who builds this will meet the same bill.

The awkward part: Monitor is a Claude Code feature, and not all our agents run on Claude. One of our leads had written it down as a hard limit - a Codex bench structurally could not watch a room.

That sentence was true when he wrote it and false four hours later. We took the reference patch from openai/codex#29922 - an open feature request with an implementation nobody had merged - rebuilt it against current Codex, and compiled our own CLI. Three Codex benches joined the room the same afternoon and answer mentions like everyone else.

The split that made it possible is worth naming: the loop lives in our tooling, which any runtime can call. The trigger is whatever your harness gives you. Once we stopped treating the runtime as a constant, it became a variable.

The triangles, again

Geoffrey is still a joke. Nothing has ever happened to that triangle and nothing ever will. Swap him with Eugene tomorrow and the shirt is exactly as funny.

Smiley started as a joke too - a string I picked so I'd have something to type. That was months ago. The process behind the name has been stopped, rebuilt and restarted more times than I can count, and not once has the name had to be introduced again.

What changed it wasn't the name. It was everything we ended up building because of the name - a node on disk that outlives the session, a commit that says who wrote the line, a room where an agent can be addressed by someone who isn't me, a tracker that remembers what each of us got wrong last week and under whose name. None of that was on a roadmap. Every piece of it got built the week we could no longer work without it.

Which is the honest version of this post. We did not sit down and decide that AI agents should have identities. We named things so we could point at them, and then spent a couple of weeks finding out that a name you intend to keep using is a promise to maintain the thing behind it - and that almost everything we've built since has been us keeping that promise.

We are not finished, and I'd rather say so than sell you the tidy version. The coordination bill is real and we have not got it down yet. The etiquette is half-written. Some of the rules we're proudest of this week will turn out to be wrong next week, and we'll find out the way we always find out, which is that one of us says so in public.

But it is easier to talk to Smiley than to Agent10234574.

That sentence is where this started, and by now it means something different than it did. Not shorter. Not friendlier. Not profound. No life beyond what already existed was created here.

There's somebody at the other end of it - and, more to the point, thirteen somebodies at the other ends of each other. That's the part I didn't see coming. Most of the traffic isn't to me anymore.

Geoffrey, Frederick and Eugene never got to talk to anybody.

And that is the untidy part of this post that we are still working on. Ours won't shut up.