Parallel work
You will be able to run several agents against one project at the same time, choose which tasks are safe to dispatch together, and recover a task whose agent died — without a coordinator, a queue, or a person allocating work.
Audience: anyone running more than one agent. Assumes: task lifecycle.
The primitive: select and claim, atomically
COMMISSION_ACTOR=claude:api commission next --project myapp --claim
COMMISSION_ACTOR=claude:web commission next --project myapp --claimThose two commands return different tasks, and it does not matter how close together they run. The claim is decided by a single conditional statement in SQLite — one row per held task, with the task id unique — so there is no window in which both can win and nothing to lock outside the database.
The second one says what it passed over:
claimed myapp-3
skipped: myapp-2 (claimed by claude:api)
routing: claude:web is holding it; autonomy autonomous (default); no actors are declared, so nothing is restrictedThis is the whole coordination mechanism. There is no dispatcher process, no
work queue, and nothing that has to be running for it to be correct. POST /v1/next and the commission_next MCP tool are the same call.
Give every agent a distinct COMMISSION_ACTOR. It costs nothing and it is the
difference between a legible event log and an anonymous one.
The frontier, and what may go together
commission ready --project myappready frontier (dispatch independent tasks in parallel):
myapp-2 · Webhook ingest endpoint [open · p2 · gate:shipped]
myapp-3 · Event feed in the web view [open · p2]
safe to fan out together (no touches overlap): myapp-2, myapp-3ready is everything workable right now: dependencies met, not claimed by
anyone else, and not filtered out by an execution policy the asking actor fails.
It is derived on read, so it cannot be stale.
touches, and exactly how much it promises
touches is a free-text label for the area of the codebase a task will edit:
{ "key": "ingest", "title": "Webhook ingest endpoint", "touches": ["src/http", "src/db"] }Commission compares those labels between ready tasks and reports overlaps, then
suggests batches with no overlap inside them. In --json:
{
"entries": [{ "task": { "id": "myapp-2" }, "touchesOverlap": ["myapp-4"] }],
"batches": [["myapp-2", "myapp-3"], ["myapp-4"]]
}An honest limit, stated before you rely on it. Commission does not read your code. It compares strings you wrote. So:
- An overlap warning is a good signal that two tasks will fight.
- No warning does not prove two tasks are safe to run together. Two tasks
labelled
src/httpandsrc/dbcan still both need to edit the same shared type. - Labels only overlap if they are spelled the same way.
src/dbandsrc/db/are two different labels.
Use the batches as a starting point and think before fanning out. commission prime
tells agents the same thing, in the same words.
Fanning out
commission ready --project myapp --jsonTake a batch, and start one agent per task with its own actor name. The practical shape, whatever your orchestrator:
COMMISSION_ACTOR=claude:ingest commission next --project myapp --claim
COMMISSION_ACTOR=claude:feed commission next --project myapp --claimEach agent then runs its own loop — note, check, evidence, done, next — and stops when the frontier is empty. Nothing needs to tell them what to do next; the graph does.
If the work genuinely cannot share a checkout, give each task its own worktree and its own ports:
commission workspace prepare myapp-2See workspaces.
Keeping a wide frontier
The frontier is only as wide as the graph allows. Two habits keep it wide:
- Declare dependencies only where there is a real ordering. A
depthat encodes “I would rather do this first” serialises work for no reason. Commission will happily give you a chain of thirty tasks and one ready item. - Split by area, not by phase. “Implement X” and “test X” is a two-link chain; “the ingest endpoint” and “the event feed” is two parallel tasks.
Watch it directly:
commission board --project myappThe blocked section names each blocker. If it is long and ready is short,
the graph — not commission — is the bottleneck.
When an agent dies
Claims are leases. The default is eight hours; an expired lease is reclaimable by anyone, and the reclaim is recorded:
commission ready --project myappA task whose claim has expired appears on the frontier again with no intervention. Nobody has to notice, and nothing has to be cleaned up.
To take a task back deliberately:
commission release myapp-2
commission claim myapp-2For long stretches of real work with no commission activity, the actor holding the claim extends it rather than letting it lapse — only the holder may, and the refusal says so:
commission heartbeat myapp-2commission doctor reports claims that are expired or have seen no activity, and
commission inbox surfaces stale claims as an item with the command that clears it.
Watching several agents at once
commission board --project myapp
commission log --project myapp --last 20
commission standup --project myapplog is the append-only feed with a cursor: store nextCursor from --json
and pass it back as --after. Do not compare timestamps — ids are monotonic,
clocks are not.
commission log --project myapp --json
commission log --project myapp --after 11standup is the same facts as a narrative: what got done, what is in flight,
what is blocked, and the latest note on each.
The whole sequence
commission ready --project myapp
commission ready --project myapp --json
COMMISSION_ACTOR=claude:ingest commission next --project myapp --claim
COMMISSION_ACTOR=claude:feed commission next --project myapp --claim
commission board --project myapp
commission heartbeat myapp-2
commission log --project myapp --last 20
commission standup --project myappEvery command on this page is executed by tests/docs.test.ts against a scratch
database.
Related
- Planning a task graph — how to build a frontier worth fanning out.
- Workspaces — a directory, an environment, and ports per task.
- JSON output — the
ready,board, andlogshapes.
Part of Guides.
This page is docs/guides/parallel.md in the Commission
repository, rendered in place — the site keeps no copy of it. The repository is private, so there is no edit link to follow.