Machine-generated, human-curated. Everything labeled “Xiaomi measured” comes from their technical blog (27 Sep) and model cards — training logs, replay tables and all; the dollar figures are their estimates. What’s labeled “I checked” was verified today against the repos via the HuggingFace API. Download counters are batch-updated aggregates: two reads a few minutes apart today, neither moved.
A small boxy robot at an office desk stamping identical paper forms beside an overflowing document tray, a red-needled gauge behind it and a suited human pointing it on.
Busy, making no progress — the failure mode nobody’s eval caught.

The failure that doesn’t fail

Xiaomi’s description of the bug is almost annoyingly accurate: “Nothing fails outright, so it tends to go unnoticed.” The model issues the same or a near-identical tool call, over and over, while nothing about the environment has changed. Tools keep returning. The context window keeps filling. The user just waits. When we covered the community’s attempts to serve the Flash-RL checkpoint on pairs of DGX Sparks (24 Sep), the sharpest field measurement was a coding-agent turn containing 148 identical grep calls out of 446 — a 44-minute turn that had learned nothing by call 447.

At the time the story was a serving-configuration trap: coding agents typically send no sampling parameters, and near-greedy defaults push this model into the repeat. That’s real, and it’s printed below — but Xiaomi’s blog says the deeper thing is trained in. And the way it got trained in is the lesson of the whole post.

The threshold was the blind spot

Xiaomi replayed fixed examples that had exhibited repetition against checkpoints taken through the RL run — the same examples at every step, so the behavior could be tracked as training progressed. What they found: the flooding was already there at RL step 0 (11.1% of replayed examples contained a turn with more than ten tool calls) and it grew under RL to 24.6% by step 20. In the MiMo Code harness it went from 30.6% to 41.7%. Training was making this worse.

The pipeline did have a guard. If a rollout exceeded 32 tool calls in one turn, the rollout was cut and the reward set to zero. But the training log showed the model almost never hit that threshold early on — and from about step 15, hits started climbing. The reading: a ceiling of 32 priced nothing below 32. Turns with eight, twelve, twenty calls paid full reward. And if calling things repeatedly happened to correlate with finishing tasks, RL was doing exactly what RL does — amplifying whatever the reward doesn’t forbid, until it crossed the only line the guard drew.

The token-level look at one 59-call trajectory makes it concrete. At the 12th tool_call position, the pre-fix model assigned 94.56% to continuing with another tool call. The repaired model, same position, assigned 92.17% to the end-of-turn token. Cumulative stopping probability crossed 99.87% within eight calls after the fix; before, it was still only around half at 59 calls. The model had never learned when to stop — because a reward for correctness never priced the route taken to the answer.

Two fixes, priced

The direct fix was lowering the threshold from 32 to 8. It worked — flooding dropped without visible reward loss. Two problems. First, the effect only appeared after roughly 20 training steps, so applying it at full scale meant restarting 20 steps of the MixRL run: Xiaomi estimated that at $2.31 million. Second, it generalized poorly: replayed repetition cases went from 13.45% down to 3.83% at the best checkpoint — better, not gone; and when the visible history already contained a flooded turn, replay repetition stayed near 17%. Loops beget loops.

The fix they shipped is a distillation trick. Train a small, repetition-specialized RL teacher on the replayed cases — reward 0 if a repetition occurs, 1 only for no repetition and no erroneous tool use, a KL term pinning it to the original policy. It converged in 12 steps on ~7,000 examples and drove replay repetition to zero on training and held-out sets. Then a final MOPD pass — multi-teacher on-policy distillation, rolled back five steps, with this teacher’s prefixes supervising the student’s own generations — folded the stopping behavior back into the main model. Total training cost of that path: about $90,000 — roughly 4% of the retrain estimate.

Worth noticing: they published the per-harness repetition table for the shipped RL checkpoints. Response-level rates ran above 0.05% for every harness; the Flash-RL row: OpenCode 1.02%, Claude Code 0.27%, Codex 0.23%, MiMo Desktop and OpenClaw and a DeepSeek harness 0.17–0.19%, Hermes 0.16%, Zcode 0.07%. The Pro-RL row is lower everywhere (0.05–0.54%). The harness is a variable, and a lab printed a table of its model’s behavior inside competitors’ harnesses. Their defined metric — exact within-turn duplicates after JSON canonicalization — is stated as a lower bound on what’s observable; cross-turn loops and near-duplicates don’t count toward it. The post-MOPD before/after rates are published only as per-cell figures in the blog’s images, with the text saying repetition “dropped substantially” and the benchmark suite held steady. That half is still a vendor claim; nobody outside has measured it yet.

What actually landed, and what it’s worth running

The two new checkpoints from Sunday — MiMo-V2.6-Flash-MOPD (637 downloads two days in) and MiMo-V2.6-Pro-MOPD (307) — carry the fix. One line first: API users already got it. The corrected weights have been live on Xiaomi’s platform since 25 Sep under the same model names — the open weights trailed the API by two days.

I diffed the Flash-MOPD config.json against the Flash-RL one: identical across all 66 keys — architecture, 256-of-8 expert config, the 1M window, encoder sections, everything. This is a weights-only successor; every serving recipe written for Flash-RL addresses it unchanged. The 309B/15B-activated card also now ships a `dflash/` drafter directory and a 5-layer MTP head (7 draft tokens per pass) as standard parts of the repo.

And llama.cpp didn’t even blink: ggml-org’s MiMo-V2.6-Flash-MOPD-GGUF went up yesterday — same shelf as the RL builds, which their auto-converter confirms: MXFP4 with experts kept native (~156 GiB), the Q2_K hybrid at ~117.6 GiB with down-projections left MXFP4, an MTP sidecar family plus a DFlash drafter (BF16/Q8_0), and the Q8_0 mmproj carrying the vision and audio encoders — audio-output decoder excluded, exactly as before. Two honesty notes from their own README: the Q2_K hybrid still has no imatrix calibration, and the GGUF repo sits at 0 downloads so far — its RL sibling needed a few days to move.

Which means every fit conclusion from the 24 Sep post holds verbatim: the 2×Spark TP2 shape is the proven way to run this class, a single 128 GB Spark still cannot hold either quant, and a 512 GB Mac has room for the Q2_K (weights + mmproj + both drafters ≈ 124 GiB, leaving roughly 390 GiB for KV at a full window) with no measured Mac recipe existing yet — watch, don’t set up. If you’re serving Flash-RL on vLLM or SGLang today, MOPD is the drop-in successor and the config diff proves the drop-in. The downside of trying early, as far as I can tell, is a download. For what it’s worth, the post-fix repetition numbers are not yet third-party-measured, so nobody should be re-litigating their model choice on them yet either.

One thing the fix did not touch, and this is checked, not believed: the generation_config.json is byte-identical between the two checkpoints — and it still says do_sample: false and max_new_tokens: 2048. The sampling-defaults trap this blog printed on 24 Sep is sitting in the fixed repo, unchanged, on purpose or not. The official recipe now spells it out in prose — recommended sampling: temperature 1.0, top_p 0.95, vLLM with --generation-config vllm — but nothing in the weights forces it. Update the weights; keep your own sampling flags. The model got better at knowing when to stop; the config file would still encourage it not to sample at all.

The other layers fixed their half this week, too

The odd thing about tool-call repetition is that the honest fix is split across three layers, and all three shipped within four days. The model layer: above. The serving layer: mlx-serve v26.9.6 (26 Sep) shipped what it calls “Qwen3.8 agents stop going in circles” — earlier turns’ thinking was being replayed into the prompt, inflating long agent sessions to roughly double the context they need; and a separate fix says images from earlier turns or returned by tools never reached the model at all — “an agent that read three pages saw only the last one.” If a Mac agent of yours has been looping or going blind on multi-page reads, one of these two is likely why. The harness layer: oh-my-pi v18.4.3 (out today) stopped re-delivering identical tool-call context when several calls in one batch return the same text — a model loop, after all, is only visible when the harness hands it back.


The reusable lesson is the pricing table, not the model: a reward that prices the outcome leaves the trajectory free, and RL will amplify anything the threshold fails to catch. Xiaomi’s standard evals never saw the loops; agent harnesses did. If your telemetry is a benchmark suite, this is the cautionary tale — and a good example of a lab publishing the training curves of its own failure, with prices, three days before you can download the fix.