Case study, personal infrastructure
The model wrote code that looked right.So I built the thing that checks it.
One engineer can now attempt what used to need a team. That sentence is easy to say and expensive to mean, because an agent is only worth having if you can leave it alone, and you can only leave it alone if the models are good enough and the boundaries are real. So I built the thing that measures the first and enforces the second. Here is what it is, what it caught, and where it still stops short.
- M4 MaxApple silicon
- 64 GBunified memory
- 40GPU cores
- ~85tokens per second
01
Why a person builds this
Start with the part that has nothing to do with technology. The gap between having an idea and having something real has always been staffing, and for the first time it might not be.
I have spent eleven years watching good ideas die in the queue. Not because they were wrong, and not because nobody could build them, but because building them needed four people for three months and there were only ever two people and one month. That arithmetic is the reason most software that should exist does not.
An agent that writes and checks its own code changes that arithmetic, but only if you can walk away from it. An assistant you have to supervise line by line is a slower version of typing. The whole value is in the leaving alone, and leaving something alone requires two things I did not want to take on trust: that the model is genuinely good enough, and that the worst thing it can do is something I could survive.
Neither is answerable by opinion. The first is a measurement problem and the second is a permissions problem. So I bought one machine, put both questions to it directly, and this page is the answer to each.
The rest of this is in two registers on purpose. If you run a company, sections 01, 02, 05 and 09 are the ones that matter and they assume nothing. If you write code, 03 through 07 are where the receipts are.
02
The shape of it
Two ways in, one spine, and a hard stop at the end. Everything drawn here is running today.
My phone
via Telegram
Autonomous
- Runs as its own macOS user, not as me
- Local models only, no cloud provider configured
- Confined to one workspace directory
My laptop
via Terminal
Hands on
- Runs as me, with my privileges
- Local by default, can escalate to a frontier model
- For the work I want to watch happen
Context and gates
Current library documentation, plus the scripts a task has to survive.
Inference server
Stateless, OpenAI compatible, bound to the machine and never to the network.
The model
Qwen3.6-35B-A3B, 4-bit, chosen by measurement. The bake-off is section 04.
One workspace
Every project lives here. Nothing the agent does happens anywhere else.
Five gates
Install, lint, typecheck, test, build. A task is not done until all five pass.
A local commit
And it stops there. Pushing is mine. Section 05 explains why it has to be.
The two lanes are the part people miss. They are not two interfaces onto one thing, they are two different levels of trust. The phone lane runs as a separate operating system user with no ability to reach my documents, my keys or my work, because that lane is the one I use when I am not watching. The laptop lane runs as me, and is for the work I want to see happen. Both talk to the same server and write into the same workspace, so a project started on a train can be finished at the desk. The truth lives in files and git, not in any tool’s memory.
The hardware decides the shape of all of it. Sixty-four gigabytes of unified memory puts the ceiling at around thirty-five billion parameters at a speed you can hold a conversation with. That single constraint is what turned model selection from a matter of taste into a matter of measurement, which is section 04.
The test I set myself was deliberately unglamorous: away from the desk, no laptop, scaffold a small TypeScript project from my phone, put it through every gate, and commit it. It passed in July, from a pub. That is the whole system working end to end, and it is also the honest ceiling of what it does today.
03
The code looked right
The proving project was deliberately dull: a small chat interface, no database, no authentication, nothing to hide behind. The local model wrote most of it and the result looked entirely reasonable. I read it anyway, and found a real bug in how it handled a stream of newline delimited JSON, the kind that survives a glance and fails on the second message.
“Local model output looks right and still carries real bugs.”
That is the whole thesis of this page, and it arrived early enough to shape everything after it. The interesting problem with generated code is not that it is bad. It is that it is plausible. Plausible code passes review by a tired human and fails in production, so the answer cannot be more careful reading. It has to be a machine that does not get tired.
So nothing the agent writes is considered done because it says so. A task is finished when a script has installed the dependencies, linted it, type checked it, run the tests and produced a build. The same script I run by hand is the one the agent has to satisfy, which means there is no gentler standard for the machine than for me.
04
I stopped reading about models and measured them
Two structured bake-offs, both run on the machine above, because a benchmark published by somebody with a different box answers a question I was not asking. I wanted to know what was good here, at a speed I would actually tolerate.
Three builds, one task
Qwen3.6 35B-A3B 4-bit
Cleanest behaviour. Won.
- Result
- 20/22
- Speed
- ~85 tok/s
Qwen3.6 35B-A3B 8-bit
Twice the precision, two points worse.
- Result
- 18/22
- Speed
- ~85 tok/s
Qwen3.6 27B 8-bit
Best answers, too slow to hold a conversation with.
- Result
- 21/22
- Speed
- ~16 tok/s
The middle row is the one worth stopping on. It is the same model as the winner, at twice the weight precision, which is supposed to be the better build. It scored two points lower and cost roughly thirty gigabytes of memory to do it. Had I taken the received wisdom, I would have run the worse model and never known.
Every specialist lost
Qwen3-Coder 30B-A3B 4-bit
Fastest thing tested, and it emitted corrupt tokens.
- Result
- Corrupt output
- Speed
- 110 tok/s
Qwen3-Coder-Next 80B 4-bit
Genuinely good, and unusable at conversational speed.
- Result
- Cleanest code
- Speed
- ~16 tok/s
Qwen 4 Coder 32B —
The article recommending it had invented the model ID.
- Result
- Does not exist
- Speed
- —
The third row is not a joke. A well ranked article recommended a model, gave its identifier, and the model did not exist. Not deprecated, not renamed. Invented. It is a small thing that says something large about how much of the current writing on local models is worth acting on, and it is the reason the rest of this section is measurements rather than opinions.
What I took away: more precision is not free quality, the specialists were worse than the generalist here, and the real bottleneck was never the model’s intelligence. It was how much it talked to itself, and how fast. I then deleted around ninety-five gigabytes of models that had lost.
05
The agent cannot push, and not because I asked it not to
Most writing about agent safety is a list of instructions: do not touch this, always confirm that. I wrote one of those documents too, and it is useful. It is also, in the end, a piece of prose being read by something that produces prose for a living.
“A command policy document is documentation, not enforcement. If the policy file and the operating system ever disagree, the operating system wins.”
So the boundary is not written down, it is built. The agent runs as its own non administrative user on the machine. My home directory is closed to it. Its workspace sits outside every home directory on the system, owned by it and nothing else. It has no administrative rights and is never granted any: everything it legitimately does needs none, so a task that demands them is the signal that a human should be doing it.
It holds no access token and no key that can push anywhere. That is why it cannot reach a remote repository. Not a rule it is obeying, a capability it does not have.
The difference matters more than it sounds. A rule is a thing that holds until something unusual happens, and unusual is the normal operating condition for a language model. A missing credential holds when the model is confused, when the prompt is adversarial, and when I have made a mistake. It is the only part of this system I would still trust if every other part of it failed at once.
06
Three bugs, and what was actually wrong
Running this yourself means the failures are yours too, and none of them arrive labelled. These three are here because in each case the symptom pointed nowhere near the cause.
A one sentence answer took three minutes and thirty-nine seconds.
The model was thinking out loud where I could not see it. On simple prompts the hidden reasoning was consuming around 97% of the token budget: 831 reasoning chunks against 20 chunks of actual answer. One sentence cost 25,000 invisible tokens.
Turned thinking off at the client, per request, rather than switching to a weaker model.
3m 39s~3s
The agent reported zero tools available, on every single request.
A grouped toolset had been switched off, and its membership quietly covered every file and terminal tool the agent had. Disable beats enable, so a single line removed the lot without ever saying so.
Removed the group. The failure was silent by design, which is what made it expensive.
Backspace did not work over the remote session. I lived with it for four phases.
The terminal I use locally advertises a name the remote machine had never heard of, so it fell back to a keymap where the delete key means something else.
Installed the missing terminal definition on the far side. Four phases of irritation, one entry in a database.
07
One of them was not my bug
Everything above is my account of my own machine, which is worth what you decide it is worth. This part is not. It is on the public record and you can read every line of it without taking my word for anything.
The server I run the models through streams responses back in pieces, and the final piece is supposed to carry a flag saying why it stopped. On a natural ending it never did. If the model ran out of room the flag appeared correctly, so the bug was invisible to anybody whose prompts were long. Mine were short, and every strict client I pointed at it rejected the stream outright.
I traced it to the point where the engine finishes a response, filed the bug with a reproduction, and wrote the patch. The maintainer asked for regression coverage, added it alongside the tests I had proposed, resolved a conflict with a parallel fix, and merged it.
An OpenAI compatible inference server for Apple silicon
It is three lines of consequence in somebody else’s project, which is roughly the size of most real contributions. I include it because it is the one claim on this page that survives a stranger clicking it.
08
What it still cannot do
A page about a machine in your own house, written by the person who owns it, is worth about as much as its worst admission. So here are the five a reader would otherwise have to ask about, starting with the one that matters most.
It scaffolds and checks. It does not yet ship
What runs today is the engine, the boundary and the gates. The layer above it, project templates, a push and review flow, preview deploys, is designed and written down and not yet built. Anyone describing this as an idea going in and a product coming out is describing my plan, not my machine.
It cannot survive a power cut on its own
Disk encryption has no unattended unlock, so a cold boot leaves the machine off the network until somebody walks over and types a password. Remote access is only as good as the last time the room was occupied.
Thinking is still expensive
The reasoning tax is managed, not solved. It is switched off where it hurts and left on where it helps, and deciding which is which is still a judgement I make by hand.
Sixty-four gigabytes is the ceiling
Thirty to thirty-five billion parameters is as far as this box goes at a usable speed. The models above that line are not slow here, they are absent. That is a second machine, not a setting.
Nothing it writes reaches the internet
The agent commits locally and stops. Every push is mine, deliberately, and that is a limit I intend to keep rather than a gap I am working on.
09
Why bother
Because of the arithmetic in section 01. Four people for three months is why most software that should exist does not, and that number is moving. Almost all of the difficulty has moved with it. It is no longer in producing the code. It is in knowing whether the code you were handed is any good, and in making sure the thing that produced it cannot hurt you while you find out.
That is not a prompt engineering problem. It is an engineering problem, and it looks exactly like the ones I have spent eleven years on: measure instead of assume, put the boundary somewhere the kernel enforces it, and never let anything call itself done because it feels done.
I did not build this to have a local model. I built it to find out what changes when the model is yours, the hardware is yours, and every mistake is yours to diagnose. What changed is that I now trust generated code less, and ship it more. The next thing to build is the layer that takes a checked commit and turns it into something a user can open. When that exists this page will say so, and not before.