How to Run Multiple Coding Agents at Once Without Babysitting
Run ten coding agents at once and see in one query which one needs you, even after the terminal that started it is gone. The board is one SQLite table, and it catches the worker that is still checking in but hasn't done anything for 18 minutes.
>This covers the board. Remote Coding Agents goes further: leases that stop two workers from grabbing the same job, side effects that are safe to deliver twice, and the drill where you kill a worker mid-write and prove nothing repeats.

Remote Coding Agents: Ship Verified Pull Requests Anywhere
Run Your AI Coding Team From Your Phone and Ship More in 30 Days
Hello builders,
Ten coding agents working at once is a real way to ship more in a day, as long as you can see in one glance which of them needs you right now. The one that costs us is the worker that keeps checking in every thirty seconds and has done nothing useful for 18 minutes, because the job that failed loudly at least told us so. When you run multiple coding agents at the same time, what you need is one SQLite table and one status query that keeps “alive” and “moving” in separate columns.
Somebody asked the question better than I can: “How do they persist the sub agents and interact with them to keep track of 10 projects?” A thread on r/ClaudeCode asking how people run several agents at the same time pulled 85 comments, and the top answer, from u/Employ-Flaky at 19 points, is good advice:
The safest setup is one worktree per agent, one narrow task per branch, and a handoff note saying what changed, what was tested, and which files are off-limits.
I agree with every word of it. None of it tells you which of the ten is stuck, though. Which one needs me right now? That is the question we actually have at 4pm with a phone in hand.
Jobs outlive sessions
Terminals close, clients disconnect, and a worker that loses its lease gets replaced by a fresh one, while the thing you asked for stays exactly the same, so a session makes a poor name for a job. We give the job its own row and let sessions come and go underneath it. If the only way to learn what is running is to ask a model to summarize its memory, we’re one closed tab away from knowing nothing.

Eight columns, four jobs, and every row answers a different question:
- J-1042 running, owner A-2, heartbeat 12s, progress 3m, SHA 8cd31aa
- J-1043 blocked on APPROVAL, owner A-1, heartbeat 8s, progress 11m, SHA 12b7f90
- J-1044 waiting_for_review, no owner, progress 1m, SHA 71f6c21, evidence link attached
- J-1045 failed on TEST, no owner, progress 22m, evidence
logs://J-1045
The side panel is the part people skip. Heartbeat age is how long since the job last checked in, progress age is how long since it did something meaningful, and when the first is fresh and the second is old you get STALE PROGRESS: the job is alive but not making progress.
One table, one query
Here is the whole board, and it is short enough to read on a phone. Your dispatcher inserts a row per job, each worker runs the two updates, and we run the last query from any device.
CREATE TABLE jobs (
job_id TEXT PRIMARY KEY, state TEXT NOT NULL,
owner_attempt_id TEXT, lease_expires_at TEXT,
version INTEGER NOT NULL DEFAULT 0,
last_heartbeat_at TEXT, last_progress_at TEXT,
head_sha TEXT, blocker_code TEXT, evidence_uri TEXT
);
-- worker, every 30 seconds: "I'm alive", counted only if it still owns the job
UPDATE jobs SET last_heartbeat_at = datetime('now')
WHERE job_id = :job AND owner_attempt_id = :attempt
AND version = :version AND lease_expires_at > datetime('now');
-- worker, after a command finishes or evidence is written: "I moved"
UPDATE jobs SET last_progress_at = datetime('now')
WHERE job_id = :job AND owner_attempt_id = :attempt;
-- you: the board
SELECT job_id, state, owner_attempt_id AS owner,
strftime('%s','now') - strftime('%s', last_heartbeat_at) AS hb_s,
(strftime('%s','now') - strftime('%s', last_progress_at)) / 60 AS progress_m,
substr(head_sha, 1, 7) AS sha, blocker_code AS blocker, evidence_uri AS evidence,
CASE WHEN state != 'running' THEN ''
WHEN strftime('%s','now') - strftime('%s', last_heartbeat_at) >= 90 THEN 'STALE_HEARTBEAT'
WHEN strftime('%s','now') - strftime('%s', last_progress_at) >= 900 THEN 'STALE_PROGRESS'
ELSE '' END AS attention
FROM jobs ORDER BY job_id;
The :job, :attempt and :version values are bound by your worker code, or with .parameter set if you’re trying it in the sqlite3 shell, and the dispatcher’s own write is a plain INSERT INTO jobs (job_id, state) VALUES ('J-1042', 'queued') that the lease step fills in later. I loaded the four jobs from the picture into a fresh agent.db, then aged J-1042 to a 5-second heartbeat and 18 minutes without progress. The board flagged it STALE_PROGRESS on the first run. A late heartbeat from the old owner A-1 changed zero rows, which is the whole point of putting the owner and version in the WHERE: a worker that lost the job can’t make it look owned twice.
So what counts as progress? A finished command, new evidence or a state change, and the worker names which one it was. A loop can rewrite the same file forever, which is why a timer should never be the thing that writes last_progress_at. And do not auto-block a job just because progress is old, since a big compile is quiet too. The flag asks you to look and leaves the job with its owner, and on my own board it’s the first column I read.
Keep agent.db on one machine. The SQLite docs are blunt about it:
All processes using a database must be on the same host computer; WAL does not work over a network filesystem.
If several machines need to write, that is the day we move to a server database, and not a day before.
Close everything, then look
The test takes two minutes. Start two jobs, close the terminal, the phone app and the browser tab, reconnect from another device, and we run the query again. The job IDs, owners, SHAs and evidence links should match what you saw before, and nobody had to ask a model what it remembers.
If the board can’t explain a warning from the rows it stores, treat that warning as noise and go fix the board before you add an eleventh agent.
Now go build something this weekend!
John Cook