All articles
Harness engineering · September 8, 2026 · 9 min read · 4 of 8

Running a coding agent on a laptop in 2026: what works, what breaks, and the numbers I measured today

I spent this afternoon getting two open-source coding harnesses to run a 30-billion-parameter model against real SWE-bench tasks on a MacBook Pro, with nothing leaving the machine. It works. It also took four detours that none of the quickstarts mention, and since the numbers along the way turned out to be the useful part, I wrote them down.

The model

The consensus pick for a 64 GB Mac in 2026 is a mixture-of-experts model with about three billion active parameters, which in practice means Qwen3.6-35B-A3B or Qwen3-Coder-30B-A3B. Sebastian Raschka's write-up on local coding agents settled on Qwen3.6 as the default, with North Mini Code as a peer, and OpenHands' local-LLM documentation recommends Qwen3.6-35B-A3B as the first model to try. I went with Qwen3-Coder-30B-A3B at Q4_K_M from the Ollama library, because it is the coding-tuned sibling and it declares native tool calling.

Two facts about its size matter more than the file. The download is 18 GB. Loaded with a 64k-token context, flash attention and an 8-bit KV cache, it occupies 22 GB of GPU-wired memory on my M1 Max, and 22 GB is the number to plan around.

Speed, and why it is fine

Raschka's rule of thumb is that anything past 20 to 30 tokens per second is workable for agent loops. A benchmark on an M4 Max with the 35B-A3B model put Ollama at 40 to 46 tokens per second, llama.cpp at 66 to 71, and MLX somewhere between 84 and 124 depending on whether the requests go through HTTP.

The same model on the same Mac under four serving stacks. Bars are from antekapetanovic.com on an M4 Max. The dashed lin
The same model on the same Mac under four serving stacks. Bars are from antekapetanovic.com on an M4 Max. The dashed line is my median on an M1 Max under agent load.
My own M1 Max through Ollama, measured across 113 requests during an actual agent run today, gave a median of 19 tokens per second, with a range from 14 to 46. The low end came while the iOS simulator and a browser were also using the GPU, and the high end came when the machine was otherwise idle. On an older chip under real conditions, the low twenties is what to expect. Prompt processing ran between 100 and 400 tokens per second, which matters more than it sounds, because an agent turn is mostly prompt. The working view grows with every tool result, and the server has to re-read whatever part of it is not already cached.

At 19 tokens a second, a 250-token reply takes about 13 seconds. One agent step, including the model reading the new tool output, runs 15 to 40 seconds, so a task that takes forty steps takes twenty minutes. That is slow, and it is the honest budget. If you want to halve it, run llama.cpp or MLX instead of Ollama and give up Ollama's convenience along with the Anthropic-compatible endpoint I describe below.

Which harness will talk to it

Every major open harness accepts an OpenAI-compatible base URL now, so the endpoint itself is not the obstacle.

OpenHands reads a base URL through LiteLLM and documents Ollama, LM Studio, vLLM and SGLang. OpenCode takes a provider block in its JSON config, using the @ai-sdk/openai-compatible adapter pointed at localhost:11434/v1, and Ollama's integration guide for it says local models need a context of 64k or larger. Codex CLI has an --oss flag for Ollama and LM Studio and a custom provider table in its config file. mini-swe-agent is LiteLLM underneath, so a model name of openai/qwen3-coder:30b plus an api_base is the entire configuration.

The one that surprised me was Claude Code. Since January 2026 Ollama has served the Anthropic Messages API natively, so setting ANTHROPIC_BASE_URL to the Ollama server and ANTHROPIC_AUTH_TOKEN to any string points Claude Code at a local open-weight model. Raschka tested this and noted that Claude Code used about 578,000 input tokens across 25 turns on one task, far more than Codex CLI on the same model. That is the same per-turn-volume effect the Scaffold Effect paper measured across open harnesses, where it found up to 40x the tokens for the same pass rate. Locally, tokens are seconds, so the sensible way to pick a harness is by how much it sends per turn.

Does the harness matter more than the model?

Raschka's five-task test found that Qwen3.6 did better through Codex CLI than through Qwen-Code, the harness built for it. That is a single informal observation, and whether it holds under a controlled design is the study I am running now; the pilot started today. What is already established is that small open-weight models are good at one kind of work and bad at another, and it is worth knowing where the line falls before you pick a task.

Pass rate by capability tier for selected models, from AgentFloor's six-tier ladder.
Pass rate by capability tier for selected models, from AgentFloor's six-tier ladder. "n/r" means not reported in the extract I worked from.
[AgentFloor](https://arxiv.org/abs/2605.00334) ran 16 open-weight models from 0.27B to 32B parameters, plus GPT-5, through a deterministic 30-task ladder. The tiers are instruction following with no tools, a single tool call, two tools chained, conditional branching on an intermediate result, multi-source synthesis, and long-horizon planning under constraints. Small models clear the first three rungs; a 3B Ministral reaches 88 percent on single-tool calls. Nobody, GPT-5 included, reaches 80 percent on branching or synthesis, and nobody clears the long-horizon tier at any threshold. The steepest drop in their whole heatmap is between chaining two tools and branching on their result.

Two of their counterexamples have stayed with me. A 24B Mistral fell from 96 percent on instruction following to 16 percent on two-tool chaining, which the authors attribute to a tool-template mismatch rather than to capability. And a 4B Nemotron beat a 26B Gemma on multi-source synthesis. Parameter count, as they put it, is "a poor predictor of agentic capability across model families." The practical consequence is that you have to test the model and harness as a pair.

The four detours

These are what consumed the afternoon, and they are the part I would have paid someone to tell me in advance.

The first is that the benchmark images are built for x86. SWE-bench's evaluation containers are published for x86_64, and the mini-swe-agent documentation says plainly that they cannot be used on Apple Silicon directly. Under QEMU emulation a full SWE-bench Verified run has been reported at about 14 hours, against 2 to 3 hours with native images. The fix is that Docker Hub also carries swebench/sweb.eval.arm64.* images, and Epoch AI publishes an arm64 registry covering 1,819 of 2,294 instances, untested. All ten of my pilot tasks had arm64 images. Check yours before you pull the model; I wrote a ten-line loop around docker manifest inspect to do it.

The second is that Ollama listens on loopback only. Containers reach the host through host.docker.internal, which under Colima maps to the host gateway. That worked on the first try, but only because Colima routes the gateway to the host's loopback address, and on other setups you will need OLLAMA_HOST=0.0.0.0.

The third is that the Docker VM's memory reservation is invisible until it hurts. I started Colima at 16 GB out of habit, and the containers then used 7 MB. With the model's 22 GB wired and the VM's 16 GB reserved, the machine went to 9 GB of swap and the fans came up. Resizing the VM to 6 GB and four cores fixed it, and the workload now sits at 25 GB against a 36 GB ceiling that I enforce with a thirty-second sampler. Django's test suite does not need the memory, and yours probably does not either.

The fourth is that the default context is not what the model declares. Qwen3-Coder declares 262k. The server runs whatever you configure, and the KV cache is what you pay for. I set 64k, because OpenCode requires it and because the Same Model, Different Harness result shows the harness's context handling stops mattering well before 256k. At 64k with an 8-bit KV cache the cache is a few gigabytes; at 256k it is not.

One thing that was not a detour: tool calls parsed on the first try. A raw request to Ollama's /v1/chat/completions endpoint with a bash tool schema came back with a well-formed tool_calls block from Qwen3-Coder. AgentFloor's Mistral result is a reminder not to assume that for every model.

What twenty minutes buys you

My first smoke run was mini-swe-agent against a single Django task. After 78 model calls and about twenty minutes it had not submitted a patch, and I stopped it to reconfigure memory. That is one trajectory and it proves nothing about the harness or the model. What it does give me is the shape of the budget: on this hardware a real task is tens of steps and tens of minutes, and ten tasks through two harnesses is an overnight job, which is what I scheduled.

If you want the same setup, the sequence that worked for me was to pull the arm64 task images first, size the Docker VM small, start Ollama with an explicit 64k context and a single-model limit, verify a tool call by hand, run one task under a hard timeout, and only then batch. Everything else in the quickstarts is right.

What I did not expect to write is that the model is the easy part. It is eighteen gigabytes and one command, and it works. The afternoon went to the layers around it, which, if you have been following the harness-engineering literature this year, is the whole point.

Sources