Falsifying Swarm Orchestrator
ActionsAbout
A coding agent whose claims about its own work resolve to machine-captured evidence.
The model can say whatever it likes. It cannot make a gate pass, it cannot mark a claim verified, and it cannot change a record after the fact. Those are the harness's to decide, and the run exports a signed, hash-chained bundle that anybody can check without installing this tool.
What it does | Install | Use | Several workers | Watching it work | What is claimed | What is not claimed | How it works | Limits | Upgrading from v12
Give it a task and a git workspace. It plans, declares the files it intends to touch, edits through a chokepoint that records every tool call, runs your gates, and retries failures under a numeric ratchet that refuses a fix which trades away tests, assertions, or coverage. Then it exports the evidence.
Here is a real run, committed: live-tasks.md,
with its bundle in live-frontier/. And here is the
packaged tool doing it, installed from a tarball into a directory holding nothing else, against
a workspace it had never seen, recorded in a real terminal:
installed-package-run.md.
npm install -g swarm-orchestrator
That is 13.1.9, and it leaves swarm on your path.
Installing from a tag works too, but only into a project rather than globally:
npm install github:moonrunnerkc/swarm-orchestrator#v13.1.9
dist/ is not committed, so a git ref builds itself on install and needs this package's
devDependencies to do it. npm does not install those when a git ref is installed with -g: it
carries the global context into its git-dependency preparation, places the clone as a root
package, and runs the build without the compiler. The published package needs no build, which
is why the line above it is the one to use.
Anything below 13 is a different program. This package name carried a pull-request auditor
through 12.x, and npm install -g swarm-orchestrator@12 still installs it, so pin the major if
you depend on one or the other: Upgrading from v12.
If swarm turns out to be an older version than you installed, something else owns the command.
The usual cause is a development checkout linked into the global prefix with npm link, which
owns it until it is removed and which npm cannot install over: the install either fails renaming
a symlinked directory aside, or succeeds behind a stale executable still pointing at the
checkout. swarm doctor says which of those happened and swarm doctor --fix repairs it.
Node 24 or newer. That is a runtime floor rather than a preference: the coverage cycle
spawns the test runner with --test-isolation=process, which Node 22 rejects as a bad
option, so on anything older that measurement does not happen.
swarm "make slugify collapse whitespace and strip punctuation"
swarm # a session: type tasks, one after another
swarm doctor # what owns the swarm command, and --fix to repair it
swarm gates # run the gates over a workspace, no model
swarm select # probe this machine, recommend a local model
swarm calibrate # measure candidate models on the golden set
swarm routing # what the reward log adds up to
swarm parallel --tasks <file> # a worker per task, then a merge queue
swarm parallel --goal <text> # break the goal into tasks, then run them
--redundancy <n> # try each task n ways, land the best of them
--concurrency <n> # how many workers may hold a worktree at once
swarm review <bundle> # what a past run produced, and open it
swarm replay <bundle> # read a bundle back
swarm --no-tui "..." # plain lines even on a terminal
swarm --no-color "..." # no colour, whatever the terminal says
swarm --open-evidence "..." # open the review page when it finishes
swarm --model local:<id> "..." # a specific model
swarm --workspace <dir> "..." # a repository other than the current directory
swarm --base <ref> "..." # what the diff and the ratchet measure against
swarm --attempts <n> "..." # how many times the ratchet may retry a gate
swarm --max-steps <n> "..." # how long the loop may run before it stops
swarm --local-endpoint <url> # an OpenAI-compatible server other than the default
swarm --help prints all of it, including the calibration flags.
Set ANTHROPIC_API_KEY, OPENAI_API_KEY or GOOGLE_GENERATIVE_AI_API_KEY for a frontier
model, or start Ollama or rapid-mlx and pass --model local:<id>. With no model named, the
router picks one from what the calibration measured on this machine, and swarm routing shows
what it has learned.
Settings live in one optional swarm.toml: providers and endpoints, gate definitions, budgets,
model pins, and the [interface], [theme] and [keys] tables. Flags win over the file.
Two of those are worth knowing about before you need them.
[providers]
local_thinking = false # the model behind the local endpoint answers without reasoning first
[interface]
confirm_timeout_minutes = 30 # an unanswered confirmation refuses itself; 0 waits for ever
local_thinking matters on a reasoning model served locally. Left unset, nothing is sent and
the server's own default stands, which is the only safe default: the field is a vendor
extension and a server that rejects what it does not recognise would fail every call rather
than one. Set it, and a model that would otherwise spend its whole output budget thinking
answers instead. Against rapid-mlx serving qwen3.8:27b, one request cost 37 completion tokens
with reasoning on and 2 with it off. Ollama's OpenAI-compatible route ignores the field; that
is the server's limit rather than this one's.
confirm_timeout_minutes is why a run left alone no longer waits for ever. The chokepoint asks
before it runs a command that is not on the allowlist, and a question nobody answers used to
hold the run until somebody came back to it. Refusing is what a declined question records
either way, so the deadline costs that one tool call and the run carries on.
Run swarm with no task and it opens a session: one process, one ledger, and tasks typed one
after another, each continuing the conversation the last one left.
› create calculator.js exporting add and multiply, and calculator.test.js covering both
8 gate(s) passed, 8 step(s)
› add a divide function that throws on division by zero, and cover both cases
Each turn is measured on its own. A turn ends by recording where it left the tree, so the next
one's gates see that turn's changes and not the ones before it. Three turns of this, with the
bundle verified from outside and the page it produced, are in
session.md.
swarm "task" still runs one task and exits, exactly as before.
swarm parallel gives each task a git worktree of its own and a merge queue that lands them
one at a time under the same ratchet a single run answers to. Nothing is merged into the
branch you are sitting on; the result waits on an integration branch and the report tells you
how to take it.
Three things sit on top of that, each optional and each off unless asked for.
Workers read each other's ledgers. They coordinate through the record they were already writing, not through a bus or a daemon: which files a peer has declared, which gates have failed on it and how often, which approaches it has already spent its attempts on. Nothing a worker reads there can render green. Every signal comes from a ledger record rather than from model text, every line names the peer it is about, and no signal reports a success, so there is nothing in it to mistake for a gate result.
--redundancy <n> tries each task several ways and lands the best of them. The winner is
chosen by reading which attempt moved the measured numbers, never by asking a model which
answer it likes. The whole ranking goes on the chain, losers and the reason each was left out
included, so you re-read the choice instead of taking it.
--goal <text> breaks the goal into tasks itself. A planner reads the workspace with
read-only tools and declares a task graph, which is checked for unique ids, resolving
dependencies, no cycle, and files that two unordered tasks do not share, and recorded before
the first worker starts. Nodes land layer by layer.
Two runs of this, committed with their bundles, are in
swarm.md. The second one is the more useful: every
structural check passed on a decomposition that could not work, because the planner left out
a dependency. Whether a set of tasks adds up to a goal is a judgement about meaning, and this
tool makes none.
On a terminal, a run draws a single screen you can drive. Off one, it writes the same plain lines it always did, so pipes and CI are unchanged.
swarm make the parser trim before it splits
local:qwen3-coder:30b-a3b /Users/brad/projects/scratch-repo 20s step 4 5812 tokens attempt 1/3
plan
read the failing test, fix the parser, run the gates
actions
edit path=src/parse.ts find=text.split replace=text.trim().split
shell command=npm test
shell failed: 1 failing
ratchet accepted: tests collected 12 to 12, assertions 34 to 35, skips 0 to 0
gates attempt 1/3
PASS tests: 12 collected, 0 failed
PASS lint: no findings in 208 files
N/A coverage: no lcov artifact was written to the path the harness named
WARN diff-budget (advisory): 1 file changed, 1 line added, budget 12 files and 400 lines
DONE stopped: completed (4 steps, 5812 tokens)
j scroll enter expand tab pane / filter e evidence ? help q detach ctrl+c cancel run
While it works there is a line that says so: a spinner that turns, what is happening, how long it has been happening, and, while the model is talking, the tail of what it is saying.
⠙ thinking, step 2 5s I don't see calculator.js in the root directory
⠹ shell npm test 12s
One line, deliberately. The whole response lands in the action stream when it arrives and in the ledger for ever, and repeating it as it streams would be the same text three times. A tool that finishes inside a frame never draws one.
? lists every key. enter expands a row to its whole payload and the ledger record it came
from. q leaves the view: the screen comes down and the run keeps going, reporting the plain
lines it writes off a terminal. ctrl+c cancels the run. There is no progress bar, because an
agent run has no denominator.
When the run ends, the screen lists what it produced, says how many claims the harness
verified and how many it refused, and offers to open the review page. It says the bundle
verified only if the bundle's own verifier ran here and exited 0. swarm review <bundle>
shows the same panel for any bundle already on disk.
The keymap, the swarm.toml surface, the degradation matrix, and a recording of a session
are in interface.md, with the frames in
interface-frames.txt and a playable
asciinema capture in interface.cast.
Every line here links to a committed artifact of the thing happening. The full table,
including what backs each one, is docs/claims.md.
A green verdict is computed by the harness, and the model cannot produce one. In a real
run, the model asserted a predicate the language does not parse. The harness rendered it
UNVERIFIED (predicate-unparseable) and carried on, twice, until the model wrote one that
could be evaluated:
shakedown results.
One changed byte breaks verification. The same bundle, verified and then tampered with in a single byte of one record, exit 0 and exit 1 side by side, with a script to reproduce it: tamper demo.
The bundle carries its own verifier, and it works on a machine that has never seen this
repo. node verify.mjs <bundle> checks the manifest, the chain, the signature, every blob
against its content address, and recomputes every claim verdict. Run in a node:24 container
with no network and no mount of this repository: exit 0 on the committed bundle, exit 1 on the
same bundle one byte later, with the image digest and both transcripts in
clean-container-verification.md.
Transcripts from two earlier runs outside the repository:
live-tasks.md.
Bundles are signed from the OS keychain, not from a key in the workspace. Both manifests
in live-tasks.md say "keySource": "keychain" and
both verifiers confirm it. Where the keychain holds no usable key the run signs with a per-run
key and says which of the three keychain failures happened, rather than signing quietly with
something else; the manifest then records keySource: ephemeral, which is what the bundles in
installed-package-run.md carry.
Local model choice is measured on your machine. The probe output and recommendation from
real hardware: hardware-select.md. A hundred
and eighty calibration runs across three models, distributions rather than averages, with the
pick ranked against the other two:
calibration-report.md.
Eight untrusted boundaries are fuzzed, and the harnesses are checked against a defect
injected on purpose so a clean run cannot be a blind one: fuzz/ and
security-coverage.md.
The screen renders from ledger projections, and a keystroke has no route to a verdict.
Interactive state is a separate type with a separate reducer, and a test asserts across every
action that none of its fields can be mistaken for a gate result:
interface.md.
A choice between competing attempts is made from measured numbers. One task tried three
ways: three green attempts, coverage measured rather than abstained on, the third ranked last
on the dimension that saw it wrote one test fewer, and the top two identical on everything so
the tie broke on the earliest, which the report says in those words rather than inventing a
reason it did not have: swarm.md.
Adversarial passes, with each closure locked as a regression test. Six pass directories
under redteam/, 49 cases in src/evidence/redteam-adversarial.test.ts, and an
accounting record mapping each pass to what it actually was, because the driver ledger
records one completed lap and a directory is not a lap.
Kept short and kept honest, because the point of the rest of this file is that claims cost something.
- Not "fully secure". The secret detector does known-pattern scrubbing, not secret removal, with a four-character floor. Zero crashes at a fuzz budget is evidence, not proof.
- No benchmark numbers. Nothing here is measured against another tool. The calibration results are self-run, on one machine, and labelled directional in the file itself.
- Six known gaps are open, not closed, and they ship that way. They are in build guide 7.1. Four came out of the adversarial passes and each is a permanent test case asserting the gap as it stands; two came with the scale-out work and are the same boundary in a new place, since a decomposition that is well formed can still be unrunnable and a comparator built from gate measures reads discipline rather than completeness.
- A signature does not make the machine honest. It proves the bundle was not altered after it left the machine that produced it. The review page says that on its face.
- Claims are predicates, not prose. A claim names a record, a record kind, and a machine-checkable predicate. The harness recomputes the record's kind, evaluates the predicate, and decides. Model narrative always renders as unverified prose.
- The ledger is append-only and hash-chained. Each record carries the previous record's hash. A failed write aborts the run. Nothing is updated or deleted, ever.
- Every tool call goes through one chokepoint that records it, tags its provenance, and enforces the sandbox. Credential paths are denied by default and each denial is itself recorded.
- Gates are data. A gate declares a command, a parser, and whether it blocks. The engine never special-cases one.
- The ratchet is numeric. Tests collected, assertions in touched test files, coverage of changed lines, skip markers. Coverage comes from a report the runner wrote where the harness told it to, never from what a gate printed, and an unobtainable measure is reported as "not measured" rather than as a pass.
The full design, including what it refuses to build and why, is in
docs/build-guide.md.
Gates prove mechanical quality. They do not prove design quality, and nothing here pretends a passing run means the change is good. What the bundle buys you is that reviewing the change is fast and that its claims are checkable, not that review is unnecessary.
v12 was a PR auditor that ran as a GitHub Action. v13 is a coding agent. Same package name,
different product, no migration path. If you are using v12, stay on it: it is tagged
v12-final. Details in CHANGELOG.md.
ISC. See LICENSE.
Falsifying Swarm Orchestrator is not certified by GitHub. It is provided by a third-party and is governed by separate terms of service, privacy policy, and support documentation.