You pressed Shift+Tab to plan mode, Claude proposed a clean 5-step plan, you approved it. Execution starts. By the end, files D and E are edited (not in the plan), step 3 was skipped because “the existing code already does that” (it didn’t), and a “minor refactor for clarity” got smuggled into step 4. The PR is bigger than the plan and you can’t tell from the diff which changes were authorized.
Fastest fix: plan mode is a read-only research step, not an execution contract. Approving a plan just switches the session into an editing mode (auto, accept-edits, or review-each-edit) and tells Claude to start — it does not pin Claude to the literal steps. To bind execution: (1) make every plan step name exact files and symbols, and (2) before approving, append a “do not exceed this plan, stop after each step” instruction. Those two changes turn the plan into something Claude can be held to.
Common causes
Ordered by hit rate, highest first.
| # | Cause | Fastest tell |
|---|---|---|
| 1 | Plan was loose — steps name concepts, not files | Steps say “the billing flow”, no paths |
| 2 | Prompt had an implicit “improve while you’re there” | Prompt contains “also fix”, “clean up”, “while you’re there” |
| 3 | Claude silently revised the plan mid-execution | Diff doesn’t match the approved steps 1:1 |
| 4 | Plan steps too coarse (“refactor X for clarity”) | One-line abstractions, no concrete operation |
| 5 | Real edge case found during a step, folded in silently | One step’s diff touches files it never named |
| 6 | CLAUDE.md grants standing “improve where you see issues” | grep -i "improve|clean up|opportun" CLAUDE.md hits |
1. Plan was loose — Claude inferred extra work
Plan said “update billing flow.” Doesn’t say which files. Claude updated 5 files including 2 you didn’t expect. The plan didn’t pin paths, so any reasonable interpretation was authorized.
How to spot it: Re-read the approved plan. If steps reference concepts (“the billing flow”) not file paths, Claude had latitude to expand.
2. Original prompt had implicit “improve while you’re there”
You said “fix the bug AND clean up if you see issues.” The “AND clean up” is a license for scope creep. Claude planned the bug fix, then expanded execution to include cleanup it didn’t surface in the plan.
How to spot it: Search your prompt for “while you’re there,” “also fix,” “and improve,” “clean up.” Any of these authorize plan-overshooting.
3. Claude updated the plan silently mid-execution
The plan said 5 steps. Mid-execution Claude decided step 3 was unnecessary and skipped it without surfacing the change. Or it added a new step 6 because something looked broken along the way. This is a known, repeatedly filed behavior — see the open Claude Code issues on plan-mode adherence (e.g. issue #41062, #10398).
How to spot it: Compare actual diff to approved plan step-by-step. Discrepancies = silent plan updates.
4. Plan steps were too coarse
“Step 4: refactor billing.ts to use the new pattern” — what does that mean concretely? Claude can interpret broadly because the step didn’t specify.
How to spot it: Plan steps are 1-line abstractions (“refactor X for clarity”) rather than concrete operations (“rename processPayment to processCharge in billing.ts only”).
5. Edge case discovered during execution
Claude found a real edge case while executing step 2. Instead of stopping and asking, it incorporated a fix as part of the step. Now the diff is bigger than the plan.
How to spot it: Diff for one step touches files / concerns the step didn’t mention. Edge-case sweep.
6. CLAUDE.md authorized opportunistic improvements
Some CLAUDE.md files say “improve code quality where you see issues” — that’s a standing authorization that overrides plan boundaries. It matters extra in auto mode, where a classifier reads your CLAUDE.md content directly and treats it as intent.
How to spot it: grep -i "improve\|clean up\|opportun" CLAUDE.md returns rules that let Claude expand scope.
How plan approval actually works (June 2026)
Knowing the mechanic explains why “I approved it” isn’t enough. Press Shift+Tab to cycle modes: default (ask before each edit) -> acceptEdits -> plan. The status bar shows the current mode; in plan mode the footer reads plan mode on. In plan mode Claude only reads files and runs read-only shell commands, then writes a plan to ~/.claude/plans/ (set plansDirectory in settings.json to keep plans in your repo). The plan persists across /clear and context compaction.
When you approve, Claude presents a dialog. As of June 2026 the options are:
- Approve and start in auto mode
- Approve and accept edits
- Approve and review each edit manually
- Keep planning with feedback
- Refine with Ultraplan (browser-based review)
Whichever you pick, approving exits plan mode and switches the session into that editing mode, then Claude starts. Nothing in this flow forces Claude to execute the steps verbatim — that constraint has to come from the plan text and your execution instruction. Two consequences:
- If
showClearContextOnPlanAcceptis enabled, each approve option also offers to clear the planning context first. If you say yes, any rule you stated in chat (but didn’t write into the plan) is gone. Put your constraints inside the plan, not just in the conversation. - In auto mode, the classifier honors boundaries you stated in conversation (“do not touch files outside the plan”), but a boundary can be lost if compaction drops the message that stated it. For a hard guarantee, use a deny rule instead.
Shortest path to fix
Ordered by ROI. Steps 1 + 2 turn the plan into an enforceable contract.
Step 1: Make plan steps concrete + file-anchored
A useful plan step names files, symbols, and the exact change. Not abstractions:
Bad: "Step 3: Refactor billing logic for clarity."
Good: "Step 3: Rename `processPayment` to `processCharge` in src/billing.ts:42 and update 3 call sites (src/api/checkout.ts:87, src/jobs/billing-sweep.ts:23, src/services/refund.ts:55). No other changes."
A concrete step has a verifiable result. Abstract steps cannot diverge because they’re already too loose to diverge from. Before approving, press Ctrl+G to open the proposed plan in your editor and tighten any step that names a concept instead of a path.
Step 2: Bind execution with an explicit “do not exceed” rule
While still in plan mode, ask Claude to fold this into the plan (so it survives a context-clear on approval), then approve:
Execute ONLY the approved plan above.
- Do NOT add steps.
- Do NOT skip steps; if a step seems unnecessary, STOP and ask.
- Do NOT touch files outside the explicit paths in the plan.
- If you find issues outside the plan, list them at the end as TODOs — do NOT fix them.
After each step:
1. Paste a one-line summary of what changed.
2. Stop and wait for "proceed."
The “stop and wait” line per step is the contract enforcement. On approval, pick review each edit manually (not auto mode) the first time you’re testing whether a plan holds.
Step 3: Diff actual changes vs planned scope mid-execution
After Claude finishes a step, before approving the next:
git diff --stat HEAD
Compare to the step’s explicit file list. Any extra file = scope creep. Revert and re-prompt:
Your step 3 touched files D and E, which weren't in the plan.
Revert those changes. Step 3 only touches src/billing.ts.
Step 4: Reject silent plan updates
If Claude announces “I’m going to skip step 3 because…” or “I’m adding step 6 to handle…”:
Plan changes require my approval. Stop.
Tell me what you propose to change and why.
I'll either update the plan or you keep going as planned.
This catches drift at the announcement, not after the code lands. If it has already drifted, press Shift+Tab back into plan mode and re-plan from the current state — a high-leverage habit is a CLAUDE.md line: “If execution diverges from the approved plan, stop and re-enter plan mode.”
Step 5: Audit your prompt + CLAUDE.md for scope-creep authorizations
Search for and remove standing authorizations that override plans:
# In your prompts / CLAUDE.md, find:
grep -in "while you're there\|also fix\|opportunistically\|improve where you see" \
CLAUDE.md src/**/CLAUDE.md
Replace each with a tighter rule: “fix only what’s specified; flag other issues as TODOs.”
Step 6: For repeat divergence, lock it with a deny rule + commit-per-step
A CLAUDE.md instruction is a strong nudge, not a wall. If Claude keeps wandering into the same off-plan directory, add a hard deny rule in .claude/settings.json so edits there are blocked regardless of mode:
{
"permissions": {
"deny": ["Edit(src/legacy/**)"]
}
}
Then force a commit after each plan step so each commit’s diff is the auditable unit:
After each step:
1. git add <only-the-step's-files>
2. git commit -m "step N: <one-line summary>"
3. STOP
If `git status` shows unstaged changes after the commit, you exceeded the step.
A clean commit log per step makes “did you stick to the plan?” verifiable.
How to confirm it’s fixed
git log --onelineshows one commit per approved step, no extras.git diff --stat <plan-start-commit> HEADlists only files named in the plan.- Every plan step’s diff touches only the files that step named — no edge-case sweeps, no “while I was here” refactors.
- If you used auto mode,
/permissions-> Recently denied is empty of off-plan edits you didn’t intend.
Prevention
- Plan steps name files + symbols + concrete operations; never “refactor for clarity”
- Fold the “do not exceed this plan” rule into the plan text, not just chat, so a context-clear on approval doesn’t drop it
- For the first run of a new plan, approve with review each edit manually, not auto mode
- Audit
CLAUDE.mdand prompt templates for standing scope-creep authorizations; remove them - Add
"If execution diverges from the approved plan, stop and re-enter plan mode"toCLAUDE.md - For directories that must never change, use a
denyrule — the only mode-proof guarantee - Commit per step so the audit trail is git-native
FAQ
Does approving a plan force Claude to follow the steps exactly? No. Approval exits plan mode and switches the session into an editing mode (auto / accept-edits / review-each-edit), then Claude starts working. It is not a binding contract. The steps are followed only as well as they’re written and constrained, which is why file-anchored steps plus a “do not exceed” rule matter.
My plan rules disappeared after I approved — where did they go?
If showClearContextOnPlanAccept is enabled, an approve option clears the planning context first, dropping anything you said in chat but didn’t write into the plan. Put constraints inside the plan text, or disable that setting in settings.json.
Claude skipped a step claiming the code “already does that.” How do I stop this?
Add to the execution instruction: “Do NOT skip steps; if a step looks unnecessary, STOP and ask.” Then verify with git diff per step — a skipped step shows up as a missing change against the plan’s stated file list. This silent-skip behavior is tracked in the Claude Code issues.
Can I make plan mode the default so I don’t forget to press Shift+Tab?
Yes. Set "permissions": { "defaultMode": "plan" } in .claude/settings.json, or start with claude --permission-mode plan. The session opens read-only until you approve a plan.
Auto mode keeps editing files I told it not to — why doesn’t it listen?
Auto mode’s classifier reads boundaries from the transcript, so a stated boundary holds only while that message survives. After compaction it can be lost. Re-state it, or better, add a deny rule in settings so the restriction is enforced regardless of mode or context state.
How do I edit the plan before Claude runs it?
While the plan is shown, press Ctrl+G to open it in your default editor, tighten the steps (add file paths, remove vague language), save, and then approve.