Summary
When diff_preview is enabled (via /config) and the session is in plan mode, approving ExitPlanMode does NOT auto-approve subsequent Edit/Write/Bash tool calls. The user must manually click Approve for every tool — defeating the plan mode UX of "review once, execute freely".
Evidence
Chat: Scout (-5243261989), staging v0.35.1rc2
Session: 14a9c041-bc51-4078-a2b5-237fd36d8037
Config: diff_preview: true (set via /config)
Log timeline (2026-04-13):
15:30:03 — control.9: ExitPlanMode (buttons=2, keyboard_rows=3 — Approve/Deny/Pause)
15:34:12 — callback.dispatch: user clicks Approve
15:34:13 — control_response.sent approved=True ← ExitPlanMode approved OK
15:34:34 — control.11: tool request (buttons=1, keyboard_rows=2 — Approve/Deny only)
15:35:57 — callback.dispatch: user clicks Approve
15:35:58 — control_response.sent approved=True
15:36:04 — control.12: another tool request
15:36:34 — user clicks Approve again
…repeats through control.15
The user perceives this as "can't exit plan mode" because they keep getting Approve/Deny buttons after they already approved the plan.
Root cause
claude.py line 553-556 — the diff_preview gate:
if (
run_opts
and run_opts.diff_preview is True
and tool_name in _DIFF_PREVIEW_TOOLS
):
# Falls through to manual approval — does NOT auto-approve
This gate applies unconditionally. After ExitPlanMode approval, Edit/Write/Bash tools still hit this gate and require manual approval.
Expected
After the user approves ExitPlanMode (the plan), subsequent tools should auto-approve even with diff_preview enabled. The user reviewed the plan and explicitly approved it — per-tool approval is redundant.
Fix
Add a _PLAN_EXIT_APPROVED: set[str] session-level registry. When ExitPlanMode is manually approved via write_control_response(), add the session_id. In the diff_preview gate, check this set and bypass the gate if the plan was already approved. Clean up in _cleanup_session_registries().
Affected files
src/untether/runners/claude.py
tests/test_claude_control.py (new test)
Summary
When
diff_previewis enabled (via/config) and the session is in plan mode, approving ExitPlanMode does NOT auto-approve subsequent Edit/Write/Bash tool calls. The user must manually click Approve for every tool — defeating the plan mode UX of "review once, execute freely".Evidence
Chat: Scout (
-5243261989), staging v0.35.1rc2Session:
14a9c041-bc51-4078-a2b5-237fd36d8037Config:
diff_preview: true(set via/config)Log timeline (2026-04-13):
The user perceives this as "can't exit plan mode" because they keep getting Approve/Deny buttons after they already approved the plan.
Root cause
claude.pyline 553-556 — the diff_preview gate:This gate applies unconditionally. After ExitPlanMode approval, Edit/Write/Bash tools still hit this gate and require manual approval.
Expected
After the user approves ExitPlanMode (the plan), subsequent tools should auto-approve even with
diff_previewenabled. The user reviewed the plan and explicitly approved it — per-tool approval is redundant.Fix
Add a
_PLAN_EXIT_APPROVED: set[str]session-level registry. When ExitPlanMode is manually approved viawrite_control_response(), add the session_id. In the diff_preview gate, check this set and bypass the gate if the plan was already approved. Clean up in_cleanup_session_registries().Affected files
src/untether/runners/claude.pytests/test_claude_control.py(new test)