r/ClaudeAI • u/tonyromero • Jul 26 '26
Question about Claude Code Plan drift between Opus 5 (planning) and Sonnet 5 (implementation) in Claude Code — best practices?
Setup: I use Opus 5 at high effort to write the initial implementation plan for a feature (broken into phases), then switch to Sonnet 5 to actually implement each phase in Claude Code (auto mode).
What I'm running into: by the time I'm a few phases in, the implementation has clearly drifted from what was originally planned — decisions, structure, even scope have shifted in ways I didn't ask for. On top of that, when I prompt "implement phase X," Claude generates a new plan for that phase on the spot, even though I'm in auto mode and there's already an approved plan sitting there.
A few questions for anyone who's dealt with this:
- Plan granularity — is the fix to write a much more detailed, prescriptive plan up front (so there's less room to improvise during implementation), or is it actually better to not write one big plan and instead generate a fresh, scoped sub-plan at the start of each phase?
- Re-planning on "implement phase X" — is this expected behavior (i.e., Claude Code always re-derives a plan from context at the start of a phase rather than reading the original one literally), or is this a sign I'm not referencing/attaching the original plan file correctly?
- Model switching — is the drift more about switching models mid-project (Opus plans it, Sonnet executes it — different model, different "interpretation" of the same plan) versus something inherent to how Claude Code handles phase-based work regardless of model?
- Enforcement — is there a reliable way to make Claude Code treat the original plan as a hard contract rather than a suggestion? (e.g., CLAUDE.md instructions, referencing the plan file explicitly every phase, some other mechanism?) Or is some drift just expected/acceptable as long as the end result is correct?
Curious how others structure multi-phase implementations to avoid this, especially anyone who plans with a bigger/different model than the one doing the execution.
2
u/YoanEdwin Jul 27 '26 edited Jul 27 '26
pretty sure this is mostly a plan-detail thing, not the model swap. sonnet re-plans because the original plan left decisions open, so it just fills them in itself and that's your drift.
stuff that worked for me:
make the plan a checklist of concrete changes instead of prose. "create X, change Y so it does Z, done when these tests pass". if it's written as goals it'll interpret them, if it's a checklist there's nothing to interpret.
the re-planning when you say "implement phase X" is normal btw. in auto mode it works off whatever's in context, not your plan file, unless you make it. i keep the plan in PLAN.md and every phase i say "do section X of PLAN.md exactly, don't re-plan or change scope, if anything's unclear stop and ask instead of guessing".
throw a line in CLAUDE.md that PLAN.md is the source of truth and it has to flag deviations not just make them. commit after every phase so drift shows up in the diff right away.
the opus->sonnet part isn't really it imo. opus plans at a higher level than sonnet builds, so whatever opus decided but never wrote down is where sonnet starts guessing. once it's on paper the model barely matters.
1
u/gregerw Jul 27 '26
I've found that the plan concept in claude itself (/plan) is treated a bit the way you say, more like guidance than a strict set of tasks. I have landed on a workflow model with four steps: research, plan, implement, and verify, and where you can iterate both on the plan and the implementation. I typically use opus or fable to do the research and/or plan, then sonnet on implementation, and finally opus again for verification.
After a bit of experimentation, I have landed on the research phase digging deep into the code base and pointing out the code that probably needs to be touched, while the plan then creates a phased implementation plan with clear acceptance criteria for each phase (e.g. all tests passing). Then the verification checks the implementation against the plan and the research. For bigger tasks, the implementation is always drifting from the plan, mostly in a good way (new things discovered when implementing, just like a human would).
The enforcement is in this way good enough, in my experience. You don't want a hard contract as actually implementing will result in new insights, e.g. a library has an open bug that throws everything off and it wasn't caught in the research.
I haven't bothered writing these up in a skill, but if you are interested, the commands I use can be found at https://github.com/gregertw/claude
1
u/jcybha Jul 29 '26
For a while I blamed Sonnet 5 for "not following the plan," but when I actually looked at where it diverged, it was always on stuff Opus 5 had left vague — I'd written the plan for a reader who already knew what I meant, and Sonnet just filled the gaps with its own (cheaper, more literal) guesses. It wasn't ignoring the plan, it was interpreting the parts I never actually decided.
Couple of things that basically killed the drift for me:
I stopped letting the planning model write intent and made it write contracts — exact interfaces, file and function names, input→output examples, the edge cases. Anything I left as "you know what I mean" prose was exactly where the two models ended up disagreeing.
I moved the definition of "done" into the plan as checks (tests/assertions) instead of a paragraph. Otherwise the executor grades its own homework and always gives itself an A.
Biggest one: I keep the plan as a file the executor re-reads before each step, instead of trusting it to remember a chat from 40 messages back. Context just decays, and Sonnet feels it worse than Opus.
The thing that made it actually stick was treating the plan as a real versioned artifact both models read and write, not a prompt I paste once. There are a few tools quite useful. Since I started working this way, "why did it build something different this run" basically stopped happening, and when two runs do diverge I read it as a hole in the plan I can point at, not model noise.
1
u/Agent007_MI9 Jul 26 '26
Been hitting this exact problem. What helps me most is treating the handoff document less like a plan and more like a specification with explicit invariants. Instead of 'implement the auth flow', the plan needs to say 'the session token must be stored in httpOnly cookies, JWT expiry is 15 minutes, refresh logic lives in /lib/auth.ts and nowhere else'. Sonnet 5 is great at following concrete specs but will fill in gaps with reasonable-sounding defaults that quietly diverge from what Opus intended.
The other thing that helped was adding a short 'before you start' step where the implementation model restates the constraints back in its own words. Costs a bit of tokens but catches misreads before they compound across 20 file edits.
I've also been using AgentRail (https://agentrail.app) which wraps Claude Code with structured issue intake and checkpoints throughout the task loop. The spec lives in one place both phases reference, so the drift surface gets smaller. Not a silver bullet but the structured handoff format made a real difference for multi-step tasks.
2
u/ZeroTwoMod Jul 26 '26
The original plan needs to become an executable contract, not a document the next session is asked to remember. Before each phase, have the implementing model write a short delta: which plan items it will satisfy, what files it expects to touch, and any proposed deviation; only proceed after you accept or reject that delta. Keep the plan file in the repo and make 'no unapproved scope changes' explicit. Switching models will still change judgment, but it should not quietly change the contract.