How I supervise parallel coding agents from tmux
Sounds and speech bring me back to the right terminal. Tests, diffs, and worktrees keep several coding sessions useful. The limit is no longer typing speed. It is review.
- Published
- Reading time
- 14 min read
In early October 2025, I opened hibase.co, an older Ruby on Rails application that we still operate, and handed Claude a bug that crossed the backend and frontend. I expected the usual result. Earlier coding agents could explain an isolated file or produce a useful snippet, then lose their footing among the awkward connections that accumulate in a long-lived application. I would take over once the real investigation began.
Claude kept digging. It traced the behavior through the Rails code, followed the corresponding frontend path, found the faulty assumption, and implemented a substantial fix. I still read every changed line, but for the first time I was reviewing a coherent solution instead of salvaging fragments. I remember sitting back and thinking that the threshold had moved. This was serious engineering work.
Anthropic had released Claude Sonnet 4.5 on September 29 and reported large gains in coding and long-running agent work. I did not need a benchmark to notice. The hibase session changed what I was willing to hand to a model.
I tried Google Antigravity around the same time. It never clicked. I am a terminal-oriented engineer, not because terminals are austere or fashionable, but because I have spent years making mine fit my hands. Vim is my editor. My projects, dotfiles, shells, database commands, logs, and deployment tools already live there. My .tmux.conf has evolved with that work, and its navigation is muscle memory. A graphical agent workspace gave the model a polished home and made the rest of my work feel like a visitor.
pi felt native on the first day. It ran in the terminal, used the tools already installed there, and left the shape of my work alone. Pi is intentionally small and expects users to add extensions, skills, prompt templates, and project instructions. It skips built-in subagents and plan mode. I wanted exactly that. I did not need another development environment. I needed a capable model inside the one I owned.
As OpenAI’s coding models improved, I put my existing Codex subscription through the same workbench. Soon nearly every active tmux window had a pi pane beside Vim or a shell. Small irritations went first. Before long, I was handing over changes that I would once have reserved for an uninterrupted afternoon.
Today my desk may have several coding agents running, but I do not pretend they form a miniature software company. There is usually no manager agent assigning work to worker agents while a reviewer agent watches them both. There are ordinary pi processes, a few Git worktrees, and tmux windows whose names I already know.
Tmux had taught me how to divide work long before models could edit code. A session holds a project. Windows separate tasks. Panes hold an editor, shell, logs, or agent. Coding agents increased the amount of work in motion, but they did not require a new map.
My problem is deciding which tasks can proceed independently, returning when judgment is needed, and proving that a plausible result is actually good.
The short answer
A small pi extension named ready-sound.ts plays a chosen sound when a session returns control to me. I wrote it because polling silent panes was ridiculous. Later, I gave every session access to sayneat through my top-level AGENTS.md. An agent can speak a specific request when it needs a decision instead of merely sounding an alarm.
I return for an unclear requirement, an irreversible action, an unexpected failure, or a completed change ready for review. Routine tool use should not need me. Production changes, destructive commands, and scope decisions should.
I do not trust an agent’s closing summary. I use it to find the evidence. I inspect the diff, read the changed tests, run the relevant checks, and look at the behavior. Passing tests are necessary, but they do not rescue the wrong requirement, a weak test, or an unnecessary abstraction.
I supervise by callback, not by watching token streams. When completed diffs arrive faster than I can understand them, I stop starting sessions. More agents would only create a larger pile of code I have not judged.
One process owns one context
I usually treat each pi process as a first-class work session, not as a hidden sub-session. It has one conversation, one repository view, and one task. Its context may later be compacted, but it remains attached to the work that produced it.
I can always find the state. The source tree and Git record the material result. The pi session records the investigation and decisions. The tmux window gives both a stable place. If I return after an hour, I do not ask an orchestration layer which synthetic role currently owns the task. I switch to the window whose name I already know.
Parallel work in the same repository needs a harder boundary. Two agents editing one checkout can invalidate each other’s assumptions even when Git can merge the final text. I use a git worktree for each concurrent branch. Every session gets its own files, index, and branch, while all worktrees share the same object database.
Worktrees do not make coupled tasks independent. Two branches can still redesign the same interface, compete for a migration number, or assume incompatible states in a shared database. I split work only when I can state a clean boundary and a way to verify the pieces separately. If I cannot explain how the results will combine, I have not found parallel work. I have found a future merge meeting.
For an unusually large job, I can ask pi to start another pi process in a new tmux window and monitor it. I have had good results with that arrangement, but I use it rarely. Most work does not need recursive delegation. One or two explicit worktrees are easier to understand than a tree of sessions whose coordination becomes a separate problem.
What pulls me back in
A session should not demand attention merely because it has been running for ten minutes. Long tool calls, broad searches, and test suites are normal. Watching them continuously turns me into a progress indicator.
ready-sound.ts was my first pi extension because it solved that mundane problem directly. When a turn ends and the session is ready for me, it plays a sound. I can move to another project, read a diff, or answer a message without checking each pane on a timer. The extension does not decide whether the work is good. It only tells me that the next exchange can happen.
Sound identifies an event, not its meaning. I later wrote a speech command called saynice, then replaced it with the local-first sayneat. My global AGENTS.md tells agents that sayneat is available when they need my attention. A waiting session can say that a Terraform plan is ready, that a requirement has two plausible readings, or that tests exposed an unrelated production assumption.
The result can be funny and occasionally embarrassing. During a meeting, my computer may suddenly address me in a remarkably natural voice. It is still better than maintaining a mental list of silent terminals.
There are four common reasons I return to a session.
First, the task has reached an ambiguity that code cannot settle. The agent can research existing behavior and present options, but it should not invent product policy or rewrite a business rule because one interpretation made the implementation easier.
Second, the next action crosses a risk boundary. My pi processes usually run without per-command approval inside source trees I control. Pi itself has no built-in permission sandbox and inherits the permissions of its process. That removes repetitive friction, but it is not blanket authority. My standing rules allow commands such as terraform validate and terraform plan, forbid -auto-approve, and require my confirmation before an apply. Untrusted repositories and stronger isolation needs belong in a container or virtual machine. A fast local loop must not become unattended production access.
Third, verification failed in a way that changes the premise. An ordinary failing test is part of the agent’s job. A failure that reveals undocumented behavior, shared infrastructure, missing credentials, or a conflict with another task may need a decision from me.
Fourth, the proposed result is complete enough to review. This is the most frequent callback and the one that consumes most human time.
Speech is useful because the agent can distinguish those cases in a sentence. The completion sound remains useful because most turns need no elaborate announcement. Together they replace polling without pretending that notification solves supervision.
How I decide whether to trust the result
Trust starts before the agent edits anything. I try to give it a bounded outcome, the relevant constraints, and a concrete way to prove the behavior. Repository-level instructions explain where tests live, which commands are safe, which style the code uses, and what must remain untouched. Those rules live in AGENTS.md because repeated expectations should be versioned, not reconstructed in every prompt.
For behavior changes and bug fixes, I prefer a red and green sequence. The agent adds or selects a focused test, runs it, and sees it fail for the expected reason. Only then does it make the smallest implementation change and rerun the same test. A test that passes before the fix does not demonstrate the gap. A test that fails for a syntax error demonstrates the wrong gap.
A captured failure tells me more than a polished summary. I can inspect the claimed failure, the change that makes it pass, and the broader checks that guard against collateral damage.
When the session calls me back, I usually review in this order:
- I read
git statusand the diff to establish the actual scope. - I inspect tests as critically as implementation code.
- I run the focused check and the relevant full suite.
- I run language-level checks such as formatting, compilation, type checking, or linting.
- I exercise the user-visible path when the change affects a template, interaction, browser, or deployment boundary.
The diff comes first because successful checks can coexist with an unacceptable change. An agent may modify a fixture until the test agrees with the bug, add a dependency for a five-line problem, duplicate an existing helper, or quietly broaden the requested scope. Senior engineering experience is useful here. Years spent recognizing weak boundaries, accidental coupling, and plausible but false explanations do not become less relevant because code appears faster.
I also read the agent’s summary, but I do not count claims such as “all tests pass” as evidence until I have the command and result. The summary helps me navigate. The repository decides what changed, and independently executed checks decide what currently works.
For a large or sensitive patch, I may open a fresh pi session and ask it to review the diff without the implementation conversation. Fresh context can catch an assumption that the original session and I have both absorbed. It is another review input, not an approval. Two agents can agree because they inherited the same mistaken requirement.
Not every change deserves the same ceremony. A spelling correction and an authentication change have different failure costs. The constant is that responsibility remains with me. I can delegate investigation, editing, and test execution. I cannot delegate accountability by asking a model whether its own work should be trusted.
Parallelism stops at the review queue
I have no interest in maximizing the number of agents on my screen. My laptop can keep more sessions active than I can supervise well.
Every task has at least two human costs. I must frame it clearly enough to begin, then reconstruct enough context to judge the result. Tmux makes switching cheap because the locations are familiar. It does not make understanding free.
I spend less time writing routine code and more time selecting and reviewing work. I decide what should exist, identify independent slices, write acceptance criteria, evaluate trade-offs, and check finished work. This is a better use of my experience, but it remains work.
I stop adding parallel sessions when one of three things happens. Tasks begin touching the same conceptual boundary. Completed changes wait for review. I can no longer remember why a session exists without rereading its whole history. At that point another agent increases work in progress while reducing confidence.
I want a short feedback loop and no unattended queue of plausible code. Sometimes that means several repositories moving at once. Sometimes one difficult migration deserves the entire desk.
I tried building the orchestration layer
In April 2026, I tested the alternative rather thoroughly. I built a file-backed kanban system for coding agents. It launched worker and manager roles in tmux panes, assigned tickets, woke managers on a schedule, ran independent reality checks, tracked activity, accepted priority overrides, and exposed an operator role for me.
Eleven days after the first commit, I deleted it.
The system worked well enough to reveal its real cost. A worker could be alive but stuck. A periodic check could interrupt useful work. A manager needed context compaction and recovery rules. Tmux copy mode affected process control. Roles needed authority rules when priorities changed. The operator shell needed lifecycle handling. Each reasonable answer added another state transition to software whose only purpose was coordinating software that was supposed to save coordination time.
I did not delete it because the implementation failed. I deleted it because manager and reviewer agents shared the same incomplete specification as the worker. More roles produced more messages without producing independent knowledge. I had added a “reality check” role because I could no longer understand the work from the main loop. That was a damning feature for a tool I had built to improve oversight.
I kept the lessons and removed the organization chart. Tasks should be explicit. Tests and diffs should hold shared state. Fresh context is useful when review independence matters. Parallel work needs clean boundaries. Priorities remain close to the human who is responsible for the outcome.
I still do not use subagent systems by default, even though pi can support them. Recursive delegation helps with some large, separable jobs. It is not a free multiplier. Coordination consumes context, tokens, time, and attention whether the participants are people or model instances.
The commercial model had to change too
Hourly pricing became harder to defend. If a task that once consumed several manual days can now be investigated, implemented, and verified much faster, billing by keyboard time punishes the investment that made the work efficient. It also encourages clients to compare hours rather than outcomes while leaving me responsible for the same production risk.
I now prefer selling bounded chunks of work or a sprint at a fixed price. The client buys an agreed result. I price the scope, uncertainty, review, communication, and accountability, not the number of minutes in which code appeared.
Agents do not make engineering free. Review can dominate a short implementation. Poorly understood legacy behavior can still consume days. A production incident remains expensive even if the faulty patch took seconds to generate. With a fixed scope, finishing the same work faster helps both sides instead of turning efficiency into a smaller invoice. I still carry the estimate and the production risk.
The human bottleneck did not disappear
Coding agents removed a surprising amount of routine implementation work from my days. Small irritations no longer wait at the bottom of a backlog. Large refactors can begin with a capable investigation instead of a blank editor. Several independent projects can move while I concentrate on one difficult decision.
I now run out of review attention before I run out of agents. Good work still needs a coherent definition of done and an experienced person willing to reject code that merely looks finished.
On my desk, tmux holds the contexts. Pi does the work inside them. Git worktrees isolate concurrent changes. A sound tells me when a session is ready. Sayneat explains why it needs me. Tests, diffs, and direct observation decide whether the output survives.
The tools do not replace supervision. They bring me back at the right moment.