Dry Prototype
Before spending a cent on a real model, can the whole machine — the arena, the shared tool archive, the sandbox that runs untrusted bot code, the replay recorder, the report — actually work end to end?
A deterministic mock mutator stands in for the LLM: it swaps directions and pastes in a few canned "weird" templates instead of writing anything genuinely new. No API key, no cost. I let it run for roughly 210 generations across a few sessions, purely to shake out plumbing bugs.
Nothing yet — this is the first build. The loop itself is the thing under test, not any particular idea inside it.
- Archive coverage — the percentage of behavior cells (a grid binned on aggression × mobility) that have ever been filled by a winning bot.
- Whether the sandbox could run arbitrary generated code safely and record full match replays.
- Whether a handful of hand-written baseline bots (a camper, a rusher, a random walker) could reliably beat a bot with no strategy at all — a sanity floor for the whole arena.
The loop, archive, sandbox, replays, and report all worked end to end at zero cost. Archive coverage reached somewhere between 50% and 78% across the accumulated sessions, and the mock mutator extracted 3 reusable tools into the shared library — even though the mock bots barely used them. All three hand-written baselines beat the no-strategy bot with zero losses across 10 seeds, so the arena's win condition was doing real work.
The more interesting finding had nothing to do with intelligence: even a completely mindless mutator, wired to nothing but random shuffling and a survival filter, produced corner-campers and ambushers. Selection pressure alone was enough to generate behavior that looks strategic. That's a useful thing to know before crediting any future run's cleverness to the model doing the mutating.
Two bugs, both caught the same way — by watching the raw output instead of trusting a summary number:
- The replay picker was broken. The "interestingness" score used to pick the ten most interesting matches for the report saturated at a maximum value (equal damage dealt on both sides, zero margin), so ties were resolved by insertion order — meaning "top 10 replays" was silently just the ten oldest replays in the database. Fixed by picking the best replay from each generation-era instead of a single global top 10.
- A silent engine fallback. A synchronous-only stdin option made the CLI mutation engine quietly fall back to the mock mutator while still reporting a successful run. This one was only caught because the run summary's call count read
apiCallsMade: 0, which contradicted a report that otherwise looked completely normal. The lesson: a proxy metric (report looks fine) is not proof (calls were actually made) — the discipline that shaped every later run's instrumentation.
Build in honest failure handling (a loud abort instead of a silent fallback), an era-spread replay picker, and treat the raw API-call count as the one number in every report that can't lie. Then spend the first real dollar on Run 1.
This run made no API calls at all — it ran on the free deterministic mock mutator.
Series through Run 0: 0 calls · $0.00–$0.00. Call counts are exact, straight from each run's metrics file. Costs are a range rather than a figure because the provider bills cached input tokens at half price and does not report the cached fraction in the API response, only in the console dashboard, so the upper bound prices every input token as uncached and the lower bound prices all of them as cache hits. A sampled dashboard minute showed 68% of input tokens were cache hits, which puts the truth nearer the bottom of each range. Token counts are measured exactly from Run 8 onward; earlier runs additionally reconstruct tokens from Run 8b's measured per-call profile (3,642 in / 1,348 out), because the usage field was discarded before it was ever written down. A bound is not a measurement and a reconstruction is neither, so both are labelled.
Raw receipts — every archive, lessons ledger, metrics file, and match replay: github.com/godfreystorm/ratchet-lab