Several agents — and the people among them — working one run at the same time.
The multi-participant layer over polyflow. A crew of agents share one broker, take work orders nobody assigned, and never do the same order twice — because coordination is a journaled step of a machine that was checked before it loaded, not an instruction in a prompt. Nothing to install beyond a clone; nothing to configure beyond a name.
A prompt that says “don't touch a file someone else claimed” is a request.
polycrew makes a second edit order something the engine will not emit.
The rules a workflow is admitted under hold for the crew exactly as they hold for one agent, because the crew is one run. There is no second gate and no second guarantee.
A single agent works a run one order at a time. The moment a step emits three orders — run the tests, scan for vulnerabilities, check the licences, then publish only if all three pass — one agent does them in sequence and three agents do them at once. Fifty files that each need the same change are fifty small runs, and any number of agents can drain them.
Each agent asks what is free, claims one order, does it, reports it. The split falls out of who was free when. In the acceptance run one session took a check and the publish; the other took the two remaining checks. Nobody decided that.
A claim is a lease: it holds while you work and lapses on silence. Only the holder may report. A session that dies mid-order loses its claim, and the order returns to the offer list for someone else — at a higher attempt, in the journal.
Every step carries the actor that caused it — an identity the server minted at boot, which no model can choose or forge. Ask the journal afterwards and it tells you who did what, and which claim was refused, and why.
polyflow is one process, one broker in memory, stdio and SQLite. polycrew is what changes when there is more than one participant: a broker they share, orders one of them can claim, and a page a person can watch. Everything else scales down to one participant for free, so polyflow keeps it.
The operating system hands a port to exactly one process. That process is the broker; the rest forward their calls to it over loopback. If it dies, the next call from any session elects a new one on the same port, and the runs continue out of the same store. No consensus protocol, nothing to configure.
No tool schema has a field for an actor or a role. A session that could name itself could collide with another or invent a second self; one that could declare its own roles could attach as a reviewer and satisfy its own approval gate. Anything a model can say, a model can say wrongly — so it cannot say it.
claimed: false comes back with who holds the order and a hint to ask for something else. A model that receives an error retries the call it just made; a model that receives an answer goes and finds other work. Both live sessions got this right on the first attempt.
Report an order after the broker that was waiting for it has died, and the result is written down rather than refused. The run takes it up when that order is next offered. Nobody is asked to redo work that was already done — the failure this exists to prevent, not one it falls into.
Eight tools. Six are polyflow's, unchanged. workflow_next — the open orders you may take, across every run in the crew — and workflow_claim — take one, so nobody else starts the same work — are what a crew adds.
workflow_next {} → 3 orders, none claimed
workflow_claim { order_id: "a1b2…" } → claimed: true, claimed_until: …
…do the work with your own tools, under your own permissions…
workflow_report{ order_id: "a1b2…", result: { count: 7 } }
→ the run advanced; here is what is next
A workflow, release-check, that emits three work orders in a single step and publishes only if all three pass — admitted at 243 paths explored. Two claude sessions started at the same moment with the same prompt. Neither was told the other existed beyond “another agent is working the same release.” What follows was read back from the crew's own store afterwards, not from what the models said about themselves.
run : claude-code|acceptance|release-check|2.4.0
phase : shipped | checksDone 3 | failures 0 | done true
seq action actor via
0 $create claude-code/8c6f1db6 start
1 START claude-code/8c6f1db6 start
2 CHECK_PASSED claude-code/8c6f1db6 report
3 CHECK_PASSED claude-code/82b4feb2 report
4 CHECK_PASSED claude-code/82b4feb2 report
5 PUBLISHED claude-code/8c6f1db6 report
completions : 4
distinct actors : 2 → claude-code/8c6f1db6, claude-code/82b4feb2
any order twice?: false
workflow_start. The identity is derived from the input, so the second start re-attached to the firstclaimed: false and moved onFrom the sessions' own summaries: “the vulnerability scan and the publish step were claimed by the other agent, so I didn't retry them.” The kill case — one session SIGKILLed mid-order, its lease lapsing, the survivor claiming the abandoned order and finishing the release — runs on every commit in test/acceptance.test.mjs with real processes and no model.
A runnable script, not a sketch. Point it at a monorepo and a crew of headless Claude Code agents makes the change, sharing the work with no supervisor and no assignment — and no file is edited twice, because that is a rule the workflow was admitted under.
examples/codemod-sweep/bin/sweep.sh \
--repo ../my-monorepo \
--change "replace moment() with dayjs()" \
--match "moment(" \
--test-cmd "npm test --" \
--workers 3 --limit 10
A workflow's rules are checked by enumerating every reachable path over a finite declared domain, and “fifty, but sometimes eleven” is not one. So each file is its own two-step run — change it, then test it — and the sweep is fifty of those. The run's identity is the file path: two runs for one file are not prevented by care, they are not representable. Restarting tomorrow re-attaches to the runs that exist instead of starting a second sweep.
One headless agent reads the candidates and decides which genuinely need the change — the judgement step, which is why a model does it. It rejected two of four in the run below: a file where moment( appeared only in a comment, and the sweep's own checker. It cannot edit; a file it changed would be changed again by whoever claims it.
| Rule the workflow was admitted under | What it stops |
|---|---|
at-most-one-edit-per-file | two agents editing one file — the failure mode of a shared sweep |
at-most-one-verification-per-file | paying for the same test run twice |
verification-implies-a-prior-edit | calling something verified when nothing was changed |
nothing-is-verified-after-a-skip | running a suite for a file nobody touched |
exactly-one-edit-when-started | a queued file silently doing nothing |
packages/alpha/src/date.js changed + verified by claude-code/0532d096 packages/beta/src/report.js changed + verified by claude-code/ae9169ad packages/gamma/src/notes.js not selected (the planner did not queue this file) scripts/check.mjs not selected (the planner did not queue this file) 4 candidate(s): 2 done, 2 not selected 2 agent(s) did the work, and no file was edited twice
The summary is read back from the crew's store, not from what the agents said about themselves. The example's README also carries the one thing that will bite anyone building on this with short-lived agents: give the crew a broker that outlives them — the script runs one process whose only job is to hold the port and the parked orders for the whole sweep.
Every broker serves a read-only dashboard on the port it already holds. First: what needs a person — orders addressed to a human role that nobody has taken, longest wait first. Then: what is running — each run's state, who holds each order, and what the machine is waiting for and until when. The boot line prints the URL.
It is loopback-only and read-only, and both are enforced rather than assumed: any method but GET is refused, a non-loopback caller is refused, and polycrew will not bind a non-loopback interface at all. There is no authentication because on loopback the trust is a shell on the machine — to reach a crew from elsewhere, tunnel over SSH.
git clone https://github.com/cognitive-fab/polycrew
cd polycrew
npm install
npm test # 64 tests, no model, no network
Node 22.13+. It is an MCP stdio server: name it in a project's .mcp.json with a POLYCREW_INSTANCE, then open two terminals in that project and run claude in each. The first becomes the broker; the second proxies to it; both print which they are. A workflow is the same five files it is for polyflow, and the same gate admits it — polycrew never forks the gate, because two gates would mean the guarantee stops being one sentence.
Coming next: a progress view over a run tree, a rendered run report, host wake events, and child workflows with cross-workflow cancellation.