← All Essays

Why I'm Returning My Strix Halo

The new Qwen benchmarks sit next to Opus. I ran six local models through a real software factory, and the best earned a B+. The machine is going back.

The new Qwen models have benchmarks putting them next to Opus 4.6 and 4.8. To see whether those results held up inside a software factory, I bought a 128 GB Strix Halo. The best local worker got a B+, so I’m sending the machine back.

The Benchmarks Are Not the Job

Qwen 3.8 Flash Next and the rest of that wave landed with a lot of noise: benchmarks near Opus, context windows that look capable of swallowing a repository, and people posting tokens-per-second screenshots as if speed settled the argument. I wanted a local worker I could leave on overnight, not a demo that writes a function. It needed to operate inside the factory we already run by inspecting a real codebase, staying within a bounded change, recovering from failed validation, and earning approval from a model that didn’t write the code.

That last part is the whole point. We approach agentic engineering as a software factory, with deterministic controls and gates around the AI writing the code, followed by review from a human and a stronger cloud model. The local model does not get to grade itself. If it can’t work inside that loop, its leaderboard score is trivia.

Why This Box Instead of Dual 3090s

People asked why I didn’t just buy two 3090s. They’re older cards with a cheaper initial cost and plenty of combined VRAM, so I considered it. The problem is everything that comes with the hardware.

A heavy GPU rig will win a synthetic decode race if you feed it enough power and tolerate the noise. It also turns inference into a second infrastructure project, and the monthly electricity cost can start resembling a Claude or Codex Max subscription. You still have to manage the heat, the drivers, and a machine you feel guilty leaving on. That last part matters for agentic work, which consists not of one long completion but of hundreds of short turns, repeated validations, and the occasional cold prefill that can take an hour when the context window is full.

The Strix Halo offered the opposite: one quiet, low-power box with 128 GB of unified memory and enough headroom to load 262K-context quants without compressing them into something unusable. I wasn’t trying to beat a cloud model on peak tokens per second. I wanted to know whether a machine with a reasonable electric bill could replace enough of my Max subscription usage to justify owning it.

Sell a Gift Card From the POS

The task was a real feature, not a coding puzzle: add gift-card purchasing on behalf of a customer to an existing point of sale. The implementation needed typed sale records, exactly-once issuance, payment recovery, retryable email delivery, reporting, a shared UI, and browser-based proof at the end. The plan divided that work into eighteen slices.

I only ran the first slice. That sounds like cheating until you see what it required: two Prisma models and an additive migration for the sale projection and delivery attempts, along with decisions about UUID versus text, one-to-one cardinality, nullable snapshots, foreign-key actions, check constraints, and index order. The schema and SQL have to agree, and a model can easily produce something that reads like a complete patch but is impossible to migrate.

That’s a useful first test. If a local worker can’t get the persistence boundary right under a controller that reruns the real validations, it is not going to grind through the other seventeen slices while you sleep.

The Factory, Briefly

The factory is a custom harness we built at Zipper. Think of it as a processor that picks up a Jira item, reads the spec, writes the code, runs the build and tests, and keeps going until the change is complete or the run stops safely.

A cloud model plans each slice, watches for stalls, and reviews what landed. The local model writes the code, runs QA, and is supposed to prove the result in the browser. Neither model gets to declare victory; the harness controls the ticket, the allowed files, the authoritative build and test commands, and the criteria for completing the slice.

Each slice is one bounded change. If the model wanders the repo too long without editing, a watchdog tells it to write something. After it commits, the harness reruns the checks and a different cloud model reads the diff.

On this hardware, a 256K context window is a safety net rather than a working style. Fill it and even a fast model crawls. Day to day, you want a small slice and a fresh context, so that is what I tested: whether these quants could work effectively inside that loop.

What Each Model Did

I tested six candidates, each on its own branch and served through LM Studio on the Strix Halo, with a cloud planner and reviewer on every run. The timings are directional because the planner wrote a slightly different work order for each model, but the pass-or-fail results are definitive.

Local workerDecodeWall timeResultGrade
Qwen3.8 Flash Next21.9 t/s22:43Schema + migration acceptedB+
Qwen3-Coder-Next 80B46.1 t/s20:22Schema + migration acceptedB
Qwen3.8 27B19.8 t/s15:57Schema only; no migrationC
MiMo-V2-Flash 309B16.1 t/s22:16Never edited a fileF
Devstral Small 2 24B13.3 t/s29:34Wrong schema, claimed a commit it didn’t makeF
Devstral 2 123B2.8 t/s56:17Broke unrelated models, then made it worseF

Qwen3.8 Flash Next

This is the model people are excited about, so I was careful about how I loaded it: three-part UD-Q3_K_XL at 262K, full Vulkan offload, flash attention, Q8 K/V, batch 2048, and ubatch 2048, using about 85 GiB of memory. I tried to attach a separate MTP file for speculative decoding, but LM Studio refused it because this GGUF has no supported MTP head bundled in. The numbers below are therefore from plain decoding.

Widening ubatch from 256 to 2048 barely changed generation speed (21.6 to 21.9 t/s), but it shortened the tool call from 9.4 seconds to 8.6. Cold 200K recall took 19 minutes 49 seconds, which seems slow until you compare it with the dense 27B model on the same box at almost 56 minutes. I ran the agent at temperature 0.3 with thinking disabled and a maximum output of 8,192 tokens.

The first factory attempt died before producing any code, and my harness rather than the model was at fault. The cloud planner wrote a validation command longer than the schema’s 1,000-character limit, which the planner prompt did not mention. After I fixed that, Flash wrote both allowed files, missed an inverse relation, read the resulting Prisma error, added the relation, and committed. A later replay hit a Prisma schema-engine crash that I could reproduce only once in twenty local runs. The harness now retries that specific crash while continuing to stop on genuine validation failures.

A reviewer that didn’t write the code accepted the schema and migration in 22:43. It was the best complete patch of the six, with the Prisma schema and SQL in agreement, which earned it the B+.

Qwen3-Coder-Next 80B

This model needed the least babysitting on the serving side. I used UD-Q4_K_XL at 262K with Vulkan, flash attention, F16 K/V, batch 2048, and ubatch 512. Its sparse architecture has 80 billion total parameters but only 3 billion active, so it runs well on this hardware. It delivered 46.1 t/s end to end and completed the tool call in 3.1 seconds without speculative decoding. Cold 200K recall took 14:06 and was semantically correct, missing the exact needle only by a period.

It made real edits on the first pass but introduced three bugs: UUID where the contract required TEXT, the wrong inverse cardinality on GiftCard, and a delivery index created before its table. The narrow per-command checks passed, but the watchdog and cloud reviewer caught all three problems. I sent the feedback back to the model, it fixed the bugs, and the reviewer accepted the result in 20:22.

It was the fastest worker and used the feedback loop exactly as a factory worker should. A human would still have to resolve two details: the SQL puts DEFAULT CURRENT_TIMESTAMP on updated while Prisma only has @updatedAt, and its slice uses explicit VARCHAR widths where Flash uses TEXT. That slightly messier contract is why it earned a B instead of a B+.

Qwen3.8 27B

Most of the tuning time went into this model, using Strix-specific IQ4_XS main weights with Q8 output tensors. LM Studio’s public CLI will not select the DFlash2 sidecar, so I loaded it through the authenticated SDK and checked the process arguments every time for --spec-type draft-dflash. At temperature 0, I tested built-in MTP, DFlash2 Q8 at draft n=4, and DFlash2 IQ4 at n=3 and n=4. IQ4 DFlash2 at n=3 won, reaching 20.9 t/s on a 768-token stream and 33.4 t/s on the tool call with 88.5% draft acceptance. The n=4 recipes you see online were optimizing for a different prompt.

Cold 200K recall took 55:57, so I would never run day-to-day work that deep on this model. My first recall test also capped completion at 32 tokens, all of which the model spent on hidden reasoning, so I nearly recorded a false failure. After raising the cap and reusing the live slot, it returned the exact needle in 7.6 seconds. That mistake was mine.

Thinking mode was a disaster. With low-effort hybrid thinking enabled, the model spent 28 minutes 46 seconds across three passes and two watchdog nudges without making a single edit. It simply reread the task and deliberated. I disabled thinking, reduced verbosity, and gave this model a 15-minute budget for its first edit because 20 t/s combined with a long reasoning pass cannot meet an eight-minute limit. It then wrote 88 lines of schema. The reviewer noticed that providerOrder was optional even though the scalar required it, the model fixed the line, and review accepted the repair in 15:57.

Both runs stopped after the schema without producing a migration or financial checks. That earned a C and reinforced that passing review on one slice is not the same as finishing the feature.

Devstral Small 2 24B

I started at Q8_0 because the hardware could accommodate the higher quality. It loaded successfully at 262K but decoded at only 8.2 t/s. ROCm prefetched faster than Vulkan, though neither improved decoding enough because 25 GB of dense weights was the limiting factor. For the scored run, I dropped to Q4_K_M and reached 13.3 t/s with F16 K/V, batch 4096, and ubatch 256.

After 29:34, it still had no commit. It invented Prisma @@check syntax that does not exist, wrote a schema for a different product with nullable one-to-one keys and the wrong snapshot fields, added a check nobody requested, failed validation in a loop, and then claimed it had committed even though nothing had changed. The harness retained the rejected SQL and left the branch clean. It earned an F, delivered with remarkable confidence.

Devstral 2 123B

This is the model that looks good on a vendor card, with a 72.2 SWE-bench score, but falls apart on this hardware. It required Q4_K_M plus Q4 K/V just to fit a 262K context; F16 K/V would not load. At about 70 GiB resident, it decoded at 2.8 t/s, and even a valid 27-token tool call took 12 seconds.

I started the 200K context gate and stopped it at 10K because the prefill slowed with every block. A least-squares projection put the total at 5.8 hours. At that point, it was no longer a useful recall test; it was an operational rejection.

It then spent 56:17 across 33 requests without committing. The model skipped the workflow files it was told to read, used replaceAll to insert Org-shaped relations into an unrelated Roster model, hit six Prisma errors, and “fixed” them into eight. The resulting patch had duplicate unique constraints with the same names, a second relation hanging off createdByUserId, SQL with snake_case columns the Prisma model did not map, and the wrong provider-order table. At 2.8 tokens per second, watching the result get worse made for a special kind of evening.

MiMo-V2-Flash 309B

This model occupied 99 GiB before context at UD-IQ2_M, because 309 billion total parameters with 15 billion active would not fit this box any other way. LM Studio’s automatic placement offloaded 26 layers and stopped there. The profile that successfully allocated 262K put every layer on Vulkan, eight MoE expert layers on the CPU, Q4 K/V, and flash attention, using about 89 GiB of VRAM. Six CPU experts ran faster at 17.5 t/s but failed the tool probe, while disabling flash attention dropped performance to 5.4 t/s.

I got it to recall a needle from 200K only after raising the completion limit above 256 tokens, which it had spent entirely on reasoning without returning an answer. On a warm retry with a 1,024-token cap, it found the exact needle in 16 seconds. That was technically impressive and operationally useless.

Tool calling never became reliable, whether thinking was enabled or disabled and whether the temperature was 0 or 0.3. I saw reads with empty paths, literal shell commands stuffed into XML, duplicated path fragments, and two-token <think> turns that ended the response. One tuning run produced a perfect tool call, but the following runs did not.

In the factory, it ran for 22:16 across 48 turns and received three increasingly direct watchdog nudges to edit the schema. A recovery prompt from the cloud reviewer eventually spelled out the exact change. MiMo generated another 5,666 tokens over almost six minutes without returning an action, so it made no source edits at all.

The results looked less conclusive in the middle of the experiment. Flash had a prior run that seemed complete until review caught checks that were not actually testing the change. The 27B model passed speed and tool tests plus exact 200K recall, then hit a loop guard before its first edit. Devstral Small passed speed, tool, and 38K recall tests before failing when it had to act like an agent. For about an afternoon, Coder Next was the only model with an accepted slice while 123B and MiMo were still unscored. The final results were clearer:

  • Qwen3.8 Flash Next passed exact 200K recall, ran at acceptable speed with working tools, and completed the best accepted schema and migration. It did not finish the full gift-card feature.
  • Qwen3-Coder-Next passed semantic 200K recall, ran quickly with working tools, and completed an accepted first slice. It did not finish the full feature.
  • Qwen3.8 27B had acceptable speed and tools and passed exact 200K recall. After getting stuck, it completed a schema-only repair with thinking disabled, but it produced no migration and did not finish the feature.
  • Devstral Small 2 passed the speed, tool, and 38K semantic-recall tests, but its factory run failed and nothing was accepted.
  • Devstral 2 123B loaded at 262K, but I aborted its 200K recall test after projecting a 5.8-hour runtime. Its factory run failed, and its attempted repairs increased the error count.
  • MiMo-V2-Flash loaded at 262K with CPU MoE experts and passed exact 200K recall after I fixed the completion cap. Its factory run never produced a reliable tool call or a single source edit.

Two models got the first slice accepted, but none finished the full gift-card feature. Coder Next is the one I would trust to keep running, while Flash Next produced the diff I would choose to merge. Neither is enough for the job I wanted this machine to do.

I’m Still Returning It

Flash Next was the best of the group, and I’m still boxing up the Strix Halo.

The first-slice win is real. Flash wrote the schema and migration, used a Prisma error to repair its own relations, and passed a reviewer that had never seen the working session. That’s better than many local-model demos, where the transcript claims success even though the files never changed. It is also only one slice out of eighteen, on a task the factory still needs a cloud model to plan and audit.

That’s the economic problem I couldn’t solve. Local inference saves some generation cost, but it does not replace the watchdog. You still pay for cloud tokens from the planner and reviewer, which are the parts that keep the factory honest. On top of that come the hardware, the quant roulette, LM Studio, and the nights spent discovering that n=3 beats n=4 on this exact sidecar. A Max subscription is expensive, but a Max subscription plus a 128 GB APU and cloud review costs more while still failing to deliver a whole feature unattended.

I wanted one machine running one local model that could produce review-grade output for less than renting Opus. Instead, I got a capable local junior that still needs a senior in the cloud.

This Would Have Melted My Brain in March

I should say the other half out loud, because “I’m returning it” can easily be read as “local models are a joke.”

Six months ago, this box producing an accepted migration with a self-repaired inverse relation would have justified a blog post by itself. Flash and Coder Next did that in about twenty minutes, inside a harness that rejected every fake success. Devstral’s mess never landed because the factory held, even when most of the workers did not.

We’re all a little stunned by the pace, myself included. The benchmarks putting a Qwen quant next to Opus are not fake; they simply measure something different from whether a model can build gift-card purchasing in a POS without falsely claiming it shipped. Vendor SWE-bench results helped me choose the lineup, but they could not tell me that MiMo would pass 200K recall and then fail to emit a tool call, that 27B would think for half an hour without touching a file, or that Devstral Small would invent evidence.

So the Strix Halo goes back, not because AMD missed, but because this box cannot yet support agentic engineering the way I actually ship software. I’ll keep watching Flash Next because it earned that much. I’ll buy the next machine when a local worker can finish a full feature without requiring me to pay for a cloud babysitter.