What it does
claude-batch-runner runs one claude -p process per unit of work, in parallel, from a campaign
spec: a JSON file naming a worklist, the agent that processes each unit, and the shape of the
result. The loop that decides what runs is ordinary code, so every unit in the worklist is
dispatched and every unit appears in the report. The model is called only to do the work, to redo a
unit on escalation, and to grade it.
Configuring a campaign
The spec requires three fields: worklist, worker and schema.
worklistis a file with one unit per line, either a JSON value or a bare string such as a path. It is resolved relative to the spec file.workeris{agent, tier, effort}: the agent that processes each unit. The agent must already exist in your Claude Code setup.schemais the JSON schema every worker result must match.parallelismis optional and sets how many workers run at once. The default is the smaller of the unit count and 4.escalateis optional:{condition, advisor}. Units whose output matches the condition are redone by the advisor agent.verifyis optional:{rubric, agent, tier, effort, max_revisions}. It grades each unit against a rubric file.deliverisreport-only(the default) orpr-dispatch.pr-dispatchis a stub that raisesNotImplementedError, so usereport-only.reportis an optional output path template.{date}and{campaign}are substituted, and the default isreports/{date}-{campaign}.md.
Validation
The spec is checked before any agent runs. An unknown field, a missing required field, an invalid tier name, a missing worklist or rubric file, or an empty worklist stops the run with an error that names the field, and no tokens are spent.
The escalation condition is parsed before the batch starts, too. It has the form
<field> <op> <number>, for example confidence < 0.6, and it is checked against a numeric field
of the worker’s structured output. There is no boolean logic; to select a subset of units,
filter the worklist.
With verify set, a grader scores each unit against a rubric: a markdown file with one
## N. <id> section per criterion. On any failing criterion, the worker is re-invoked with the
failed criteria attached, up to max_revisions times (default 1). A unit that still fails is marked
FLAGGED. A grader result with no evidence, or that is otherwise malformed, is rejected and never
counted as a pass. The grader may not run on the cheapest tier.
Concurrency
Each unit is a separate claude subprocess, and parallelism sets how many run at once. The limit
is threads around subprocesses, so what your machine and your rate limits allow is the practical
ceiling. Each call is bounded by AGENT_TIMEOUT_S (default 600 seconds). On Linux and macOS a
timeout stops the whole process tree; on Windows only the direct child is stopped.
Result files
A real run writes one markdown report, at the report path. It has a header line (date, wall-clock
time, worker, parallelism), a count of outcomes (ok, escalated, revised, FLAGGED), a table
with one row per unit, a cost table of calls and tokens per tier, and a short handoff block. Each
unit row shows the unit number, the input, the outcome, the verify result, the number of revisions,
the tokens used and a note.
Call counts are exact. Token counts come from the usage field on each result when the CLI
provides one, and the report states how many results carried none.
Failed units
A worker that errors out, an advisor or verify call that errors, and a unit that still fails its
rubric after the allowed revisions all come back as FLAGGED rows. The note column gives the
error or the failed criterion ids, and the rest of the batch keeps running. After a run, read the
FLAGGED rows in the report and fix the cause, such as an agent that does not exist or a
rubric the output cannot meet.
The runner has no resume: a campaign that stops partway starts over from the first unit. Split a large worklist into several campaigns, or remove finished units from the worklist before re-running.
Quick start
1python -m claude_batch_runner --spec campaign.json --dry-run
--dry-run validates the spec and counts units without dispatching anything or spending a token.
It prints the worker, parallelism, escalation, schema, verify and report settings it would use.
Drop --dry-run to run the campaign, which calls the agents named in the spec and costs tokens.
Requirements
Python 3.11+, standard library only beyond the claude CLI it shells out to, one process per
unit of work. That CLI must already be on PATH and authenticated. The runner uses whatever
authentication the CLI already has and never reads or sets credentials of its own.
The Windows installer above, claude-batch-runner-setup-<version>.exe (attached to each release),
is the runner frozen into one executable, so it needs no Python. Claude Code is separate and is not
bundled: with no claude on PATH every unit comes back FLAGGED and the campaign accomplishes
nothing. The installer puts the runner in Program Files and appends that directory to the system
PATH; open a new terminal afterwards, because one already open still holds the old PATH.
Documentation
claude-batch-runner’s documentation lives in its repository, next to the code it describes — the README is the manual.