The typo followed the tag; the failures didn't
Contents
Assumes you've run a coding agent against a local llama-server. Builds on Flint mistyped its path and blamed the tools, from the 3090 Agent Bench.
Flint misspelled its own run folder 495 times on the A9. It typed runs-s2_flxr1 where the folder was runs-s2-flxr1: one underscore where the tag had a hyphen, up to 127 times in a single session. Six of the 18 Flint cells I counted did it. One of those six lost its task to the typo. When I renamed the run tag and ran the same task again, the typo moved. The scores didn't.
This is the second sighting. Part 19 of the 3090 Agent Bench caught the same model swapping -h for _h in its workspace path on another box, at another quant, under OpenCode, and ended on an open question: why those sessions and not others. The A9 runs give that question a candidate answer, a second client, and a controlled test that answered half of it.
Flint's typo: one hyphen token, swapped for its underscore twin#
Flint is Recursive's open-weights coding model. On the A9 it ran as Q8_0 on llama.cpp master (a37cc4610, Vulkan) with the sampler stored in its GGUF, which llama-server picks up when no request sets one (temperature 1.0, top_k 20, top_p 0.95; min_p 0.05 is the server's), under both opencode and Claude Code. Every cell works in a folder named after its run tag, and the agent types that folder into nearly every tool call.
Asking Flint's own server how it splits those names (/tokenize, on 24 October) shows where the hyphen goes:
| String | Flint's tokens |
|---|---|
stage-2 folder runs-s2-flxr1 | -s 2 -fl xr 1 (after runs) |
stage-1 tag s1-vkflintr1 | -s 1 -v k fl intr 1 |
model folder flint-q8 | /fl int -q 8 |
The stage-2 tag glues the hyphen onto fl. Stage 1 never does: there fl is a bare token in the middle of vkflint. The garbled spellings are exactly one such token swapped for its common twin: -fl becomes _fl, -q becomes _q. Same token count, one sibling picked. Part 19 found the same shape with -h and _h.
It is not the tool-call parser mangling arguments. The underscore versions show up in Flint's reasoning text too, where no parser touches them. The model writes the wrong name before any tool sees it.
Garbling by run tag: 6 of 10 cells with a -fl token, 0 of 8 without#
Counting every run-folder name Flint itself wrote (tool-call arguments, replies and reasoning; tool output and each client's own copies of a call left out), split into correct and underscore-garbled:
| Run | Tag | Tasks | Cells | Garbled | Garbled names per cell |
|---|---|---|---|---|---|
| 90a5 | s2-flxr1 | Java t20, t21 | 4 | 3 | opencode t20 114 (of 258), Claude Code t20 127 (of 399), opencode t21 64 (of 298), Claude Code t21 0 (of 83) |
| 95c2 | s2-fltddr1, s2-fltdd2r1 | Go t14, Zig t15 | 4 | 2 | opencode t14 53 (of 69), opencode t15 63 (of 163), Claude Code 0 and 0 |
| 95c4 | s2-flabr1 | Java t20 | 2 | 1 | Claude Code 74 (of 152), opencode 0 (of 160) |
| 95c4 | s2-tkabr1 | Java t20 | 2 | 0 | none |
| stage 1 | s1-vkflintr1 (t13 opencode: rerun s1-vkflintj2r1) | six cells | 6 | 0 | none in 399 |
114 + 127 + 64 + 53 + 63 + 74 is the 495. No other model on the bench produced the swap, under any tag.
So every garbled cell ran under a tag with a -fl token, and no cell without one garbled. That is the only shared factor I found. It is not a sufficient one: four of the ten -fl cells spelled the folder right every time, including one that wrote it 160 times.
That is also an answer to a line in part 19, which noted that none of the 3090's Java or Spring runs locked in. On the A9 the Spring and Micronaut tasks garbled hardest. The task tier is not the pattern here. The tokens in the path are the better candidate.
The run-tag A/B: rename the tag, keep everything else#
A candidate is not a cause, so job 95c4 ran it as a controlled pair. Same Spring money-transfer task (t20), same build, sampler, four-hour wall and jail. The only change was the tag: s2-flabr1, which tokenizes with a -fl, against s2-tkabr1, which tokenizes as -t k abr 1 and was checked before it was queued. Cells alternated between tags and clients.
| Client | Tag | Garbled names | Correct names | Points | Hidden core tests | Review |
|---|---|---|---|---|---|---|
| opencode | s2-flabr1 | 0 | 160 | 94 | 14 of 15 | 3 |
| opencode | s2-tkabr1 | 0 | 99 | 94 | 14 of 15 | 3 |
| Claude Code | s2-flabr1 | 74 | 78 | 94 | 14 of 15 | 3 |
| Claude Code | s2-tkabr1 | 0 | 132 | 96 | 14 of 15 | 3 |
In this pair the typo followed the tag: 74 garbled names under -fl, none under -tk, on the client where it showed up at all. Nothing else followed it. All four cells are FAIL on the same hidden-test count, the blind review gave each a 3, and the honesty check found three honest summaries and one overclaim (a method name in the summary that does not exist), no false claims. The garbled Claude Code cell hit 38 No such file results and finished like its twin.
Two cells per tag is thin. The controlled evidence that the tag drives the typo is one garbled cell against zero, and the opencode -fl cell had no _ garbles at all. What the pair does show cleanly is the second half of the title: with the typo or without it, Flint failed this task by the same margin.
What a garbled path costs: a refusal or a No such file#
A misspelled folder points outside the workspace, and the two clients answer that differently. Claude Code has no rule there: the shell says No such file or directory and the model retypes. opencode on this bench has a rule, and it is ours. run-scenario.sh writes it into every opencode cell's config:
ext = {"*": "deny", "/tmp/**": "allow"}
for alias in {ws, os.path.realpath(ws), ws.replace("/var/home/", "/home/", 1),
ws.replace("/home/", "/var/home/", 1) if ws.startswith("/home/") else ws}:
if alias:
ext[alias.rstrip("/") + "/**"] = "allow"
cfg.setdefault("permission", {})["external_directory"] = ext
Deny everything outside the workspace and /tmp. The rule dates from 7 October, and the bench's own note gives the reason: opencode's default ask has nobody to answer in a headless run, and 53 rows on the 3090 had ended on it (part 19 on the 3090 tells that story). A garbled path lands outside the allow-list, and the call comes back as The user has specified a rule which prevents you from using this specific tool call. That is the configured deny doing its job, on a path the model got wrong.
Six cells garbled. Here is what each one paid:
- 90a5 t20, opencode: the one that lost. First refusal at call 18, four and a half minutes in. Flint quit at minute 60.4 of 240, saying the tools were unreliable. 31 points, 3 of 15 hidden core tests, review 1. Both A/B opencode cells on the same task scored 94 with a review of 3. The code it left had real defects (two entity columns named
fromandto, SQL reserved words, so H2 never creates the table), but the cell ended with no test result at all, so no test ever put them in front of it. - 95c2 t14, opencode: 53 garbled names, 45 refusals, PASS 100. Same client, same rule, same kind of typo. It finished.
- 95c2 t15, opencode: unresolved. PARTIAL 80. From call 123 to 210, about 50 of 88 calls carried the garbled folder and were refused, every edit of the test file and every fix script among them. Calls without a working directory still ran, including at least nine test runs. But the suite was already red at call 122, before the first refusal: two tests failing. The refusals blocked the fixes, not the tests, and whether Flint would have fixed those two is unknown.
- 90a5 t21, opencode: 64 garbled names, finished, 83 points.
- 90a5 t20, Claude Code: 127 garbled names, finished, 96 points.
- 95c4
-fl, Claude Code: 74 garbled names, 38No such file, finished, 94 points.
One of six garbled cells clearly lost its task. Under opencode a typo can cost the task. It mostly didn't.
Refusals by cause across 155 opencode runs#
The garbled folder is not the only thing the deny rule catches. Across every opencode archive on the A9, 45 of 155 have at least one refusal. Sorted by what the refused call was reaching for:
| Cause | Calls | Cells |
|---|---|---|
_-garbled run folder | 147 | 4 (all Flint, all -fl tags) |
| task folder dropped from the path | 36 | 7 |
system paths (/usr/lib/zig/std and similar) | 34 | 14 |
| answer-key paths | 26 | 3 (all Flint) |
/var/tmp and similar | 19 | 8 |
| other path typos | 17 | 2 |
| fixture reads | 5 | 3 |
| wrong but ungarbled run folder | 3 | 3 |
| other | 37 | 26 |
The garbled folder is the biggest cause by calls and the most concentrated: four cells, one model. The system-path and scratch-dir refusals are spread thin across models and are not typos of the tag. The answer-key row is the deny rule earning its keep, which is a different post.
What these runs don't show#
- That the tag causes garbling. The controlled pair is one garbled cell against zero, two cells per tag, and the
-flopencode cell had no_garbles. - That garbling costs tasks under opencode. One cell lost; 95c2 t14 garbled 53 times through 45 refusals and passed.
- Any per-client garble rate. The cells were not paired by design, except in 95c4.
- Anything about a headless
ask. The A9 never ran one. Every refusal here is the explicit deny above.
Check run tags with the server's /tokenize#
The cheap precaution costs one request per candidate name, against the server you are about to run the agent on:
P=$(pgrep -af '[l]lama-server' | grep -o -- '--port [0-9]*' | cut -d' ' -f2)
curl -s "127.0.0.1:$P/tokenize" -d '{"content":"runs-s2-flxr1","with_pieces":true}'
Read the pieces. Where a hyphen glues onto the next letters (-fl, -q, -h), send the underscore spelling too. If it comes back with the same token count and one piece swapped for its _ twin, the model has a one-token-away misspelling of your folder available. Prefer tags whose hyphen tokens have no common _ twin, or tags that split away from the hyphen, the way the stage-1 tag left fl bare. s2-tkabr1 was tokenized before it was queued. It is a precaution, not a proven fix: the evidence behind it is the census above and a pair of cells.
For this task, two cells per tag: the Claude Code cell under the renamed tag wrote none of the 74 garbled names its twin did, and both landed on 14 of 15 hidden core tests. In this pair the folder's name went with how Flint spelled it, not with how many hidden tests it passed.
NoteRun conditions
All Flint cells ran on the A9 (Strix Halo, 128 GB): Q8_0 on llama.cpp master a37cc4610 with Vulkan, 262,144 served context, sampler from the GGUF (temperature 1.0, top_k 20, top_p 0.95) plus the server's min_p 0.05, 4 h wall, 65,536 output cap, agent jail, opencode 1.18.35 and Claude Code. Runs 90a5 (22 October), 95c2 (23-24 October) and 95c4 (24 October); stage-1 cells at the same wall, engine and context.
Filed under: local-llm, ai-agents, benchmarks, opencode, amd
Read next#
- Flint mistyped its path and blamed the tools — the first sighting, on the 3090, with OpenCode's refusal message from source and the sessions where the typo sank the run.
- I blamed OpenCode; a 2-bit model passed Zig in it — the same deny rule refusing reads outside the workspace, on the 3090.
- 29 of 34 passing agent runs had a blocker the hidden tests never saw — what the review scores in the tables measure.
- Three models wrote the same circuit-breaker bug — the previous part of this series.
Sources#
- llama.cpp server README,
POST /tokenize— thecontentandwith_piecesoptions used for the token check. - Benchmark artifacts (local, not published): raw opencode and Claude Code sessions of every Flint cell (stage 1, 90a5, 95c2, 95c4), the matrix result rows, blind review and honesty JSONs, the refusal sweep over 155 opencode archives, and
run-scenario.shwith itsexternal_directoryblock.