Released 2026-08-18. Follows 2.8.2.
2.8.2 fixed the start wizard's re-tune loop, but field reports of slow,
stuttering chats kept coming — and they were right. Two more regressions
shipped in 2.8.0 and survived until now because both live on a code path
release QA never exercised: the uncapped request, which is what every
desktop, web, and agent chat actually sends. Benchmarks and gates always
cap max_tokens, and capped requests take a different path.
2.8.0 added a wire safeguard for the runaway-loop trimmer: while an uncapped request is armed, the stream held a fixed ~448-token tail so a trim could never chase bytes already delivered. At chat speeds that silenced the wire from roughly token 320 to token 768 on every uncapped response: reasoning froze for 6–11 seconds while the speed readout collapsed, then the backlog arrived in one burst — and the last ~448 tokens of every response arrived the same way. The engine was decoding normally the whole time, which is why every server-side number looked healthy.
The holdback is now engaged only while the output actually shows a
forming loop. Healthy responses stream live, byte for byte, exactly like
2.7.1. A genuine runaway loop still gets trimmed with at most a short
prefix of the repeated run ever reaching the wire — still strictly
better than 2.7.1, which streamed all of it.
MTPLX_REPETITION_STREAM_HOLDBACK=candidate|strict|off selects the new
default, the 2.8.0–2.8.2 behavior, or the pre-2.8 wire.
2.8.0's turbo profile walked a background warm ladder up to 32,768-token prefills after every boot so deep-context benchmark rows would start warm. On real machines that meant 30–60+ seconds of full-throttle GPU after every launch (2.7.1: about 4.5 s), warm rungs re-firing between chat turns, and a chat sent mid-rung waiting several seconds for its first token.
The product ladder is back to the two rungs interactive chat actually
reaches early. Benchmark harnesses that want deeper buckets pre-warmed
opt in with MTPLX_WARMUP_LADDER. Background warm steps now also wait
for 90 seconds of request quiet (MTPLX_WARMUP_IDLE_GRACE_S) before
touching the model, so warming never competes with a conversation.
Founder testing at temperature 1.0 on a long, multi-turn conversation caught what a fresh chat never showed: the desktop app itself rendered in O(entire transcript) per frame. The window's SwiftUI hosting view re-measured every realized message on every constraint invalidation (a third of the main thread at idle, up to 62×/s while streaming — the guard written to prevent exactly this had silently never applied), the markdown pipeline re-counted every character of every block per frame, and per-delta paths copied the whole answer to ask if it was empty. When those walks stalled a frame, the typewriter's catch-up path pasted the whole backlog at once — the literal freeze-then-vomit. The engine was streaming cleanly the entire time; replaying the wire gate over the founder's exact 37k tokens showed zero holdback engagements.
2.8.3's app build fixes all of it: the transcript min-size walk is dead, fence classification and syntax-lex state are computed once per block instead of once per frame, scroll pacing no longer invalidates the view tree, catch-up is rate-limited to read as fast typing (never a paste), the 10 Hz metrics chip no longer re-evaluates every bubble or burns a core parsing its stream byte-by-byte, and markdown tables no longer draw rows on top of each other (cells measured single-line but drew wrapped).
The release pillar gate now includes an uncapped streamed chat measured for delivered cadence — any gap over 2 seconds fails the release. The capped-only blind spot that let both regressions through is closed.
App-side, the bar moved too: streaming QA runs on a heavy multi-turn conversation (fresh chats hide every O(transcript) render term), and "smooth" is judged at three layers — engine wire, app ingest telemetry, and the actual on-screen text — because the first two were green while the third was frozen.
| 2.8.2 | 2.8.3 | |
|---|---|---|
| Worst mid-stream gap | 6.4–11.5 s, every response | under 1.3 s |
| Time to first token (warm daemon) | up to 6.6 s under warm-rung contention | 0.26 s |
| Boot warm burn | 30–60+ s max GPU | ~4.5 s |
| End-of-response burst | ~448 tokens at once | none |
The first 2.8.3 candidate fixed the measured pathologies and then failed a human in three ways the instruments weren't pointed at. This round is those three, fixed and re-measured:
The founder retested with his own hands and the stutter was still there — while every hands-off verification pass stayed clean. Both observations were correct, and the difference between them was the bug:
Follow-up turns in a conversation decode slower than the first — the founder measured 43 → 40 → 33 tok/s across three turns at under 10k context — and this release deliberately does not paper over it. Two real mechanisms, both now precisely attributed in every request record: each verify cycle costs more as context grows (~60 ms/cycle at 1k context → ~77 ms at 8k, honest attention physics plus a context-linear draft cost), and speculative acceptance tracks content entropy — at temperature 1.0 it collapses in free-form prose (down to ~0.4 by depth 1 in wrap-up passages) so each cycle commits fewer tokens. Neither is a regression: the same curves are measurable in every 2.8.x build. The per-cycle cost work is the standing decay track, continuing in 2.8.4; acceptance-vs-entropy is the 3.8 MTP head calibration campaign. What 2.8.3 ships is the instrumentation that makes both visible per-request instead of anecdotal.