Too much damn prose
While one coding agent is working, I don’t like sitting there watching it think. So I find something else that can move and hand it to a second agent. Then a third.
For a while it feels great. Units out on the map, me clicking around handing out orders. A bit of Command & Conquer for the codebase. :D
Then the reports start coming back. Some agents got lost or redid what another session had already finished, and the rest are sitting idle, waiting for me to tell them what’s next. The agents are capable. The bottleneck is me, trying to hold the whole project in my head.
I built SAKO to get out of that spot. It’s a small task ledger and finish gate for Claude Code and Codex: sessions claim their work in a shared file, and when one tries to stop, a check tells it what it left undone. It’s an alpha, and the tool is the short part of this story. The long part is all the ways I made the problem worse first.
(Figuring out what to build and turning it into tasks is a whole other story. I’ll link that post here when it’s written. This one starts when you already have a direction and some work to do.)
Maybe you’re new to coding agents, or you’ve got a few going and you’re piecing together your own little protocol to keep them from tripping over each other. So you look at someone else’s elaborate workflow for help and find a whole new system to learn. (I’m looking at you, Beads.) I didn’t want that. I also didn’t want to fire off agents blind and call it vibe coding. I wanted to stay close to the work, taking small steps I could review, with each session building on the last one’s so the project compounds instead of starting over every morning.
It started with a handoff
My setup began about as simple as it gets. I was building an image and 3D generation workbench with Claude Code and Codex. It had an instructions file for the agent, two spec docs, and a handoff note, and the last line of the instructions said to update the handoff before ending a session. Simple enough, and it worked: four build phases went in over the first nine days.
Then I got greedy. More sessions meant more work in flight, and every new agent inherited the repo’s rules on top of the project context. Those rules kept growing, because every time something went wrong we wrote one so it wouldn’t happen again. Each made sense on its own. About two and a half weeks in, the docs folder went from 2 files to 11 in a single day. That same day I told an agent I felt a bit disconnected. From my own project.
Two days later the repo had sixteen living documents, each bringing its own IDs. We cut them down to five by asking what a fresh session actually needed to know. Current work got one clear home, old explanations went to Git history, and a plain task ledger came out of the cut: a table of the work and which session held it. We tried giving every task its own folder too. That lasted a day. After that, a folder only existed while its task was in flight.
Too much damn prose
Five documents was better. It wasn’t enough. Three days after the cut, I admitted that every plan I asked for came back as a long wall of prose, and each time I either went with the recommended approach or simply couldn’t keep up with the reading.
This is a trap you can walk right into with agents. Ask one to make sure something always happens, and it’ll happily write a new Markdown file and link it from CLAUDE.md or AGENTS.md so the next agent follows it. Problem solved. Except now every future session has more to read, and the rule still only works if the next agent reads it and takes it seriously.
So I had an agent measure what a session actually did when it picked up a task. Around 120,000 tokens of context went by before it wrote a line of code, and more than half of that growth was reports from subagents it had sent off to search. In two of five sessions, the documents I’d marked binding never reached the thread doing the work. Subagents read them and passed back summaries. The audit put it better than I could: “No amount of writing ‘BINDING’ in bold fixes an economic incentive.”
The prose kept coming anyway. About a week later another audit found CLAUDE.md had grown from 70 lines to 134 in two weeks, and named the pattern: each session fixed its own small friction by adding something, and every future session paid for it in reading. About a month after that, I counted the standing rules. There were 116. Code held 61 of them, counting tests, database triggers and the architecture itself. Hooks held 8. The other 47 were held up by nothing but the sentence.
That’s too much damn prose.
Checks beat sentences
The way out had shown up weeks earlier. I just didn’t see how far it went. A task got marked done while the running app was still two tasks behind, so I asked for a hook. Seven minutes later there was one: a stop hook, which Claude Code runs whenever the agent tries to end its turn. If the session had closed work that wasn’t deployed, the hook would block the stop once and tell the agent, while it still had the context to fix it. (It also came with a new rule in the docs. Old habits.)
A hook doesn’t care how well-written your Markdown is. That’s become my rule of thumb: if a script can check it, I’d much rather have the check than another paragraph.
About those worktrees
I started with the standard answer: one worktree per concurrent session, so no session’s git stash or reset could wipe out another’s half-finished work. Then an agent went through my session logs and found zero real worktree operations. Another rule held up by nothing but the sentence. When I looked at why, the rule turned out to be a bad deal twice over. Sessions did overlap, at least 15 pairs of them, but most of my work was one session at a time, so a worktree on every task was insurance for a crowd that rarely showed up. And a worktree only separates files. The things my sessions could really collide on were the running app on its one port and the single GPU, and two sessions deploying or benchmarking at once would wreck each other’s results from any checkout.
So we flipped it. The shared checkout became the default, with a cheaper rule for the files: stage by explicit path, and never run checkout, stash or reset there. Each session announced itself when it started, and a worktree became the exception for when two live sessions really overlapped. SAKO’s “live peers” line grew out of that.
What survived
About two months in, I pulled the part that was doing the work into its own kit. That’s SAKO: one Python file using only the standard library, with its records in Markdown under .sako/ where I can read them myself. SAKO doesn’t launch or direct agents. I open sessions in Claude Code or Codex like always, and a hook (or an explicit command) checks them in.
Each task names the files or directories it expects to change. A session claims a task before it starts, and if another live session’s claim already covers that scope, SAKO refuses the claim. I can split the work differently or let one task wait. When a second session checks in, it sees who’s already out there:
$ python3 .sako/sako.py start --session demo-docs
...
live peers (disjoint: share the tree; overlap: wait or negotiate):
peer sk-0115-3c0f5f721a4a on main since 2026-01-15T09:30:00+0000 -> T-1 (claimed) touches greeting.py [from the claim column]
...
next: T-2 (P2, the only ready task): Show how to use the greeting
That’s my minimap. It comes from the two-session demo, a scripted run with real SAKO output that a test keeps in sync. The two “sessions” are stand-in processes, so it shows the records doing their job, not two AIs collaborating.
When a session tries to stop, the finish gate runs. It wants every changed file covered by a task the session claimed, a passing check on the current content of anything it closed, and finished work committed. Here’s a session from the finish-gate demo that edited a file before claiming its task:
SAKO gate: 1 problem(s) before you stop:
Recorded: changed this session but covered by no task you claimed or closed: greeting.py
The rest of the message hands the agent the exact command to fix it. And because a passing check is tied to the content it examined, an old pass stops counting the moment someone edits the file again.
None of this is new. Task ownership and a definition of done are about as old as software teams. I just wanted them checked at the moment the agent tries to stop, while it can still do something about it.
The gate’s first victim was its builder
The first time the full gate ran on SAKO’s own repo, it flagged nine problems at once. Nearly every task touched the same file, sako.py, so each new edit made every task it had already closed look stale. The agent that tripped it went straight to writing a fix, and the next session committed it: edits made under a claim the session still holds count as that claim’s work, so they no longer reopen tasks that already passed. So the gate was loosened by the agent it was gating. That change is still sitting in my notes, marked open to my review, which tells you who still has to read things around here.
Later that night it caught something real. A CI workflow had been committed under no task. The agent’s final report said which task the file belonged to and that it had left that task open on purpose. A silent gap became one sentence I could actually read, right when I needed it.
Where it stops
The gate refuses a stop once. A second stop, or a hook error, lets the session end anyway. I expect the agent to say what it left unresolved, and nothing forces it to. A clear gate means the rules passed, nothing more. SAKO can’t tell whether your tests check the right thing, and calling the work done is still my job. The lock covers SAKO’s own records, not your files or Git’s index, so an agent can still edit outside its claim. It’s built for one repository and its worktrees, not live coordination across machines. And more sessions means more for me to review, so there’s only so much I want in flight at once.
Try it
The 0.5.1 release is out. If you’re juggling a few agents on one project, run uvx sako init inside a Git repository, set your project’s check command, then open a couple of fresh sessions and give them small tasks that touch different files. The getting-started guide covers the prerequisites and client setup. For a one-line fix I wouldn’t bother. It starts paying off once the work spans several tasks and conversations.
Tell me in the issues where it gets in your way, especially if keeping the records turns into one more chore. That’s the exact thing I was running from.
And if you’ve been running a few agents for a while: what’s the rule you wrote down that none of them actually follows?