rig3.5.3

performance

Numbers, not adjectives.

Every number on this page is computed, and each one names the mechanism that produces it. The store numbers come from one SQL read of the session state — the same arithmetic sessions summary runs. The tree numbers are re-read live by scripts/readme-measured, and CI refuses drift, so a number here cannot quietly go stale.

The honest scope: these are this repo's own numbers, over 3,444 sessions and 67,699 turns of real work — not a cross-harness benchmark, and not a claim about your endpoint's tok/s. Where a number came from one machine, it says so.

7.6Bprompt tokens served
97.7%of them served from cache
67,699recorded turns, over 3,444 sessions

the cache ratio

About 98 cents of every prompt dollar

The ratio is cache_read / prompt, and the reason it is so high is not a trick: the request's prefix never changes a byte. The system prompt, the tool schemas and the message order are lean and byte-stable by construction, so the provider's cache can reuse almost the whole conversation on every turn. A stray timestamp, a random tool ordering, or a reworded guideline would quietly kill it — so the tests hold the line:

prompt

TestSystemPromptIsByteStableAcrossBuilds — the preamble is a constant, not a composition.

wire

TestWireMarshalingIsDeterministic — one request marshals to the same bytes every time.

messages

TestWireMessagesAreAppendOnly — the conversation grows at the end; it does not re-order behind you.

bytes

The wire job renders the request bodies at the merge-base and at the head and posts their diff: a byte that moves is reviewed, not trusted. The menu aims under 15,000 characters and the build fails past 16,500.

One ratio spans two provider wires, and the denominator is each endpoint's own number: on the OpenAI wire the cached tokens sit inside prompt; on the Anthropic wire rig sums input with the cache read and the cache write to land on the same convention. One ratio over both, and the convention is the reason it holds.

era by era

The kink is visible

Every recorded turn since the first store, split by the version each session recorded at start. New tokens per turn is (prompt − cache_read) / turns: what the model actually had to read cold.

0.x

14,006 turns · 1,499 new tokens a turn · 722 out · 98.65% cache

1.0–1.3

7,440 turns · 933 new tokens a turn · 676 out · 99.13% cache

1.4–1.9

10,045 turns · 962 new tokens a turn · 525 out · 99.26% cache

2.0–2.9

7,360 turns · 906 new tokens a turn · 503 out · 99.09% cache

2.10.x

14,785 turns · 3,946 new tokens a turn · 667 out · 96.37% cache

The 2.1.x consolidation rethought the system prompt and the toolset and kept the machinery: the 2.0–2.9 row carries slightly fewer new tokens per turn than the era before it, and the cache line holds. The last row is the fleet's own traffic — swarm workers and delegates are short-lived sessions whose first turns cannot hit a cache that does not exist yet, and the models rotate. The interactive sessions of that era hold the ~98% line; the row is honest about the dip, and the reason is on the row.

the size of things

What the whole machine costs to own

448lines is the loop — loop.go + batch.go, stdlib only
686lines is the whole swarm — claim, spawn, complete, verdict, reap, the status throttle
55,116lines of Go, with 90,302 lines of tests

The loop is the part you must trust, so it is the part kept small. The rest is a tree of leaves that plug into typed seams — a tool, a provider, a policy, a frontend, a middleware: one file plus one registration line, and the loop never names a concrete type. The one store dependency is pure-Go SQLite, which is why the binary has no runtime beside it.

1324 commits. Most of them written by rig, running inside rig — the git log is the receipt.

mechanism → number

Every figure has a cause

cache

97.7% of 7.6 billion prompt tokens — the byte-stable prefix, tested at the build.core/ · policy/compact · scripts/wire-check

concurrency

Reads overlap eight at a time; effects serialize in call order; nothing polls, so a waiting step costs no timer and no lock.loop/batch.go · kernel.DefaultParallel

ordering

One priority queue orders input 90, a delegate's return 70, a write 60, a read 50, the fleet 30, the reviewer 10 — a worker's message costs one post.evt/ · kernel.go

stores

One SQLite file per concern, WAL, migrated on open; the session summary is one read, not a replay.store/ · store/state

bounds

Every tool result is capped at 64 KiB by default with a loud [TRUNCATED] marker; identical re-issuance is bounded per turn; a corrected call always runs.middleware/guard

the room

No router process, no poll: a claim is ordered by priority, then tick, member, nonce; the port is read from the world at a join.room/ · specs/SPEC_ROOM.md

the room, measured

Why the router had to become an order

The room's design note carries its own numbers, from one machine (battlestation, 2026-10-11, huihui-alpha-s on the Maya engine behind llama-swap). They are the reason 3.0.0 removed the router:

6workers arriving first — the operator's turn waited behind whichever one reached the server first
64 → 90tok/s when two conversations decode at once, against 64 alone
45tok/s median for a single overlapped stream — one in ten under 32, the slowest at 12; alone, the slowest was 55

The engine's fair slice lets a waiting request cut into a long answer — but only a request that is waiting at the engine. So the fix was not a faster router; it was deciding who is waiting. The room orders it: the operator's turn 90, a delegate's return 70, a fleet worker 30, a child's start or stop 20, an outside client 0. A swap takes about 50 s and ends whatever is in flight, so swaps are ordered acts too, at the holders' end.

The mesh's cost is measured the same way: one horizon (four periods, 250 ms by default) at the front of a remote request, about 1.3 s on loopback at the defaults — the price of a claim that must reach the key's home machine. A lone mesh member idles between ticks.

reproduce it

The receipt is the store

Nothing here is typed by hand. scripts/readme-measured reads the stores, writes the aggregates to docs/measured.json, and renders the marked block in the README; the check mode re-reads the tree and refuses when the README drifts. The site's numbers are rendered from the same file by scripts/site-build, so the page, the README and the store agree by construction.

# the aggregate, from your own sessions
python3 scripts/readme-measured update

# the drift gate CI runs
python3 scripts/readme-measured check

what the page does not claim

No cross-harness benchmark, no tok/s league table, no "faster than X" — those depend on your endpoint, your model and your machine, and a number measured on someone else's box is a story, not a measurement. The store numbers and the room's numbers are ours, and each names its mechanism and its date. If you want to compare, run your own traffic through both harnesses and read the stores.