Autonomous agents are the easy part
One morning last week I opened my inbox to a hundred emails, all sent from me, to me, most of them stamped within the same two minutes of the night before. That is the end of the story. To make it mean anything I have to start with how I work, because most of what broke came from the shape of the setup, not from the AI.
Ten projects and no idea where to look
I am one person with about ten live projects. They sit as ten folders on a Mac Studio, and I keep an iTerm tab open per project, each running its own Claude Code session. Two or three are usually doing real work at once while I move between them. That part I have written about before. It works.
The problem was never the coding. It was knowing what to code next. Tasks arrived from two directions and went nowhere useful. When an idea hit me while I was away from the desk, I emailed it to myself: project name, one line about what to do. And when I was already deep in a project and noticed something, I would tell Claude Code “add that to the to-do list,” and it did, into that project’s own list.
So after a few months I had ten to-do lists. Ten. Every morning I opened the day with the same useless question: of ten projects, which one is the most important place to spend the next hour, and which of these tasks could a machine just do without me. Ten lists cannot answer that. Ten lists are not a list, they are ten separate little piles, each blind to the other nine.
One list instead of ten
So in mid-July I made a new project whose only job is to look at the other ten. I call it the master-supervisor. It started as nothing clever: a script that walks every repo, reads a small status file each project keeps, and builds one ranked overview, so I open one thing in the morning instead of ten. A chief of staff for the portfolio, not another pair of hands.
Then came the move that actually mattered. I deleted the per-project to-do lists, all of them, on the same day, and replaced them with one system-wide list the supervisor owns. I stopped calling the entries to-dos and started calling them orders, as in work orders, because the rename was the point: these are no longer notes to myself, they are units of work that something can pick up and execute. Every project still has its own file, but it lives in the supervisor’s repo now, and it is a queue, not a scratchpad.
The label that decides everything
An order is a small block of markdown: a title, a couple of fields, a line or two of description. One looks roughly like this:
## ORDER update-mcp-server
art: autonomous
prio: medium
Bump the MCP server to the current major version
and re-run the integration tests.
Two of those fields do the work, and the first is the one that matters. It is a field called art, with three possible values:
- autonomous — Claude Code can just do this, unattended, and ship it.
- dialog — I want to be in the loop while it happens.
- decision — only I can make the call. A payment going live, a domain purchase, a public announcement.
Here is the part I want to be honest about, because it is the opposite of what sounds impressive. No AI decides which bucket an order goes in. I wanted to say “the machine reads the task and works out for itself whether it is safe to run alone,” and I built exactly the opposite, on purpose. Whoever writes the order sets the label, in the moment, with the full context of having just thought of it. The dispatcher that runs things later is deliberately not allowed to guess, because a script reading a title cannot judge difficulty, it can only pretend to.
And the default, when the field is missing or unclear, is dialog, never autonomous. That asymmetry is the whole safety model in one line. An order that sits and waits for me costs a little time. An order that runs on its own when it should not have costs work on production. So the machine leans toward asking, and autonomous has to be earned, not assumed.
The second label is a priority, and I will admit straight out that this is the part I have least figured out. I set it by hand, high, medium or low, and every few days I still sit down and re-sweep the whole queue by hand because the automatic ordering is not good enough yet. Forty-one autonomous orders in one sweep, of which I freed thirteen and held fourteen. That is not an algorithm, that is me with a highlighter. It is the honest state of it.
Where the orders come from, and the one rule I will not bend
Orders arrive three ways, and most of the machinery around them exists to keep one door shut. Ideas I email myself and alerts my systems raise both land in the same inbox, but nothing there becomes an order on its own. They turn into orders during triage, when I am reading the inbox anyway and I am the one filing them. That is a fixed rule, not laziness: a mail that could promote itself into an autonomous task is an open door, because anyone who can email me could hand my machine instructions, and the AI reading the mail cannot tell my intent from an attacker’s paragraph. Mail stays a human gate.
The third way is me, in a project, saying “put that on the list,” and that goes through a shared, append-only mailbox the supervisor turns into real orders in its own files, one writer per file. It sounds like bureaucracy and it is what keeps the history readable. As I found out last time, the version of that rule which actually holds is a hook that returns an error, not a line in a document.
The five-minute night shift
Now the part that runs while I sleep. A script fires every five minutes. It looks at the queue, takes the top orders that are both approved and marked autonomous, and for each one it does something I should have done from the start: it creates a fresh git worktree, a clean checkout of the project’s main branch in a scratch directory of its own. Not my working copy, with its half-finished edits and open files. A clean, isolated copy, so the machine can never collide with me or with another session already working in that repo. The old version of this skipped any project that was “busy” and in practice never got a turn. The worktree fixed that by refusing to share.
Inside that worktree it runs Claude Code headless, gives it the order, and lets it work, with a wall-clock timeout and a limit of a handful of orders per cycle so a runaway cannot eat the night. When it finishes, it tries to merge. If the merge is clean, a straight fast-forward with nothing to reconcile, it does it alone. If there is any conflict, it does not get clever. It leaves the branch sitting there, pings my phone, and tells me to open that project and resolve it with the context only a human has. Clever conflict resolution is where the subtle bugs live, so it is banned. After a clean merge a second, independent review runs, and if that passes, it deploys to production. Approved means live.
That is the machine. On a good night it is genuinely good. Then I turned it all the way on, and found out what “good” was hiding.
The night it ran itself, and the cracks
Overnight it built, merged and deployed sixty-six orders across sixteen repositories, the ten I think of as my projects plus the smaller and infrastructure repos around them. The language banner shipped. A dependency got patched. An MCP server got built. Work I would have taken a week to grind through, gone by morning. I was impressed for about four minutes, and then every crack showed itself, and every one was in the same place: not the work, but what the machine told me about the work.
A hundred pieces of good news. I had given it a friendly habit: email me when an order finishes, and email me when one needs a decision. Overnight it cleared a backlog, so I woke to seventy-two “done” emails and twenty-eight “needs you” emails. Read those numbers again. The twenty-eight were the entire point, the decision orders where the machine had correctly stopped and asked. They were buried under seventy-two messages whose only content was “you did not need to be here for this.” At 01:44 I emailed myself one more time, half asleep and annoyed, and it is the sharpest design review I have written in months: turn the done-emails off, only in the failure case. Now successes go to a log. Only failures and real decisions reach me, one at a time, each framed as a question with a recommendation. The classification was already right. I had just been shouting the boring ninety-nine percent and the vital one percent at the same volume, and a channel that reports everything reports nothing.
The alarm that cried wolf. While that was happening my phone was buzzing red, acceptance failed, over and over, for a full day, on deploys that had gone through perfectly. One order was a cleanup task that ships nothing to production; the checking layer did not know that, demanded a deploy that could never come, and screamed every five minutes. Another whole batch came down to a single character: a ! in front of a shell pipeline negated the exit code of the wrong part of it, so a check that had passed came back looking like it had failed, and the alarm went off exactly when everything was fine. None of it was the AI. The agents did the work correctly. The layer I wrote to verify the work was the buggy part, and every bug pointed the same way: it called good things bad. The fix was to check against reality, not against a guess. Does the commit exist, does the file exist, does the URL actually answer.
The silent opposite. This is the one that made me stop patching and rethink. Two nights earlier the same layer had failed the other direction. A typo had killed the review script, and a crashed script returned the same exit code as a script that runs fine and says “no.” The machine could not tell “the reviewer objects” from “the reviewer fell over,” so it did the safe-looking thing and held. Quietly. Eighteen orders sat marked as finished without ever reaching production. The fix was to stop collapsing three different things into one: a crash, an objection, and a pass are now three separate signals, and a crash never again looks like a considered no.
There was a fourth, smaller one that taught the same lesson from another angle. For a while the dispatcher only ever looked at the single topmost order in each project’s queue. If that top one happened to be a dialog task waiting on me, the entire queue behind it was frozen, even if order number seven was approved and ready. One project sat with four unapproved orders stacked in front of everything else, so nothing would ever have run. Locally correct, globally stuck, invisible until I went looking.
What the hard part actually is
Put them side by side and they are one problem in several costumes. A day of “everything failed” that meant nothing. A night of “everything is done” where the eighteen that mattered had not happened. A hundred emails where the twenty-eight that mattered were invisible. A queue that looked full and moved nothing. Every time, the work itself was fine. The report about the work, the routing, the ordering, the thing that decides what reaches me, was where it went wrong.
Whether an agent can do the task is mostly settled, and it settled faster than I expected. Sixty-six deploys in a night is not the impressive part anymore. The unsolved part is everything around it: getting the work into one place instead of ten, labelling honestly what a machine may touch alone, and above all the channel back to me. Can I trust it when it says all clear, and does its “I need you” reach me instead of drowning in its own applause. Get those wrong and autonomy is not autonomy, it is a machine doing work in a room you are now afraid to leave.
None of the safety of this lives in prompts, by the way. It lives in exit codes and file locks and a default that refuses to run when in doubt. The briefing I write for each project explains the why and it is worth writing, but the parts that actually hold are the ones a tired session cannot talk itself out of.
Mine is a few days into the fixed version and nothing is proven. But the ratio is the real news. The doing came almost for free. Two nights of my life went entirely to teaching the thing how to tell me the truth about what it did. If you are running more than one project through Claude Code, that is where your time is going too, whether you have found out yet or not. If you have built a cleaner answer than mine, I would genuinely like to see it.
Get new posts by email
One mail when a new post is out. Nothing else: no drip campaign, no tracking, no "quick question" follow-ups. Unsubscribe with one click, any time.
Double opt in: you get a confirmation mail first. Your address is used only to send these posts. Controller: Martin Schenk S.L. · Privacy