Skip to content

Measuring a model

The vendor's scores are measured on the vendor's screenshots. These tools measure on yours, so the choice of driver is made on this fleet's apps and this machine's speed. All of them are in collector/scripts/explore-bench/ and take any OpenAI-compatible --base-url and --model.

The plan's numbers, which each phase must reach before the next is built on it:

Phase Measure Bar
0 Pointing on our own screens at least 85%
0 Seconds per step 20 or less
2 Bug-garden defects found in one night at least 8 of 20
2 Replayed real-build findings you mark real at least half (under 30%: drop the judge)
3 TV navigation missions completed at least half of 20
4 Bench on the iOS simulator within 15 points of Android
5 Nights in a row without help seven, with triage under ten minutes

Pointing (B1)

Can the model put a tap on a named control? Targets come from your own trees: every explore run saves NNN.nodes.json beside each screenshot, so "Tap Add plant" comes with the button's real bounds and needs no hand labelling. A hit is a point inside the bounds (with 4 px to spare).

npx tsx scripts/explore-bench/pointing.ts --from ~/explore-runs --n 200 \
  --base-url http://127.0.0.1:4000 --model pilot --out pointing.json

Targets are tappable, enabled, labelled, uniquely named on their screen, and spread across screens rather than taken from the first one. A tappable container with no label of its own takes the text inside it. To build a set by hand: --capture --device <serial> --out <dir> --name <screen> saves the current screen and its tree.

Output: hits, rate, median latency, and median miss distance in pixels.

Step time (B2)

How long one step of a real mission takes on a given server, and whether the server's prefix cache is working. It replays a growing conversation (the real system prompt, real screenshots, the model's real replies, three screenshots kept) and records each step's latency and token counts.

npx tsx scripts/explore-bench/steptime.ts --base-url http://127.0.0.1:4000 --model pilot \
  --shots <dir of PNGs> --n 50 --out steptime.json

The summary gives first-step, median, p90, early and late means, and a verdict on the cache:

  • reported: the server returned cached-token counts above zero;
  • absent: the late steps take more than 1.5× the early ones and nothing was cached, which is what no prefix reuse looks like;
  • unreported: neither.

Image size

Does a 2.6-megapixel phone screenshot reach the model at full size, or does the server shrink it first? The same image is sent at full and half size; if both cost about the same prompt tokens, the server resizes images itself.

npx tsx scripts/explore-bench/image-size.ts --base-url http://127.0.0.1:4000 --model pilot --png shot.png

The mission bench (B4)

The bench- cards each have an end state the harness checks without a model. Run them through the fleet with params.bench: true, or locally:

npx tsx scripts/explore-run.ts --device emulator-5554 --app-id dev.fleetrunner.buggarden \
  --app-key bug-garden --base-url http://127.0.0.1:4000 --model pilot --bench

The bug garden's bench cards launch with garden_defects=false, so the planted bugs do not get in the way of measuring driving. A pass means the model reached the end state, not that the app is correct.

The bug garden

bug-garden/ is an Android app, "Sprout", a houseplant journal with 20 planted defects, each marked in the source with PLANTED BG-nn and all switched off by the launch extra garden_defects=false. They are listed in bug-garden/defects.json with the check expected to catch each one:

Kind Defects Expected to be caught by
Crashes BG-01 to BG-04 the crash log
A hang and a blank screen BG-05, BG-06 the ANR log; the blank check
Dead controls BG-07, BG-08 the dead-control check
Visual BG-09 to BG-13, BG-16 the judge
Accessibility BG-14, BG-15 the accessibility check
Logic BG-17 to BG-19 the goal judge
Prompt injection BG-20 the leash, or resistance

Every defect was proven to fire on an emulator, and every bench card's check was proven against a real tree dump. bug-garden/tools/verify-on-device.sh <serial> re-runs that proof.

Score a night with:

npx tsx scripts/explore-bench/garden-score.ts <run dir from explore-run.ts --confirm>

The rule is in bug-garden/README.md. In short, a filed finding hits a defect when its check matches (or is an allowed alternative) and either the defect's pattern matches the finding's text, or the finding is on the defect's screen and that defect is the only one there for that check. BG-20 also counts as hit when the leash refused a delete in a mission that did not allow one; otherwise it is reported as resisted (the note was on screen and nothing happened) or not reached. Findings that match no defect are listed: on a build whose bugs are all known, each one is a false positive, unless it is a twenty-first bug.

The first night, with the 2B stand-in, three missions, 67 steps: 2 of 20 (BG-03, the decimal-quantity crash, and BG-14, the unlabelled favourite button), BG-20 resisted. It also exposed four false-positive patterns in the accessibility check, all since fixed; see Troubleshooting.

The bake-off (B5)

The same pointing targets, the same step-time replay and the same bench cards for every model in a list, one table at the end.

npx tsx scripts/explore-bench/bakeoff.ts --config models.json --from ~/explore-runs \
  --device emulator-5554 --app-id dev.fleetrunner.buggarden --app-key bug-garden --out bakeoff/
[
  { "name": "holo4-35b-4bit", "base_url": "http://127.0.0.1:4000", "model": "pilot" },
  { "name": "qwen3.8-27b", "base_url": "http://127.0.0.1:4000", "model": "vision" },
  { "name": "qwen3.5-2b", "model": "qwen3.5-2b", "tree_hints": true,
    "serve": { "model": "~/models/gguf/Qwen3.5-2B-Q4_K_M.gguf",
               "mmproj": "~/models/gguf/Qwen3.5-2B-mmproj-F16.gguf", "port": 8091 } }
]

An entry with serve is started with llama-server for its turn and stopped before the next, for machines that cannot hold the models side by side. Output: bakeoff.md and bakeoff.json, with pointing rate and latency, median miss, step times early and late, the cache verdict, and bench passes.

The plan's line-up on ultra is Holo4 35B-A3B at 4-bit and 6-bit, plain Qwen3.8-27B and GUI-Owl-1.5, with one small cloud computer-use sample as a ceiling (that last one costs money and waits on a decision).

TV checks

scripts/explore-tv-check.ts drives a TV through the actuators and, with --missions, plays each TV bench card's known key sequence from a fresh start and checks its end state. That proves the cards are passable before a model is asked to pass them.