A couple of posts back I pulled a month of my own session logs to catch coding red-handed as the token hog, and the meter came back flat. Planning and building cost about the same to run. The expensive part is the thinking that has to happen before the typing, and that thinking is mine.
If the typing is the cheap, fast, mechanical part, why am I paying frontier-model prices for it? Why is Opus, the model I keep around because it can think, the one grinding out a config file from a spec I already wrote?
It shouldn’t be. I should keep the expensive model for the one thing it should, and hand all the work after that to the cheapest models that can write code. Which, it turns out, means splitting the work across two different tools. Claude Code plans, Opencode builds.
This is part four of a series about working with AI coding agents on an open-ended project through a single living PLAN.md instead of vibe coding or spec-kit ceremony. Post one is the thesis: the doc is the deliverable, the code is the byproduct. The last few were about keeping that doc lean and figuring out where the real cost lives. This one is about what happens when you stop paying the planner to type.
- The Setup: One Expensive Model Doing Two Jobs
- Enter opencode (and a Roster of Cheap Models)
- The Rule I’d Just Landed On, and Why I Inverted It
- Reviewers That Don’t Share the Builder’s Blind Spots
- The Tell: The Diagrams Came Before the Code
- Three Docs, Three Sets of Write Permissions
- The Whole Truth
- What’s Next
The Setup: One Expensive Model Doing Two Jobs
For most of this series my setup has been one tool wearing two hats. I plan in Claude Code with Opus. Plain chat, back and forth, drilling down until the design is actually pinned. Then the same Claude Code hands the build work to Sonnet sub-agents. Opus knows, Sonnet does. On the Pro plan I could talk to Opus for a long planning session and barely dent my usage, because in build mode the token-heavy churn (the greps, the file reads, the edits) happens in a sub-agent’s own context and comes back as a short summary. The orchestrator stays lean.
That’s a fine setup. But it has Opus, or at least a Claude model, standing over every build task, and I’d just proven to myself that the build tasks are the mechanical part. I was using an expensive model to assemble flat-pack furniture. The instructions were already written. Any model that can read can do the work.
Here’s the framing that made me actually move, and it’s stolen from a thing I said to myself during a debugging session months ago: models are the Temu of things. They’ll have knowledge of many things, but the most generalized version of it. A one-line prompt gets you the average of everything ever written on the subject. And if the build task is fully specified, if I did the custom thinking already and wrote it down, then Temu is exactly what I want. Generalized competence at assembling a known thing is cheap and abundant. I just had to stop buying it at boutique prices.
Enter opencode (and a Roster of Cheap Models)
The tool that let me do this cleanly is Opencode, a terminal coding agent that, crucially, lets you assign a different model to every agent through a repo-local opencode.json, routed through OpenRouter. So the plan stays in Claude Code with Opus, and the entire build crew lives in opencode, each role on whatever model is cheapest for its job.
Here’s the roster, trimmed from the real config:
{
"$schema": "https://opencode.ai/config.json",
"small_model": "openrouter/z-ai/glm-4.7-flash",
"default_agent": "orchestrate",
"agent": {
"orchestrate": { "model": "openrouter/z-ai/glm-5.2" },
"build": { "mode": "subagent", "model": "openrouter/xiaomi/mimo-v2.5-pro" },
"ai-pipeline": { "mode": "subagent", "model": "openrouter/deepseek/deepseek-v4-pro" },
"qa-fast": { "mode": "subagent", "model": "openrouter/deepseek/deepseek-v4-flash" },
"qa-deep": { "mode": "subagent", "model": "openrouter/google/gemini-3.1-pro-preview" },
"debug": { "mode": "subagent", "model": "openrouter/anthropic/claude-sonnet-5" },
"docs": { "mode": "subagent", "model": "openrouter/qwen/qwen3.7-plus" },
"research": { "mode": "subagent", "model": "openrouter/deepseek/deepseek-v4-flash" },
"architect": { "model": "openrouter/anthropic/claude-opus-5" }
}
}
Not a Claude model in the build path. GLM-5.2 orchestrates. A Xiaomi MiMo model writes the Go daemon and the CLI. DeepSeek handles the ML and retrieval code. Qwen writes the docs. The only Anthropic models in the whole file are debug (Sonnet, for when something’s genuinely stuck) and architect. And to tell you the truth, most of the model choices are just a test of what will work in that slot or even if I need all these different agents.
I’ve been coy about what this crew is building, so: it’s the Context Lake, the one-brain-outside-the-agents thing I wrote about a month back. A Go daemon that watches my session logs and my vault, a Python ML layer that indexes and retrieves, a CLI and a dashboard sitting on top. That matters here for exactly one reason: it is not a toy. It’s a real multi-language system with a knowledge graph, a vector store, and a daemon.
MiMo wasn’t even my first pick for the build. I started with GLM-5.2 doing double duty, orchestrating and building, and swapped the builder over to MiMo on day three (the commit message, optimistically, reads “for improved performance”). It’s cheaper on the output tokens a builder spends most of, though I’ll warn you the exact gap is a moving target: model pricing on OpenRouter whiplashes week to week, and as I write this GLM’s page is showing 76% off. The reason I kept MiMo, though, wasn’t the couple of cents. It was that GLM tends to lose the thread across a long series of tasks, and MiMo just… doesn’t. Stack it deep and it grinds through the whole queue. For a builder whose entire job is to chew an unattended stack while I’m somewhere else, that turned out to matter more than the price.
The division of labor is the same knower/doer split I’ve run all along, just stretched across a tool boundary:
- Claude Code + Opus is the planner. It’s where I think, discuss, and write the tasks. It never runs in opencode. The picks were made against live OpenRouter pricing using my weekly model-buzz research as the value spine, so this isn’t “cheap for the sake of cheap.” It’s cheap where cheap is fine, and one expensive model where thinking has to happen.
- Opencode is the whole build crew. It reads the tasks I wrote, does the keystrokes, runs the checks, reports back.
And architect, the lone Opus-4.8 agent in opencode? It’s set to manual-only and never auto-invoked. It’s my break-glass “call in a senior for a from-orbit sanity check” button for when a Claude Code session isn’t handy. A zero-invocation count on it is expected. I wrote a note in the repo telling future-me not to delete it as dead config, because future-me absolutely would.
The Rule I’d Just Landed On, and Why I Inverted It
Here’s where it got uncomfortable, because I had to break something I’d just decided was right.
In my single-tool setup, the builder works a queue of numbered tasks, and the rule for a task it can’t finish was: skip it and keep going. If piece 3 of 8 turns out underspecified, some fork I didn’t see, don’t halt the whole queue. Mark it blocked, move to piece 4, do everything that doesn’t depend on the broken one. An hour away should come back with six of eight done, not two. I was proud of that rule. It’s the thing that makes walking away safe.
Then I moved the build to opencode and inverted it completely. The rule now: hit something underspecified, stop and report. Do not keep going. Do not invent the next task. Do not promote anything from the parking lot.
Same person, opposite rule, two weeks apart. What changed?
The tool boundary changed. In the single-tool world, the thing that skips a blocked task and continues is the same context that could re-plan it. It’s all one agent, warm on the whole conversation, and “keep going past the blocker” is safe because the planner is right there. In the cross-tool world, the planner is a different tool. Opencode is a cheap executor that knows nothing about my planning thread and has no business deciding what to build next. Discovering work and scheduling work are different authorities, and I’d just handed them to different tools. If I let the cheap builder skip-and-continue, I’m letting the model I specifically chose not to think make the scheduling calls.
So the executor’s rule became rigid on purpose. From the actual contract in the repo:
You may NOT execute items in § Next (underspecified by design — stop at the tier boundary and report), may NOT promote items out of § Parking lot, and may NOT invent new tasks. If § Now is drained, SAY SO AND STOP.
Take the planner out of the room and put it in another tool, and the safe move flips to “stop and wait.” A rule is only as good as the context it assumes, and the second you move the work across a boundary, re-check every assumption.
Reviewers That Don’t Share the Builder’s Blind Spots
If cheap models are doing the building, the obvious worry is quality. My answer has two parts, and neither is “trust the cheap model.”
Part one: the reviewers run on a different bloodline than the builder. The build agent is MiMo; the deep reviewer, qa-deep, is Gemini; the debugger is Sonnet. A reviewer that shares the implementer’s model lineage shares its blind spots. It’ll wave through the same class of mistake the builder was prone to make, because it thinks the same way. Point review and implementation at the same model and review quietly stops catching anything. Different lineage, different blind spots.
Part two, and this is the one I got wrong first: the obligation to run the checks lives on the agent that can’t forget it. Originally I had the planner enforce QA: “call the reviewer after every step.” It decayed. And I have the receipts, because this is the kind of thing I’d rather catch myself than have a commenter catch. I audited 58 opencode sessions on this project and found QA delegation went from 5 calls in the first two weeks to 0 in the last two , while the builder kept right on running. The deep reviewer, whose trigger was the soft phrase “at milestones,” had fired exactly zero times. The invariants I care about were never actually being checked.
Why did it decay? Because a planner’s context fills up over a long orchestration and it drops the soft, optional steps, the same way you stop doing the stretches your physical therapist gave you. The fix wasn’t a sterner reminder. It was structural: move the obligation onto the sub-agents, which get fresh context on every spawn. The build and ai-pipeline agents are now not-done until they’ve run the checks and pasted the output into their report, and the planner rejects any completion report that lacks it. An obligation on a long-lived context erodes. An obligation on a fresh-every-spawn context can’t. Don’t ask a model to remember. Make it structural.
The deep reviewer got the same treatment. Instead of “at milestones,” it fires on a deterministic condition. When the current batch of work is drained, before the batch is reported done, it reviews the whole batch diff against a fixed list of the project’s load-bearing rules. A trigger you can’t measure is a trigger that never fires.
I did have to fix the ordering the hard way first, because my initial config looked right and wasn’t. Agents weren’t firing in sequence, QA was skippable, and a couple of agents never ran at all. So I rewrote it to force the line: build writes the code and runs its own tests, and only after the checks pass does the docs agent, which always reads the real code first, write anything. When I later asked Claude whether that fix actually held, the read was blunt: “Ordering is right. The QA-gate fix is genuinely first.”
And then, a couple of weeks later, the whole thing handed me a lesson I didn’t order. At the batch drain the gate was green across the board: 43 Python tests passed, ruff and mypy clean, go build and go test fine. Then I ran the thing against real data. The dashboard crashed on an actual projection file, and the project-identity code reported 6 projects where the vault holds 24 , four of the six being phantoms. Its last-resort rule for naming a project was “first tag containing a hyphen,” so it had been happily minting a project per topic tag. Both bugs sailed straight through a green gate. Making the checks structural fixed whether they run. It did not turn them into a proxy for the software being correct, and I had started quietly treating it as one.
The Tell: The Diagrams Came Before the Code
Here’s the small, dumb detail that actually convinced me the split works.
Back at the start, on June 26th, before a line of the Go daemon or the Python ML code existed, I had Claude draw the architecture as a set of mermaid diagrams. Not because the code was complex; there wasn’t any. Because the plan was: “is there any way that we can create a rough mermaid diagram of what’s going on here? Cause it’s getting kind of complex now.” The diagrams were a picture of the intended system, committed a few hours before the first daemon/ and ml/ directories landed that same evening.
Then the roster of cheap models built the thing, over weeks, one specified task at a time.
At some point I had Claude audit the built system, half-expecting the usual rot, docs that lie because nobody updated them. Instead: “OpenCode has kept the architecture honest.” The diagrams hadn’t been quietly patched to match the code. The code had been built to match the diagrams. Weeks of cheap-model keystrokes, and the shape at the end was the shape I’d drawn on day one.
That’s the real proof, and it’s humbler than “the cheap models are geniuses.” They’re not. But a plan they can’t skip, run in an order they can’t reorder, gets followed faithfully enough that the map you sketched before any code existed still describes the territory a month later. That’s the whole bet: the intelligence goes into the plan, and the plan is cheap to enforce.
Three Docs, Three Sets of Write Permissions
The last piece is the guardrail that keeps a cheap, eager builder from wandering off. When two tools share one repo, “who’s allowed to write what” stops being a style preference and becomes the fence that keeps them from fighting. Three docs, three permissions:
| Doc | What it is | Who writes |
|------------------|-------------------------------------|-----------------------|
| docs/vision.md | The vision, architecture, invariants| Planner only |
| TASKS.md | The current batch of committed work | Executor (checkboxes) |
| docs/archive/ | Completed batches, frozen | Planner, at replan |
The executor, opencode, can check off boxes in TASKS.md § Now as work lands, keep the status line current, and append discoveries to a parking lot. That’s it. It cannot edit the vision doc, cannot promote a parked item into the active work, cannot write the next batch. Those are planning authorities, and planning happens in the other tool, driven by me.
This sounds like bureaucracy until you’ve watched a helpful agent decide, unprompted, that it knows what you want to build next. The most load-bearing sentence in the whole config is the one that tells the opencode orchestrator, in so many words, to never tell the human to “exit plan mode” or hand the work back. Its job is to act by delegating to a sub-agent, not to bounce the plan back to me with a “ready when you are.” And the sibling rule, which longtime readers know I’ve promoted to a standing law: planning is a discussion, not a multiple-choice quiz. The moment a tool tries to compress the design conversation into three options, you lose the part that was doing the work.
The Whole Truth
What I’m solid on: the shape is right. Paying a thinker to type is a real waste, the cost data backs it up, and separating “who plans” from “who types” onto tools priced for each job is a clean way to stop doing it. The QA-decay finding is measured, not vibed: 58 sessions, 5 to 0, a reviewer that never fired. And putting the obligation on fresh context instead of a memory is a principle I’ll stand behind anywhere.
What surprised me is how smooth the unattended part has been. I can stack five tasks, walk away, and come back to a screen full of done. Actually unattended, actually finished, and I keep having Opus re-audit the whole thing because I half don’t believe it.
What I was wrong about is the finish line. I wrote most of this post feeling like the project was nearly done. It isn’t. The batch I cut on July 18th sits at 11 of 15 remaining, it got extended three days later after another dogfooding pass, and the scope has since grown a cross-repo orchestration layer and a skills catalog. The cheap crew is fast at the work I hand it. That was never the same thing as the work running out.
What I’m still not solid on: whether the cheap builders stay good enough, at scale, over time. The two bugs above are the first real data point, and they’re ambiguous on purpose. Neither is obviously a “a Claude builder would have caught this” failure, because both were spec gaps that a green test suite couldn’t see either. But they’re exactly the shape of thing I said I was watching for, they showed up inside a month, and one project going smoothly is still a sample size of one. The qa-deep reviewer is on a preview-tier model, which means it can get rate-limited, change behavior, or vanish out from under me on a random Tuesday; I keep a same-obligation, different-lineage fallback noted for exactly that. And the roster itself drifts. Model pricing moves, slugs get deprecated, and the config is the truth while any table I write about it is already going stale. The table in my own docs has lied to me before.
So: promising shape, real receipts on the why, genuinely unproven on the “are cheap models good enough” question that the whole thing rides on. Frontier of the method, not a settled result.
What’s Next
The plan lives in Claude Code with Opus, the build lives in Opencode with a crew of cheap models, the expensive model only thinks, and the whole thing is fenced by who’s allowed to write which file. It runs. Whether it holds is the thing I actually have to live with now instead of theorize about. Can Temu models carry a real build over months without quietly costing me more in bugs than they saved me in tokens?
There’s a bigger post hiding under this one, the thesis version: that planning is the only thing in the whole stack worth a boutique model, and everything downstream is keystrokes you can buy in bulk. I’ve now watched that hold across three projects, which is finally enough proof points to write it honestly instead of as a hot take. That’s the one I want to write eventually.







Top comments (0)