Opus planned it, Sonnet built it, and the plan had the bugs
Claude Code has a model setting called opusplan. In plan mode it uses Opus. When you approve the plan and Claude starts executing, it switches to Sonnet. The idea is simple: spend the expensive reasoning on the design and the cheaper, faster model on typing it out.

This week I used it for a real feature on this site: a live Workbench. Its entries moved from a hardcoded Scala file into PostgreSQL. They are managed in my blog admin, and a Promote button copies a card from my private FOCUS board into a Workbench draft. It touches a Flyway migration, authorization, two React apps and the public page, so it was a fair test.
Then, before shipping, I switched to plain Opus and ran a deep review. I found the review's result more interesting than the feature: the two worst findings were not Sonnet's mistakes. They were lines in the plan I had approved.
How the session actually ran
The session transcripts record which model answered each turn and when every subagent started, so the timeline below comes from the logs, not my memory.

Plan, on Opus (about 20 minutes). Opus sent four Explore agents through the repository, then a Plan agent to draft. It asked me three design questions:
- how publishing should work;
- whether a promoted entry stays linked to its card;
- what the card should show afterwards.
The plan it produced was thorough: the schema and seed, a table of when "Last updated" moves, every route, the tests per spec, the docs to update, risks, and a manual verification script. It also found a security problem before any code existed. My blog's publishing token covers the whole /api/blog/admin/ prefix, so a promote route under that prefix would let the token copy private cards. The plan specified a two-layer carve-out for exactly that route.
Build, on Sonnet (about 30 minutes). Sonnet worked in the main thread with no subagents and followed the plan closely: the migration, the schema-contract checks, the repository, the controllers, both UIs and the docs. It finished with 209 backend tests on real PostgreSQL, 265 frontend tests and a 26-check script against the running stack with a real Keycloak sign-in. The tests caught one real bug on the way: Anorm binding an Option[UUID] as varchar. The token carve-out was implemented as specified, and later the review could not break it.
Everything was green. The browser extension had not connected, so nobody had looked at the UI.
The review, back on Opus
I switched with /model opus and ran /ship, my documented path to production, asking for a deep review of what Sonnet did. Its review step fans out:
- 11 finders, each looking for one kind of problem;
- 28 candidate findings, each handed to its own verifier that tries to refute it;
- a final sweep.
It ran for 55 minutes and reported 15 findings. /ship applies clear, low-risk fixes and sends judgment calls back to me. Ten were fixed directly, two came to me as questions, and three are documented follow-ups.

The plan had the bugs
The two questions that came back to me were the most serious findings, and both were plan decisions.

An immutable slug. The plan made each entry's anchor slug immutable, so links survive a retitle. Promotion builds the slug from the card title, and FOCUS card titles are private notes to myself. Promote a card, retitle it for readers and publish it, and the old wording sits in the page source for good. The fix: the slug follows the title while the entry is a draft, and freezes on the save that publishes it.
Last write wins. The plan listed it as an accepted risk. The review spelled out the concrete case: unpublish an entry in one tab, fix a typo in a stale second tab, and the typo fix silently republishes it. Seeing that scenario, I changed my mind. A stale save now answers 409.
A conditional check. The plan said that if a keyboard test showed Enter on a card button starting a drag, add a keydown stop. No keyboard test ran, and Enter on Promote did start a drag. The same plan asked for a visual check at narrow widths. That one never happened either, and in Chrome the card's "Del" button sat alone on its own line.

The findings that really were Sonnet's code were the ordinary kind a review exists for:
- a title made of no-break spaces passed validation, and a NUL character returned 500;
- note line breaks collapsed on the public page;
- saving one stage's order dropped unsaved reorders in the others;
- admin dates were shown in the browser's time zone.
All of them were fixed before merge.
What I take from it
Sonnet does what the plan says, including the mistakes. That is the point of the split, and it is also its weakness. Sonnet did not question an immutable slug or an accepted risk, and it read "if a test shows" as optional. A plan handed to a faster model is a specification, so write it like one.
Opus caught Opus. The planner and the reviewer were the same model. The difference was a fresh context and a different question: "how does this go wrong?" instead of "how do I build this?". The verifier step mattered as much. A refuted finding never reached me.
opusplan stops at the plan boundary. It covers plan and build. The review and the browser check are steps I had to add, and on this feature they were where most of the value came from.
What I change next time
- No conditionals in a plan for Sonnet. "Verify Enter on Promote presses it; if not, stop keydown" is a step with a pass condition. "If a test shows…" is a suggestion.
- Judgment calls go in the plan as open questions, each with a concrete failure scenario, so I decide them before code exists, not during review.
- A browser look is a required gate before
/ship, with a screenshot of every new UI state. - The deep review runs in a separate context, on the bigger model, with a verifier per finding.
The feature shipped as one PR with 211 backend tests and five green CI jobs. I would use opusplan again. Next time, though, I will spend longer reading the plan, because that is where this feature's worst bugs started.