Route bounded coding work across whichever installed agent CLI has quota to spare.
For multi-step work, give Bullswarm the goal and the program you author for it. The program is a JSON document of dependent actions, not a list of phases the kernel walks in lockstep. The kernel is Bullswarm’s own runtime: it validates the graph, routes each action, and computes the result. It is not an agent.
# 1. What the kernel will enforce: requirement IDs, rules, action schema, example.
bullswarm workflow plan contract \
"1. Fix the failing tests with the smallest correct change. 2. Verify them." \
--cwd ~/some-repo --json
# 2. Launch with your program. Starts independently, prints observation
# commands, and returns. Add --watch to follow low-noise progress.
bullswarm workflow goal \
"1. Fix the failing tests with the smallest correct change. 2. Verify them." \
--cwd ~/some-repo --program plan.json --watch
# Don't want to plan? Ask for a Workflow Planner agent explicitly.
bullswarm workflow goal \
"Audit and repair the parser, then run its acceptance tests" \
--cwd ~/some-repo --orchestrator auto --watch
workflow goal needs a program: with neither --program, --scout, nor
--orchestrator it exits 2, launches nothing, and prints the commands above.
That is deliberate — the kernel never plans on the caller’s behalf unless the
caller asks for it by name.
--max-agents, --max-actions, and --max-expansion-rounds are soft
planning targets for a dispatched planner. They encourage the Workflow
Planner to consolidate optional work, but the kernel never stops or rejects
essential work merely because a target was reached. --concurrency still
bounds simultaneous dispatches so the scheduler can batch a wider useful
program safely. There is no default wall-clock timeout: fresh
semantic/transport heartbeats allow a useful worker to continue, while
silence is inspected rather than blindly killed.
The caller authors a complete program, or explicitly asks for a dispatched
planner. The kernel validates the graph, executes it, and returns every action
result. Independent agents share the target worktree. ownedFiles describes
intended territory — the files that action is meant to edit — and lets the
scheduler serialize overlapping writers; it does not reject or discard edits.
A dependent starts as soon as its own inputs finish, without waiting for
unrelated siblings. A failed action skips its dependents while other branches
continue.
After a parallel implementation wave, plan one integrator depending on all its
writers. Give it lane: "build" and ownedFiles: [] to run alone with permission
to fix any file. Its prompt should read worker outputs, apply cross-territory
requests, reconcile shared files, and run the repository acceptance commands.
Analyze actions remain read-only. Evidence actions are optional and report
independent judgments; negative evidence does not open another planner round.
The graph ends with completed when all actions succeeded, or partial when
some failed or were blocked. verified separately records whether all mandatory
requirements have fresh passing evidence. Read that qualification and the
actual outputs before claiming acceptance. Further repairs use a new program.
Lane and effort are separate decisions for every proposed action. analyze is
read-only investigation, judgment, or evidence; build is contextual product,
test, or documentation mutation; chore is deterministic mechanical mutation.
The kernel rejects evidence outside analyze, file ownership inside analyze,
and any chore above low effort. Low is for fixed-procedure checks and edits,
medium is the default for ordinary bounded work, and high is reserved for
architecture, ambiguous tradeoffs, cross-cutting integration, or genuinely
adversarial acceptance judgment. Merely being an analysis/evidence action or
part of a difficult goal never promotes an action to high. The selected effort
then resolves through the High/Medium/Low routes configured by bullswarm setup
(see Strategy and Routing).
Stating lane and effort separately on every action means re-deciding two
fields for work whose nature already implies both. The optional kind field
names that nature once and derives them:
kind |
lane | effort |
|---|---|---|
mechanical |
chore | low |
io-read |
analyze | low |
digest |
analyze | low |
check |
analyze | medium |
implement |
build | medium |
integration |
build | high |
architecture |
analyze | high |
adversarial-acceptance |
analyze | high |
digest is the one kind whose instructions the kernel supplies in full — your
prompt for it is focus guidance only. It condenses the
outputs of the actions it depends on — quoting each source’s delivered items,
validation numbers, commands, unfinished work, and requests verbatim, one
section per source, with no verdicts of its own — so an expensive consumer
reads one artifact instead of many raw output files, and the digest entry in
that consumer’s dependency artifacts still names every digested source for
drill-down. Use one when three or more writers feed a single integrator, or
when a consumer’s dependency outputs would exceed roughly 20 KB. A digest must
depend on at least one action, owns no files, needs no affects, and no
evidence action may depend on one: evidence reads the real artifacts.
Resolution is per field: an explicit lane or effort on the action wins,
then the kind table, then an optional program-level defaults object — which
may set only effort and reasoning, because lane follows the individual
action — then the per-lane default table. A kind outside that closed list is
a validation error, not a runtime failure: it is a typo in your program, so
workflow plan validate exits 2 and nothing launches. A program that uses
neither kind nor defaults and states lane and effort on every action
validates and runs exactly as before; the one widening is that effort is now
optional and falls back to the per-lane default instead of being rejected.
Two advisories report effort smells without ever rejecting anything.
all-writers-high fires when three or more build/chore actions run and
none is below high effort; docs-at-high fires when a build/chore action
owns only *.md files at high effort. workflow plan validate includes them
as advisories in --json and prints advisory: lines otherwise, workflow
goal prints the same lines at launch, and both keep their exit codes. The
kernel stores them on the run, so workflow runs show lists them afterwards,
and runs result, runs show, and workflow action show print kind next to
lane and effort.
Reasoning depth is a third, independent decision. An action may carry an
optional reasoning field — low, medium, high, xhigh, max, or
default — that sets how hard the picked model thinks on that one action and
outranks every configured level for it. default passes nothing and lets the
worker CLI’s own setting decide. Omitting the field keeps the configured level.
It never changes the pool, model, or effort tier, so a low-effort mechanical
step can still be given xhigh thinking and a high-effort action can be told
to think cheaply. A connector that does not accept the requested level gets the
nearest level it supports.
The planner does not author phases or declare success/failure. The kernel derives stable presentation stages for the TUI and computes the final result. Saved runs of the current engine (V2) retain their original execution and workspace policy on resume. Earlier autonomous run directories (V1) are not migrated or resumed; explicitly naming one fails before any paid dispatch.
The detached response includes a short ID and exact observation commands:
bullswarm workflow runs show <shortId>
bullswarm workflow watch <shortId> # V2: attach, then one line per notable event
bullswarm workflow watch <shortId> --next # print the next notable event and exit
# relaunch with the --after/--since it prints
bullswarm workflow runs result <shortId> --json --summary # compact status-loop envelope once terminal
bullswarm workflow # unified human workflow home
bullswarm workflow tui <shortId> # jump directly to one run timeline
bullswarm workflow tui --json <shortId>
bullswarm workflow events --json <shortId> --after 0
bullswarm workflow action show --json <shortId> <actionId>
Manage a run with first-class verbs:
bullswarm workflow plan export <shortId> --out plan.json # the live plan, editable
bullswarm workflow plan revise <shortId> --program plan.json # change the plan while it runs
bullswarm workflow pause <shortId> [--now] # start nothing new; resume continues
bullswarm workflow resume <shortId> --watch # lift a pause; verb form of goal --resume
bullswarm workflow steer <shortId> --message "<guidance>" # guidance for whoever plans the run
bullswarm workflow cancel <shortId> --json # a run with no kernel is finalized here
A caller-planned program run can be re-planned at any time: while agents are working, while it is paused, or after it finished. Export the live plan, edit it into the whole program you want from now on, and revise:
bullswarm workflow plan export ab12cd --out plan.json
bullswarm workflow plan revise ab12cd --program plan.json --rerun write-docs --summary "Docs must cover the new flag"
The kernel matches the file to the live plan by action id within about a
second. A new id is added. An action left as exported is kept: its finished
result is reused and a running agent keeps going. An action with any field
changed is amended: if its agent is running it is stopped, and the step starts
over with the new definition. An id in rerun discards its finished result and
runs again. An action missing from the file is removed: stopped if running,
never run again, and reported as removed in the result instead of counting
against it. Every step that depends on an amended or rerun step runs again,
because its inputs change. A removed or rerun evidence step’s judgment stops
counting until it is judged again.
plan revise checks the revision before writing anything and exits 2 with
the issues for an invalid program, an unknown rerun id, a revision that changes
nothing, or a plan that moved since the export (baseRevision). Files a stopped
step already edited stay in the tree, so plan a repair step when that matters.
Revising a finished run reopens it: the earlier result.json is archived as
result-before-revision-<n>.json and the run finishes again. workflow pause
lets running agents finish and starts nothing new (--now stops them and runs
those steps again after resume); revisions apply while paused, and only
workflow resume continues the run.
--orchestrator <pool> expresses a preference and immediately falls back to
another eligible pool if that provider is quota-gated or unavailable; plain
--orchestrator auto leaves selection to the kernel. For controlled provider
QA only, add --orchestrator-strict to require that exact pool and fail if it
is not available. Controlled comparisons can additionally pin the exact planner
and worker routes without changing global strategy:
bullswarm workflow goal "Implement and verify the change" --cwd . \
--orchestrator codex --orchestrator-strict --orchestrator-model gpt-5.6-sol \
--worker-pool opencode2 --worker-model relay/gpt-5.6-luna
These pins, plus --suggested-plan and --no-scout, apply only with
--orchestrator. When you are the planner, the plan is the program.
The worker lock covers scout, work actions, and evidence actions. A pool that cannot guarantee the requested model is ineligible rather than silently substituting another model.
Reasoning depth can be pinned for a whole run the same way, without touching global strategy:
bullswarm workflow goal "Implement and verify the change" --cwd . \
--program plan.json --worker-reasoning high --json
bullswarm run --lane build --reasoning xhigh --prompt '<task>' --json
--worker-reasoning covers scout, work actions, and evidence actions;
--planner-reasoning covers a dispatched Workflow Planner and applies only
with --orchestrator. Exactly one level is resolved per attempt, and the
first layer that sets one wins — not the strongest: the action’s own
reasoning field, then the run-wide flag (--worker-reasoning,
--planner-reasoning, bullswarm run --reasoning), then the configured
strategy.reasoning level for that pool and tier, then the same for the tier
globally, then the connector’s own default for the effort tier, and otherwise
nothing is appended. So an action asking for low beats a run-wide max.
default at any layer stops there and passes nothing, letting the worker
CLI’s own setting decide; a connector with no reasoning block, or a model it
marks as skipped, never receives a flag. The applied level is recorded on
every attempt with the layer that set it and displayed next to the model, so a
run that thought more cheaply than requested is visible rather than inferred.
The opencode2 connector itself does not require a Relay provider: its base
spawn command carries no hardcoded model, so a plain OpenCode installation
dispatches with OpenCode’s own configured default. When
~/.config/opencode/opencode.json has one or more Relay providers configured,
Bullswarm discovers them and pins an explicit --model <providerId>/gpt-5.6-luna
per provider — the first as the primary opencode2 pool, each additional one
as its own opencode2:<id> pool — which is what the --worker-model
relay/gpt-5.6-luna example above locks onto.
New goal runs use the shared workspace regardless of the older setup
worktree-isolation preference. Add --isolation to workflow goal when you
explicitly want per-worker worktrees and strict ownership before integration.
Pass it to workflow plan contract and workflow plan validate as well so the
contract describes that run. Shared execution does no manifest scan, copying,
integration, or rollback. Its final Git inventory is advisory, includes
pre-existing/concurrent changes, and never prevents completion if unavailable.
--program and workflow planThis is the default. The calling agent (Claude Code, Codex, or any frontier
model with the repository in context) is the Workflow Planner, instead of the
kernel paying for a dispatched scout and planner that cannot see the
conversation. The kernel handles graph validation, quota routing, scheduling,
mechanical retries, optional evidence, durable recovery, and the result envelope while the
caller supplies the program, exactly the division of labour Claude Code’s
Workflow tool uses between the authoring model and its harness.
bullswarm workflow plan contract "1. Fix the parser. 2. Update the docs." --cwd . --json
# → requirement IDs (requirement-1..n), rules, action fields, validation, example
bullswarm workflow plan validate "1. Fix the parser. 2. Update the docs." --cwd . --program plan.json --json
# → dry run against that contract; exit 0 valid, exit 2 with the issues; nothing launches
bullswarm workflow goal "1. Fix the parser. 2. Update the docs." --cwd . --program plan.json --watch
# → validated before launch; executes with zero planner/scout dispatches
bullswarm workflow plan show <shortId> --json # initial scout or explicit steering pause
bullswarm workflow plan submit <shortId> --program plan-2.json --watch
Exit codes are a contract: 0 done or paused durably for you (nothing is running), 1 the run ended without completing, 2 usage or validation error with nothing launched. Every refusal names the commands that come next.
For foreground execution, exit 0 means the graph ran successfully or paused durably; it does not imply independent verification. An independent launch also returns 0 before the workers finish. Consume its eventual result.
--program accepts the planner response envelope or a bare
bullswarm.workflow.program.v2 document. An invalid program exits 2 with the
validator’s issues and nothing is launched. When the kernel reaches a planning
boundary (an initial plan, or a run about to finish with queued steering still
unread), it writes planner-request-turn-N.json, sets the run to waiting,
exits, and watch prints the plan show command. A submitted program contains
only new actions and is validated against the exact durable state at that
boundary. Steering queued while work is still running does not stop it: watch
prints steering received and the caller answers with plan revise (see
Changing the plan of a live run). Older saved V2
runs still support their original gap boundaries and --exhausted submissions.
--scout without
--program runs the kernel scout first and pauses at the initial boundary so
the caller plans against a real survey; scout units are advisory for a caller
planner.
An action’s result envelope is covered in Operations; the JSON example program shape (writers, a digest, and one integrator) is in Operations, Context diet.