Workbench

gauntlet

A test suite checks each station on its own. gauntlet walks in as a diner, orders and pays, then goes behind the counter and follows every hand-off from the farm to the plate.

Stage
Verify
Since
Jul 2026
Leaves
A run diary + findings in AUDIT.md
“You're like 'user' and a 'god' of the app.”
Me, Jun 2026

The user and the god, at the same time

I asked for this the night we were testing Fantopy's World Cup knockout. Play the game as a manager from the round of 32, and at the same time see every machine that each click sets off.

“It's like a customer going in the kitchen and seeing the whole supply chain from the farm all the way to the plating.”
Me, Jun 2026

The two views need each other. The journey decides which machinery gets hit, in what order, and from what state. The machinery is then tested from the state the journey actually left behind, with nothing mocked. A green click on top of an unverified engine proves very little, and a verified engine reached from a fake state proves less.

The user

The god view

Develop server

Player pool and elimination flags

Could go wrong
Players from nations that are already out get offered to the manager.
Checked by
Shadow model: recompute who is out from the fixtures, then compare with the flags.
What happened
Confirmed defectGroup-stage exits were never flagged. The elimination hook only fired when a knockout match finished, so 759 players from 16 eliminated nations stayed pickable. A sweep now flags them on every cron tick, and the shadow agreed on 759 of 759.
The Fantopy World Cup runs as a manager's journey. Pick a step to see the machinery under it and what the run found there.

The kitchen's four laws

The kitchen picture became the rules the run asserts. Each one points at a place where ordinary testing looks the wrong way.

  1. 01

    A chain breaks at the hand-offs, not the stations.

    Most testing checks each station. The seams between subsystems are where state gets dropped, so that is where it looks.

  2. 02

    A busy station is not a working one.

    A status code or a log line only proves activity. gauntlet checks what came out the other side.

  3. 03

    A road safe 99 times is still broken.

    Rare and probabilistic failures only show up at volume, so journeys run many times with varied inputs.

  4. 04

    The diner's satisfaction is the only real metric.

    A perfect kitchen that serves a cold plate failed. Every trace ends at what the user actually got.

Surface first, then deep

Sweeping every station deeply would take forever and mostly confirm things that were fine. What I asked for was “either a surface area or a deep sweep”, and the skill ended up doing both, in that order. Every station the journey touches gets a quick real pass and a score for how much rests on it and what smells off. The ranked list decides where the depth goes.

1 · Surface pass

Every station the journey touches gets a quick, real pass.

  • player pool
  • auto build
  • draft save
  • round locks
  • lock display
  • transfers
  • squad sync
  • competition entry
  • scoring
2 · Risk-ranked hit list

Each station scored by what rests on it and what smells off.

  • player pool: every squad · eliminated nations still pickable → DEEP
  • draft save: every manager · 409 on save, only on develop → DEEP
  • lock display: every transfer decision · nothing shows as locked → DEEP
  • squad sync: one agent · a 400 loop in the logs → DEEP
  • scoring: every result · nothing to score on develop yet → skip
3 · Deep sweep

Only the stations that earned it. A varied, adversarial population, at volume, from the state the journey left behind, checked by an oracle.

The hit list from the develop run, in the skill's format. Scoring was skipped there because develop had no fantasy competition to score yet; the local run the night before had already covered it.

A road safe 99 times is still broken

“Making sure, like, 100 times he doesn't stumble upon, like, a risky road.”
Me, Jun 2026

Rare failures only show up at volume. The World Cup sweep drove 8 managers through 5 round multipliers, advancement bonuses, transfers and penalty shootouts, then re-scored 1,896 predictions from 63 users. The checkout example in the skill hunts the same thing in a different world: a double tap on a slow spinner that charges a card twice.

99/100

A smoke test walks the road once and sees a green dot. The double charge, the race for the last unit and the round that freezes a save live in the one run that goes differently.

Never trust the self-report

A busy station looks fine from outside. The status code is 200 and the log says done. So gauntlet works out the right answer on its own and compares, and every verdict says which check it used and how strong that check was. “Verified by invariants only” is a useful, honest answer. A quiet green tick is the one failure the skill treats as unforgivable.

  1. 1

    Re-derive from the breakdown

    Recompute the result from its own parts.

    In the World Cup run: Every fantasy score rebuilt from multiplier, bonus and player points. 40 of 40 manager-rounds.

  2. 2

    Conservation and invariants

    Things that must always hold: money in equals money out, counts conserved, every advancer won.

    In the World Cup run: Every team in the next round won its previous match. Proved on a fresh bracket.

  3. 3

    Shadow model

    A deliberately plain second implementation of the rule, diffed against the real one.

    In the World Cup run: Base player points recomputed from raw stats. 198 of 198 starter-rounds.

  4. 4

    Property assertions

    Must hold whatever the formula: idempotent, monotonic, the same on replay.

    In the World Cup run: Prediction points constant per class across 1,896 picks.

  5. No oracle available

    The run can still surface crashes, and it has to say so plainly: “this was a load test, not a proof.”

The four kinds of independent check, strongest first, with where each one was used on the World Cup engine.

Rule out your own harness first

The rig that drives a simulation is software too, and it breaks. Before anything gets called a bug, the run has to prove the problem is in the system and not in the rig. The scoring shadow on the World Cup engine is the clearest case I have. Its first result looked like a disaster.

  1. 816 of 893 rows mismatchedharness artifact

    A forward with a goal, an assist and 89 minutes scored zero. No engine does that. The shadow was reading a column the engine never writes to.

  2. 8 of 198 starter-rounds mismatchedharness artifact

    Small and mixed in direction, all in the round of 32. The shadow scored a shootout win as a draw. The documented rule gives the shootout winner the win.

  3. 198 of 198 matchedverified

    With both fixed, the base scoring was verified from raw stats by a second implementation.

Confirmed defect

The harness is ruled out and there is a case anyone can rerun.

  • A missing migration that crashed scoring
  • Group-stage exits never flagged
Harness artifact

The fault was in the rig. Labelled and set aside.

  • The wrong column
  • The shootout rule
  • Bracket slots pinned by an earlier playthrough
  • Two local test agents both named “Dev”
Unconfirmed

It could not be reproduced or the rig could not be ruled out. It stays open with the missing check named.

Never forced into either of the other two bins.

How the scoring shadow earned its independence, and the three bins every finding goes into.

The second correction is the one worth noticing. The shadow disagreed with the engine on a real rule, the rule was checked against the written spec, and the engine was right. A second implementation that never disagrees with the first isn't checking much.

Run one: Fantopy's World Cup knockout

The night of 30 June, locally. The app ran in its manual simulation mode with a snapshot taken first. A small driver pushed the real lifecycle forward round by round, from the round of 32 to the final: produce match stats, eliminate the losers, resolve the bracket, score. The engine's own numbers were never taken on trust.

The surface pass crashed at the scoring station on a missing database migration, a hand-off that normal use would have hit without anyone noticing. With it fixed, 40 of 40 manager-rounds reconciled and all 1,896 predictions re-scored cleanly. The diary caught what the numbers couldn't: a real user would just have seen scores quietly stop updating. The fix for that, a loud alert when the scorer fails, came from the diary.

The run was honest about its gap too. The match stats were synthetic, so the path that pulls real stats from the data provider was never exercised.

The following night, on the shared develop server. Everything worked locally, but something was wrong on develop, and I had noticed players from Qatar and Uzbekistan still in squads after both teams went out in the group stage. I asked for a gauntlet on the whole fantasy flow there. It sent six stations deep and came back with:

  • Group-stage exits had never been flagged. 759 players from 16 eliminated nations were still pickable.
  • A locked round in a predictions competition was freezing fantasy drafts. This was the 409 that kept blocking saves on develop.
  • The lock display was dead on the transfer screen, because knockout fixtures are stored as round 0.
  • A World Cup squad was being synced in the background onto a Premier League agent, in a loop of 400s.
  • The develop test suite was red from three stale tests. It was repaired to 1,298 of 1,298 passing.

One more turned out to be the harness: two local test agents both named “Dev”. The fixes shipped together as PR #1183, one of eleven pull requests that night. When production picked them up, the first cron tick flagged 792 eliminated players there and the count dropped to zero without anyone touching the database.

What I take from it

I spotted the symptom: players from teams that were already out. The gauntlet found the cause, the other breaks on the same path, and proof that each fix held.

The whole launch is in the Fantopy record.

Run two: Rifter's card scanner

Rifter's scanner identifies the exact printing of a trading card from a phone camera. On 11 August the gauntlet ran it through 34 real photos from the internet: foil, sleeves, glare, toploaders, perspective, cards clipped at the edge, distant cards and compressed images. The diary from that run has the moment I care about most:

“The most worrying moment is a foil Irelia whose footer is read as 25/221 instead of 225/221. The artwork contradiction vetoes that wrong collector candidate, and the next text pass recovers Irelia. I never receive a confidently wrong card.”
Run diary, agent-driven, 11 Aug 2026

Every gate passed, and kept passing when re-verified on 13 August: 27 of 34 accepted, zero false accepts, a 900 millisecond decision time at the 95th percentile. On 16 August, a refresh of the project map asked what those green gates actually covered. Every test photo carried a box measured by hand, and the card was cropped to it before recognition ran. The app does something cruder: it crops a fixed rectangle in the middle of the frame and hopes the card is inside. Run the same 34 photos on the full frame and the picture changes.

Card pre-located by hand (what the green run measured)Full frame (closer to the app)
Accepted
27/34
19/34
Visual match ranked first
15/34
5/34
False accepts
0/34
2/34

Decision p95, pre-located

900 ms

Decision p95, full frame

1,200 ms

The same 34 photos, measured 16 August 2026. The two false accepts are the finding that matters: without locating the card first, the scanner adds the wrong card with confidence.

Live telemetry from 163 real scans told the same story from the other side: about 28 seconds per card at the 95th percentile. That clock also includes a hand reaching for the next card, so it isn't recognition speed, but it made clear the 900 milliseconds described something much narrower than a phone in someone's hand.

Every number in the run was true for what it measured. The coverage line just claimed more than the photos had tested. Locating the card in the frame moved from a speed fix to a correctness fix, and this is what the honest-coverage check below is for: say which roads were walked, and which weren't.

The diary

Every run writes one actor's journey to a user.md, labelled as agent-driven or watched by a person. It catches the dead ends and silent failures that a reconciled number misses. The rule is to keep what was seen apart from what it probably means, and the agent is never allowed to invent a feeling for a user it didn't observe.

ObservedInferred
  • The spinner remained after the error.A buyer may tap Pay again.
  • The scorer crashed on a missing migration, and nothing surfaced the failure.A manager would just see scores that stop updating.

Safe, or it doesn't run

A gauntlet changes state on purpose, at volume. So it needs a sandbox or simulation mode and a snapshot before it starts, and it never touches production, real money or real users. If a system can't be isolated and reversed, the skill refuses, or builds the missing scaffolding only when I say so. Afterwards it restores only the state it owns, and keeps the diary, the findings and the small repro cases.

  1. 0

    Frame and isolate

    Sandbox or sim mode, a snapshot, and a note of what this run owns.

  2. 1

    Map the gauntlet

    What moves time forward, how actors are seeded, which truths get computed.

  3. 2

    Surface run

    Walk the whole journey once and rank every station by risk.

  4. 3

    Deep sweep

    Varied inputs at volume on the stations that earned it, checked by an oracle.

  5. 4

    Diary

    One actor's journey in user.md, observations kept apart from inferences.

  6. 5

    Classify and report

    Every flaw tied to a journey step, a subsystem and the check that caught it.

  7. 6

    Restore

    Reset only the isolated state it owns. Never a broad reset of the working tree.

  8. 7

    Preserve the protection

    Findings into the existing ledger, a small repro per defect, tests where they fit.

The loop. The two marked phases are the ones that make it safe to run.

Next to bedrock

bedrock walks the building as an inspector and keeps the findings ledger. gauntlet drives the building through its whole life. They meet in the same AUDIT.md.

  • devourMaps the codebase to understand it. gauntlet borrows its discovery to find the levers.
  • bedrockInspects the structure and keeps AUDIT.md. gauntlet writes its findings into that same ledger, under its IDs, and never pretends a bedrock audit happened.
  • gauntletDrives the whole lifecycle as user and god at once, then checks the truth it computes.
  • birdwatchThe live twin. Same spine and stations, but a person drives and the agent keeps its hands off. Used when how a step feels needs a real person.

What it stands on

These ideas sit under the moves. A run doesn't lecture from them; they are there for anyone who wants to know why.

Deterministic simulation testing (FoundationDB, Antithesis)
Drive the whole world forward under control and assert invariants as it goes.
Agent-based playtesting
Actors play the system through in order to break it.
Synthetic user-journey testing
The realistic spine that decides what gets hit and in what order.
The test-oracle problem (property-based and metamorphic testing)
How to check an answer without trusting the code that produced it.

When it stays quiet

  • The system can't be isolated or reversed. Only a live system with real money or real users. Never sweep what you can't undo.
  • No independent check is possible. It can still report crashes, but it says plainly that this was a load test and proves nothing.
  • The job belongs to a neighbour. Mapping a codebase goes to devour, a structural audit to bedrock, one assumption to probe, and a routine test suite to normal work.

How a run is graded

  1. 1It was isolated and reversed: snapshot taken, owned state restored, or the gap flagged.
  2. 2The journey was realistic and un-mocked. Each subsystem was entered from state the journey produced.
  3. 3Deep stations were checked by a real independent oracle, and the verdict names it and its strength.
  4. 4Every confirmed defect has the harness ruled out and a case anyone can rerun.
  5. 5Coverage is stated honestly: what was swept, what wasn't, and where the check was weakest.
  6. 6The diary keeps observations apart from inferred experience, and findings land in the existing ledger.

If no independent check was possible anywhere, the right output is “I stressed it but could not prove it.” That is the honest result, and it doesn't get a green tick.