IMR R&D · engineering note · the coin-flip bug · July 2026

The coin-flip that wasn’t

For weeks, SupportFlow — our hardest benchmark, where a model fixes a real bug in a real PHP repo and must make the project’s own tests pass — behaved like a coin toss. A model would solve a task one run and fail it the next. It looked like irreducible randomness. It was a one-line bug in our own harness. Fixing it lifted essentially the entire board.

~44%
apparent SupportFlow pass rate — and it flip-flopped run to run
1/6
coin-flip benches solved even with best-of-3 retries
6/6
the same benches, one deterministic pass, after the fix
0→25
top model’s solved count, before → after, out of 32

The symptom

A single SupportFlow run wasn’t a score — it was a noisy sample. Re-running the same model on the same bench flipped pass↔fail with no code change. Averaged across the suite it sat around 44%, but the average hid the real problem: you couldn’t hand a model a task and trust the result. That’s useless for a benchmark, and worse for the actual goal — giving these models real bugs to fix.

The investigation — retries didn’t help

If a bench is a genuine 50/50 coin, best-of-3 should solve it ~88% of the time. We ran exactly that experiment on six coin-flip benches. Best-of-3 solved one of six. That result is only possible if the failures are correlated — the model makes the same mistake every attempt. So the fix was never “retry more.” The transcripts showed why: four of six failures ended with “abandoned: explored but never attempted an edit” — the model kept trying to write its fix, and every write bounced.

The root cause — a path the harness threw away

The model addressed files the way the codebase itself does: /app/app/Services/Mailer.php — a Laravel/Docker absolute path where /app is the container root. Our sandbox joined that against the repo:

# _safe_path(repo, "/app/app/Services/Mailer.php")
full = os.path.realpath(os.path.join(repo, "/app/app/Services/Mailer.php"))
# Python DISCARDS `repo` when the 2nd arg is absolute →
# resolves to a real /app/... OUTSIDE the repo → rejected → edit bounces

Every /app/-style edit was silently refused as “outside the repo,” so the agent explored, got told “you haven’t edited anything,” tried the same path again, and eventually gave up. The one bench that did solve? On its lucky attempt the model happened to emit a relative path — which the harness accepted. The “coin-flip” was a path-format lottery the harness itself created.

The randomness wasn’t in the model. It was in whether the model guessed a path format our sandbox would accept — and we were penalising it for a reasonable one.

The fix — harness v1.7.2

path recovery
Meet the model halfway
An absolute/container path that escapes the repo is recovered to the real in-repo file (strip /app, tail-match). Relative paths and /etc/passwd / ../ traversal are unchanged — security intact.
escape hatch
Don’t trap the model
A repeat write to an existing file is now accepted as a full overwrite instead of an endless “use edit” refusal loop.
generation guard
Bound the runaways
Per-call output capped (28k→12k tokens) so a stuck temp-0 generation can’t wedge a run. A size cap, not a timeout — no task is made easier.

Crucially: no timeouts loosened, no step caps raised, no hard benches scoped out, no model-specific hacks. Every change removes harness friction and lifts every model equally. v1.7.2 is its own frozen engine version — the old v1.6.1 baseline stays untouched for comparability.

The payoff — the whole board moved

Same models, same 32 benches, previous harness (v1.7.1-both) versus the fixed one (v1.7.2-both). Grey = before, blue = after.

0 16 32 benches qwen3.6-35b-bf16250 qwen3.6-27b25 qwen3.6-35b-a3b25was 7 devstral-small-218 qwen3-coder-30b12 qwen3.5-4b12 gemma-4-26b-a4b7 deepseek-r1-32b4was 2 granite-4.1-30b4 mistral-small-24b4 gpt-oss-20b3 granite-4.1-8b1 qwen3.5-2b0 — capability floor qwen3.5-0.8b0 — capability floor before (v1.7.1-both) after (v1.7.2-both, path fix)

Models that scored zero on SupportFlow weren’t incapable — they were failing on the path bug. Once it was gone, qwen3.6 (all three variants) cleared ~78%, devstral 56%, and a spread opened up that actually reflects capability. The two smallest models still score zero: that’s a real floor, not friction.

This is the whole thesis in one chart: for weak local models, the ceiling was harness debt, not the model. Pay down the friction and they climb.

How we found it, and why it matters

The lesson wasn’t just the bug — it was the method. A single agentic run is a noisy sample, so we stopped trusting single runs, ran a deliberate best-of-N experiment to quantify the retry lever, read the actual transcripts instead of guessing, and let the failure mode tell us what to build. The fix that followed was small, surgical, and it moved everything.

Method notes. SupportFlow benches are real merged PRs on a live PHP codebase; a bench passes only when the model drives the repo’s own test suite to green by its own actions. The before column is the prior v1.7.1-both harness (itself a weak SF harness), so part of that gap is escaping the old bundle as well — but the v1.7.2-both numbers are the current standings either way. Full board: touchstone.imrrd.com/leaderboard.

IMR R&D · Touchstone engineering note · harness v1.7.2 · July 2026