the coordinator pattern
One driver,
many agents.
This page explains a pattern — a way of using kolu — not a feature to turn on. It’s the “why” and the shape; the exact commands live on the kaval and padi pages, linked throughout.
kolu can be driven by an agent, not just a person. Because every terminal kolu owns can be created, prompted, watched, and read from the shell, one agent sitting in a terminal can do to another terminal exactly what you would: open it, type a task, wait for the reply, read it, type the next one. Scale that up and you get a fleet: one coordinator agent driving many implementing agents, each in its own worktree, while you stay one layer above all of it.
This isn’t hypothetical. kolu’s own features are built this way — the remote-terminals work was a coordinator dispatching briefs to agents running inside kolu’s terminals, on real hosts, for weeks. This page describes that pattern as something you can use.
Three layers: you, a coordinator, a fleet
The pattern has three roles, and the point of it is the top one.
You (the human)
Set the intent and merge the results. You don’t watch ten agents; you can’t. You talk to one agent — the coordinator — and review the pull requests that come out.
The coordinator
One agent in one terminal. It breaks the work into briefs, spawns an implementing agent per brief, watches each one, verifies what they claim, and answers their questions. It is the single door that work passes through.
The fleet
One implementing agent per task, each in its own worktree so they can’t step on each other. They do the work, open PRs, and route anything they’re unsure about back up to the coordinator — never to you.
The reason to add a coordinator at all is span of attention. A person can drive one agent well and three agents badly; past a handful, you spend all your time context-switching and none of it thinking. A coordinator doesn’t get tired of switching. It holds the state of every agent it spawned, notices the moment one needs input, and only surfaces to you when a PR is ready or a decision is genuinely yours. You trade keystrokes for review.
The loop: create, prompt, wait, read
Driving another agent is a loop of discrete moves, because each move is a separate, observable thing. The coordinator creates a worktree’d agent, sends it a brief, waits for it to settle, submits, waits for its turn to end, and reads the screen:
# spawn an implementing agent in its own worktree — it appears live on the canvas
id=$(padi-tui create --worktree fix-parser -- claude --dangerously-skip-permissions | jq -r .id)
kaval-tui send "$id" "read /tmp/brief-fix-parser.md and take it end-to-end" # 1. the brief
kaval-tui wait "$id" --until idle:300 # 2. observe it settle
kaval-tui send "$id" --key Enter # 3. submit — its own step
padi-tui wait "$id" --until awaiting,waiting # 4. its turn ended
kaval-tui snapshot "$id" --viewport # 5. read what it said
The one move that surprises people is that submitting is separate from typing. An Enter sent in the same breath as the text races the terminal UI’s paste-debounce and gets silently dropped, leaving the prompt sitting unsent — so you type the text, watch the UI go quiet, then send Enter as its own command. That’s why kaval-tui send carries text or a key but never both. The full command surface — send, wait, snapshot, the byte-exact --file, the control keys — is on the kaval page; a worktree’d agent that appears on the canvas comes from padi-tui create.
Long briefs don’t paste cleanly (kolu folds a big paste into a placeholder that Enter won’t submit — issue #1702). So the coordinator writes the brief to a file and sends a short prompt that points the agent at it, the way the loop above does. The agent reads its own brief.
Knowing when an agent is done — and whether it’s asking you
The hardest part of driving an agent isn’t sending input; it’s knowing when to send the next thing. There are two done-signals, and they answer different questions.
Output went quiet
kaval-tui wait --until idle:<ms> blocks until no output byte has arrived for a while. It works on any terminal with no setup, but it can’t tell “finished” from “stopped to ask you” — both look like silence.
The agent's state changed
padi-tui wait --until awaiting,waiting blocks on padi’s real agent-state — working, awaiting (it’s asking you), waiting (it just finished). This one distinguishes a finished turn from a blocked question, for the terminals kolu owns.
That distinction is why a coordinator uses the agent-state signal when it can: piling a new task on top of an agent that stopped to ask a question is how a fleet wedges. When the state says awaiting, the coordinator reads the screen and answers; when it says waiting, the turn is genuinely done. Silence alone can’t tell you which, so — after any wait returns — the coordinator reads the snapshot before it responds.
Questions flow up to the coordinator, never out to you
An implementing agent that hits a real decision has a problem: it’s alone in its own terminal, and the human is one layer up and probably asleep. If it pops an interactive question, that question sits unanswered in a PTY nobody is looking at, and the agent stalls.
So the discipline is: an implementing agent routes every question to the coordinator, using the same drive loop in reverse — it writes to the coordinator’s terminal and waits for a ruling. The coordinator is the one door in the seam the fleet is built on: observation flows out freely (you, or the coordinator, can watch every agent’s state at any time), but action and decisions flow in through a single point. That one-door shape is what keeps a fleet autonomous instead of stalling on a dozen unanswered prompts — and it’s the same shape kolu uses to put an agent under a chat app.
A fleet can span machines
Nothing about this is local. Because padi binds a per-host daemon over ssh, a coordinator on your laptop can drive agents running on a remote host — padi-tui wait --host prod <id>, kaval-tui snapshot --host prod <id> — the same loop, a different box. A remote PTY survives the link dropping, so an agent keeps working while your laptop sleeps. The fleet is a mix of hosts; the driving code doesn’t change.
The seams, and when not to bother
This pattern is real but not seamless, and it’s worth knowing the sharp edges before you lean on it:
- Submit is a separate step. The single most common failure is a brief that “was sent” but sits unsubmitted because the Enter got dropped. Watch the settle, then submit.
- Terminal ids re-key on a daemon restart. An id you cached can go stale after a redeploy or a crash-restore; track a long-lived terminal by its title, which survives, and re-find its current id.
- Big pastes fold. Anything past a couple of lines goes through a file the agent reads, not the prompt box.
- The human is still in the loop. In practice a coordinator still surfaces to you more than the ideal — a decision it won’t make, a PR that needs your eyes. That’s a feature, not a bug.
And the honest trade-off: a coordinator earns its overhead only past a handful of agents. For one quick task, drive the agent yourself — the loop above is the coordinator’s move, and you can run it by hand. The pattern pays off when the number of agents crosses what one person can hold in their head at once, which is exactly the moment you’d otherwise start dropping threads.