Own the factory. Rent the agents and the intelligence.
flywheel is a dark factory for AI coding work: a small Go CLI plus agent skills. The factory (work orders, gauges, the event log, its memory) is files you own; agents and models are rented from any vendor and swapped at will. Cheap disposable agents build, deterministic gauges re-measure every claim, and one frontier lead makes only the critical calls. It is for engineers who run coding agents in parallel and want every change measured, recorded and recoverable.
See it run: the live demo replays a real session and lets you drive the real factory view.
For agents: agents.html is one page with the roles, the loop as exact commands, the brief format and the rules; llms.txt indexes the docs.
Install
Download flywheel-<version>-<os>-<arch>.zip from the
latest release (Windows binaries ship as
flywheel-<version>-windows-amd64.exe.zip) plus checksums.txt, verify the SHA-256, and put the
binary on PATH renamed to flywheel (the archive holds a single binary named after the release).
Or install with Go (the module is github.com/suzworx/flywheel; the binary lands in
$(go env GOPATH)/bin), or build from source:
go install github.com/suzworx/flywheel/cmd/flywheel@latest
# or
git clone https://github.com/suzworx/flywheel.git && cd flywheel
mkdir -p bin && go build -o bin/flywheel ./cmd/flywheel
flywheel upgrade --check tells you whether a newer release exists and flywheel upgrade
installs it, checksum-verified. It refuses (exit 6) while a run's lease is live in --dir
(default .), since swapping the binary can crash that run; wait for it (flywheel wait) or pass
--force. Then install the skills into your agent, one per skill folder (Skills):
npx skills add suzworx/flywheel --skill flywheel
npx skills add suzworx/flywheel --skill flywheel-worker
# ... flywheel-planner, flywheel-foreman, flywheel-inspector, flywheel-reviewer,
# flywheel-auditor, flywheel-steward, flywheel-operator
60-second tour
Offline, no API key: the sim adapter replays a recorded worker run instead of calling a model.
In an empty git repository:
flywheel init
flywheel config set workers.default.adapter sim
flywheel config set workers.default.model .flywheel/sim.jsonl
cat > .flywheel/sim.jsonl <<'EOF'
{"type":"text","sessionID":"ses_demo","part":{"type":"text","text":"Wrote hello.txt. Gate passes."}}
{"type":"step_finish","sessionID":"ses_demo","part":{"type":"step_finish","reason":"stop","tokens":{"input":120,"output":40},"cost":0.001}}
EOF
cat > .flywheel/briefs/hello.txt <<'EOF'
owns: hello.txt (new)
needs: none
gate: grep -q "Hello from flywheel" hello.txt
# TASK: hello — write a greeting file
Write hello.txt containing the line "Hello from flywheel". At most one write per response.
## Checks
Report the commands you ran and their real exit status.
EOF
flywheel lint .flywheel/briefs/hello.txt # 0 problems, 0 warnings
flywheel log --task hello --kind planned --brief .flywheel/briefs/hello.txt
flywheel run hello
hello r1 dispatched sim .flywheel/sim.jsonl
hello r1 finished rc=0 reason=stop model=.flywheel/sim.jsonl steps=1 tokens=160 cost=$0.0010
hello r1 finished without writing a file
hello r1 never ran gate(s) 1
The worker says the gate passes. The gauges measure instead of believing it:
$ flywheel validate hello # exit 5
hello gate 1: failed (rc=2)
hello note: the worker never ran gate(s) 1 itself; its report's claims about them are unmeasured
hello owns: ok
hello validate: fail: gate 1 (rc=2)
$ flywheel inspect hello --verdict pass --session lead-1 # exit 6
flywheel inspect: T3: no passing supervisor validated reading for gate 1 on tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 after the latest finished event; run: flywheel validate hello
$ echo "Hello from flywheel" > hello.txt
$ flywheel validate hello # exit 0
hello gate 1: pass (399ms)
hello owns: ok
hello validate: pass
$ flywheel inspect hello --verdict pass --session lead-1 # exit 0
hello inspected pass
$ flywheel factory --once
units (1)
TASK STAGE ATT SESSION MODEL STEPS AGE RUN
hello passed r1 .flywheel/sim.jsonl 1 0s done
With a real worker (claude, codex, opencode or pi) the file is written by the agent; see the
Quickstart for the full path to a landed unit.
Docs
The site renders all of these: suzworx.github.io/flywheel.
- Quickstart — from a binary to one landed unit through the loop.
- Concepts — work orders,
owns:, gates, the event log, the poka-yoke rules. - Protocol — the normative contract: every event kind, rule and exit code.
- Features — the long walkthrough: adapters, routing, limits, recover, worktrees, the review agent and panel, freeze and resume, the wake schedule, ship.
- Factory screens (docs/screens.html)
— the approved k9s-style design for
flywheel factory, each screen marked shipped, building or planned. - HUMAN.md — leading the agents yourself from the terminal.
- Design docs:
- Autonomous shipping — the factory model, the required events, the poka-yoke transition rules, and the enforcement layers.
- Flywheel at scale — personas, many parallel workers, per-task worktrees and landing, native feedback, and offline use.
- Factory as files
- Runtime: architecture, gaps, design, plan
- Review calibration — the review agent measured against past PRs.
- Skills — one skill folder per factory role; AGENTS.md holds the house rules.
Built by flywheel
Flywheel is built by flywheel. Sujeet (@suzworx) runs its development as a dark factory.
- From v0.3.0 on, flywheel's own features (
flywheel run, the gaugesvalidate,inspectandverify, theflywheel factoryview), the CLI help and flag fixes, and the factory design docs were built as work orders by disposable OpenCode workers on DeepSeek v4 flash (variant max). - Since September 2026 the workers are
claude-haiku-4-5agents dispatched through theclaudeadapter, and the lead is Claude. Across 30 units (33 attempts) a unit cost a median $0.55 (range $0.27-1.63) and took a median 14 minutes; every attempt finished cleanly, yet the lead's review and an independent PR reviewer still caught defects in most units, which is why every claim is re-measured rather than trusted. - Each work order is a brief with
owns:,needs:andgate:lines. flywheel's own gauges re-run the gates on the exact tree hash and checkowns:; inspection andflywheel verify(rules T1, T3, T4, T5 and T8) gate every commit. Workers never commit. - One frontier lead seat outlines work orders, reviews diffs, runs checks against the built binary and signs off. The lead seat changed hands between vendors mid-project and the work continued from the files.
- Measured worker cost: the CLI help and flag work took 7 units, 1.7M tokens and $0.33; a design doc was drafted, corrected and extended by 5 units for about $0.03; a code unit costs $0.03-0.08 and takes 15-29 minutes.
The house rules the workers follow are in AGENTS.md.
The factory
Flywheel is a factory for AI coding work: a small Go CLI plus agent skills that run a durable orchestrator-to-worker loop. A frontier lead agent plans, briefs and judges; cheap disposable worker agents (Claude Code, Codex or OpenCode) do the reading, writing and testing. The control plane dispatches work and enforces policy; the data plane keeps traceability, telemetry and accountability for every session.
Roles
| Factory role | Persona | Does | Never |
|---|---|---|---|
| Plant manager | lead | sets goals and policy, handles escalations, signs off | implements |
| Production planner | planner | turns a spec into work orders (owns:/needs:/gates) |
dispatches or inspects |
| Line supervisor | foreman | runs a line of workers, retries by policy, pulls the cord | plans or implements |
| Line worker | worker (any agent CLI) | builds one work order at its own station | plans, inspects, commits |
| Machine gauges | supervisor (CLI, no model) | measures every unit: runs gates, checks owns: |
judges intent |
| QC inspector | inspector | inspects a unit against its work order | runs gauges or fixes units |
| External auditor | auditor | audits first articles and samples, files nonconformances | works the line |
| Continuous improvement | steward | turns signals and nonconformances into learnings | changes units |
| Owner | operator | installs, assigns roles, sets merge and publish policy | — |
A unit's path
flowchart LR
WO["Work order"] --> W["Worker (agent CLI)"]
W --> G["Machine gauges"]
G --> I["QC inspector"]
I --> A["Auditor (first articles, samples)"]
A --> L["Landing"]
I -. "rework" .-> W
Set up, run, watch
- Set up —
flywheel initscaffoldsflywheel.mdplus the.flywheel/state files and ends with a factory summary: worker lines, limits, audit policy and the enforcement layers installed (with the command for each missing one). The default worker uses the first agent CLI it finds on PATH (claude, opencode, codex, then pi:npm install -g @earendil-works/pi-coding-agent, modelsprovider/id), or the one--adapter NAMEchooses. The full factory is epic #69. - Run — the lead records each work order with
flywheel log --kind planned;flywheel rundispatches it through theclaude,codex,opencode,piorsimadapter and records the run;--worktreebuilds each unit in its own.flywheel/worktrees/<task>on branchfw/<task>. - Watch —
flywheel statederives the floor from the log,flywheel factoryopens an interactive k9s-style view of it,flywheel watchstreams every event as one line, andflywheel wait <task>...blocks until the named units finish. In the view,:pulseand:metricschart the metrics and drill to the units behind each number (factory view);flywheel fleet statusmerges every factory on the machine into one table (fleet).
The details (bases, --notify, the TREE column, withdrawing a plan) are in
docs/features.md. The planned screens of the factory
view, each marked with what exists today, are on the screens page.
- Set up —
flywheel initscaffoldsflywheel.mdplus the.flywheel/state files (available in v0.2.0). It ends with a factory summary — the worker lines, limits, audit policy, which enforcement layers are installed (and the command for each missing one) and how to view the floor. Building the full factory — lines, staffing, and the policy that keeps it safe — is epic #69. - Run — the lead records each work order as an event with
flywheel log --kind planned;flywheel rundispatches it to a worker through theclaude,codex,opencodeorpiadapter and records the run automatically. For parallel unitsflywheel run --worktreeis the default: each unit builds in its own.flywheel/worktrees/<task>on branchfw/<task>, against the main ledger. Each tree's worker, setup and gates getFLYWHEEL_SLOT, a per-tree number for ports and databases, so parallel units never share them (#697). A resource they must share (one local database, a fixed port) goes on a brief'sresources: e2e, dev-dbline:flywheel validatethen holds an exclusive lock per resource, per repository (all its worktrees on this host), while a gate that uses it runs (gate[resources]:, or every gate when none is marked), so two units never run those gates at once; it serialises validate's gates, not the worker's own runs (#697). A gate waiting for another unit's lock records aresource_waitevent as the wait starts, andflywheel factoryshows it live in the unit's RUN cell (waiting for resource e2e (<holder>) 2m) until the gate's reading ends it (#697). A gate whose suite already fails on the unit's base is writtengate[vs-base]:with afail-match: <regexp>line naming the failing-test lines: validate reruns it at the base (cached) and passes it only when the unit adds no failing line the base lacks, printing the debt (passed vs base <sha>: 24 failing on base too) (#788). The debt stays visible asN failing on base too: the floor showsvs-base <n>on the unit's row,inspect --verdict passprints each such gate, andflywheel verifychecks every vs-base pass (rule V1).--worktree --base REFbranches a newfw/<task>from REF, without checking REF out. Without--basea newfw/<task>starts fromorigin/<integration.branch>(else the local branch) whenintegration.branchis set, and is refused when neither resolves; otherwise from the main checkout's HEAD, with a warning when HEAD carries commitsorigin/mainlacks (#550). Thedispatchedevent'sbaserecords the commit the unit branched from.flywheel run --workdir PATHruns the worker in an existing tree the lead prepared (a merge in progress, say) while events still go to--dir's ledger; it is refused (exit 6) with--worktreeor--base, or when PATH is not a git working tree of the same repository.flywheel run --session ID(default$FLYWHEEL_SESSION) records the dispatching lead session as the dispatched event'slead, so leads sharing one ledger can tell their units apart. - Watch —
flywheel statederives the floor from the event log;flywheel factoryopens an interactive, k9s-style view of the floor (:units:workers:andon:events:linesto switch,/to filter, enter to explain a unit,lfor its log,?for help,qto quit;--plainkeeps the plain redraw), andflywheel watchstreams every event as one readable line. When any unit runs in a worktree, the plain floor's (flywheel factory --plain) units table adds a TREE column with that worktree and its base commit (CP-A@abcdef1), and the--jsonview carriesworkdirandbase. - Know when a unit finishes — never background a dispatch with a bare
&and hope to notice:flywheel wait <task>... [--timeout D] [--notify CMD]blocks until each named task finishes its current (or first) attempt, printing<task> <attempt> finished reason=<r>as each lands (exit 0 all clean, 4 any unclean, 8 timeout), andflywheel run <task> --notify CMDrunsCMDthrough the shell when the run returns on any path, withFLYWHEEL_FINISHED="<task> <attempt> reason=<r> exit=<code>"in its environment; a failing notify only warns.flywheel wait --notify CMDrunsCMDthe same way once per task whose finish is not a cleanstop(arate-limitedorerrorunit), and a provider-error finish is retried once in process after 2 minutes (limits.provider_error_retries, default 1) (#830).
Design priorities, in order: efficiency and consistency, then speed, reliability and recoverability — the lead spends tokens only where judgment is needed, gauges and telemetry cost no tokens, and everything replays from the log after any crash.
Why flywheel
Frontier-quality results at a fraction of frontier cost — measured, not promised: the field run
behind the skills used 99 worker runs across 44 tasks, up to 7 workers in parallel, and about
90 % of all worker input across the field run served from the model cache. max_parallel caps
concurrent workers, not concurrent gates: units whose gate: lines name the same expensive
command serialise on it, and the contention manufactures false findings (phantom timeouts) that
read as code defects — flywheel run prints a shared-gate warning at dispatch whenever an
in-flight unit declares the same gate line.
The goal is to ship with no human in the loop. That is only safe if every step is recorded (an
append-only event log), measured by the machine (gauges run by the CLI, never self-reported by
an agent), and audited by independent checkers. Today all three are in place: the append-only
event log is hash-chained, so an edited record, or one removed from the middle, is detected
(flywheel verify --log; cutting records off the end needs an outside anchor such as a pushed
commit); the gauges validate, inspect and verify measure
every unit, with flywheel supervise measuring each finished unit nobody has measured yet; and
flywheel audit re-measures a unit in a clean copy from a session that neither built nor inspected
it, selecting first articles and seeded samples
(#61). Enforcement sits in more than one layer:
the CLI refuses illegal transitions (exit 6), an agent Stop hook runs flywheel gate, and git
hooks check every commit and push (flywheel init --git-hooks; the pre-push hook runs
flywheel verify --shipped, so a unit that was only planned, never built and judged in flywheel,
cannot be pushed); whatever the worker's adapter,
flywheel run flags an attempt that moved HEAD, switched branch or stashed with a git-write
signal, which blocks landing until the lead triages it (it compares the attempt's end points, so a
push or a write undone before exit needs the command-level guard,
#319). That guard is in place: flywheel run
puts a git shim first on the worker
