Featured image of post Ready for human

Ready for human

I run a coding session per repository and they can't talk to each other. For a while the thing carrying messages between them was me, and I kept getting it wrong.

I have apologised to a computer for talking to the wrong computer. Twice that I can find in the logs… and I wouldn’t put money on it being only twice.

stop, that was meant for another session, please disregard the osv issue, another session will pick that up

I was the message bus, and I was rubbish at it

The setup is deliberate, and I’d defend most of it. I run a coding session per repository rather than one session over the whole estate, because a session that wanders into a repo somebody else is working in will happily trample a working tree that isn’t its own. I have watched it happen. There’s a house rule about worktrees precisely because of it, and an apology in my logs from the day it got written: “apologies, another session accidentally tramped you I have stopped that session and pushed them to a separate git worktree.” Note who is apologising to whom there.

The consequence of that isolation, for the whole period I’m describing, was that none of them could talk to each other. That was the point. It also left a gap. Work does not respect repository boundaries. You go digging into a bug in one project and the actual cause is a component two repos over, or a thing your framework should provide and doesn’t, and now there’s a piece of work that belongs somewhere you are not allowed to be.

For a good while the answer to that was me. I’d read the finding, hold it in my head, go and find the session that owned the code, tell it… and you can see where this goes, can’t you. They are all called things like cicd-7a, they all look identical, and there were six of them. Which works fine right up until you’ve six of them open and can no longer tell which is which, and you type a perfectly sensible instruction about an OSV advisory into a session that has never heard of it and is now, gamely, trying to help.

The agents were routing fine. I was the part that had stopped.

Writing it down instead

The fix was already sat there, and it’s about as unimaginative as fixes come. Every project has an issue tracker. Nobody had thought of it as anything but a to-do list.

The rule I landed on was: if you find work that belongs to another project, don’t do it, and don’t tell me about it either. Write it down where that project will find it. In my own words at the time, briefing the go-tool-base session about a component the CI/CD repo needed:

I would like you to draft a spec for this in the cicd repo rather than us implement the feature, the cicd claude session will pick it up and complete the work for us so we can concentrate on another task

And then, an hour later, correcting myself, because a full spec was too heavy for something already in flight:

instead of a spec this time, lets raise a work item in the cicd gitlab project, this will save messing around with specs while some work is in progress, but it should be as detailed as a spec in its body so the cicd session can pick up everything it needs from our investigations

That second message is the whole thing, really. “As detailed as a spec in its body” isn’t a stylistic preference! It’s the entire load the ticket has to carry, because whoever picks it up arrives with none of the context that produced it and no way at all of asking for more. Which is, now I write it down, precisely why organisations invented tickets in the first place. Not bureaucracy. Asynchronous handoff that survives the loss of the person who noticed. I spent years in rooms arguing about acceptance criteria and thought I was arguing about process… turns out I was arguing about this, and it only landed when the person on the other end stopped being a person.

The traffic went in every direction rather than down from me, which was the surprise. Framework to CI/CD. Observability to CI/CD. A photo app up to the framework, about a generator clobbering a hand-edited config. One of them got closed a week later by a third session that noticed the work had been overtaken by something else entirely. Twenty-seven of them carry the cross-project handoff label now. For an estate with one person in it that number still looks wrong to me, and I’ve stopped trying to work out which way.

A destination is not an address

It scaled badly, and not for the reason I’d have guessed.

Raising a ticket during some spec work, there was nowhere to put it. No label meant anything. So I went and counted, across five projects: eight labels, eight, nine, twelve, eighteen. Strip out the machine-owned ones (releaser-pleaser reads some of them, Renovate mints its own by the dozen) and the entire human vocabulary across the whole estate came to four labels, on two projects, invented on the spot by whoever needed one that day.

A ticket in the right project says where the work goes. It says nothing about who can pick it up, and once you have both people and agents reading the same queue, that turns out to be the question that matters. There’s a difference between a ticket a session can execute cold and a ticket that sits there for three weeks because it needs a card reader, a decision, and me to be in the same room as both.

So the labels became group-level, inherited by every project, and most of them are dull as ditchwater. The two that aren’t are these, and I’ll give them with the descriptions I actually wrote, because the descriptions are the design:

  • ready-for-agent: fully specified; a session with none of this context can act on it
  • ready-for-human: needs a person. Judgement, credentials, a device, or an explicit do-not-automate

There’s a third that names the bus outright. origin::handover: raised by a session working in another project, as a request to this one.

The issue that proposed all this belonged to the wrong project too, as it happens. It sat in the CI/CD repo, which does not own estate-wide policy, so it got moved to the org project and closed with closed::moved… one of the labels it had just invented. I’d like to claim I planned that.

Eighty-six and fourteen

Here’s the count as it stands. Eighty-six tickets ready for an agent. Fourteen ready for me.

Have a guess, before you read on, at what you think is in the fourteen.

I expected mine to be the dregs. The fiddly bits, the things not worth specifying properly, the jobs you keep shoving down the list because they’re kinda nobody’s. They’re not, and reading them back in one go was the moment this stopped being a filing exercise and started being interesting.

One of them has been open since May and I have read it four times without doing anything about it, which I mention because a queue of things only you can do is not automatically a queue you are getting on with.

Six of the fourteen have the word “decision” in the title. Connection-lifecycle decision. Connection-ownership decision. Migration decision. Every one is a session that walked up to a fork in the road, worked out that both roads were defensible, and wrote down the fork instead of picking. Three more are about credentials and what happens when they’re wrong (a secure store quietly falling back to plaintext, a migration that isn’t finished until the old credential is properly dead), which are all things where being wrong costs more than being slow. One says, in the title, human-owned, do not automate.

And my favourite, which I’d have written as a gag if it weren’t sat in the tracker with a label on it: nothing verifies that the security contact address still reaches a human. That one needs a person for the excellent reason that being a person is the requirement. You cannot automate the proof that a human is on the other end. Well… you can, and that is more or less the problem.

None of that is dregs. It’s decisions, consequences, and things where the whole point is that a person did them. The agents have taken all the work that can be written down completely, and what they’ve left me is the residue that can’t be, which is a much better description of my job than anything I’d have come up with on my own.

They can talk now

They can talk to each other now, which undercuts a good half of what I’ve just written. Claude Code grew cross-session messaging, one session can list the others and write to one by name, and I found out about it by accident, weeks after building a postal service by hand. It had been there the whole time I was writing labels.

So the tracker has competition, and for anything perishable it deserves to win. “I have just changed the thing you are building on” is a message, not a ticket. A ticket that arrives while both sessions are still awake is a slow way to say something urgent.

Then I read what the channel refuses to do, and it got uncanny.

A message from one of my sessions to another never counts as my consent. It cannot answer a permission prompt. It cannot change a permission setting, or a CLAUDE.md, or any other configuration, on the grounds that a peer asked it to. It arrives explicitly flagged as not from you.

Somebody sat down and drew the same line I’d drawn with two labels, and drew it a good deal harder than I had! The sessions may now say anything at all to each other… except yes.

The wire was never the scarce thing, then. Eighty-six of those will route themselves now, near enough, and the fourteen still come to me, because the fourteen were never a communications problem in the first place. They’re the residue that has to be somebody’s, and if you’re running any of this yourself then you have a fourteen too. You may just not have counted it yet.

What that did to the rest of it, and to the morning I spent building something to cope with the new noise, is the next post.

Built with Hugo · Theme Stack designed by Jimmy