anthropics / anthropics/claude-code
Approving a plan from the iOS app sends setMode 'auto', discarding the session's prePlanMode; the rejection path then falls back to 'default'
- Vorherrschende Sprache
- Python
- Sterne
- 145k
- Forks
- 23.1k
- PR-Merge-Kennzahlen
- PR-Kennzahlen ausstehend
Beschreibung
## Summary
Approving an `ExitPlanMode` request from the iOS app sends `updatedPermissions: [{type: "setMode", mode: "auto"}]` regardless of the mode the session was in before it entered plan mode. This discards the `prePlanMode` the CLI had already recorded and restores correctly on its own.
There are two separate defects here, and the second one bites even when the first is refused:
1. The iOS plan-approval button unconditionally requests `auto`, rather than the session's `prePlanMode`.
2. When that request is rejected, the bridge's fallback is a hardcoded `default` — not the `prePlanMode` sitting in the same context.
So a `bypassPermissions` session approved from iOS lands in `auto` on a host without `disableAutoMode`, and in `default` (manual approval) on a host with it. Neither is the mode the user was in.
The CLI and the Android app both get this right, which is what isolates it to the iOS approval surface.
## Environment
- Claude Code CLI 2.1.267, macOS 15 (Darwin 25.6.0), terminal-hosted session driven from the Claude iOS app
- `~/.claude/settings.json` (abridged):
```json
{
"permissions": {
"defaultMode": "bypassPermissions",
"disableAutoMode": "disable"
}
}
```
## Steps to reproduce
1. Start a local session that is in `bypassPermissions`.
2. Have it enter plan mode and then call `ExitPlanMode`.
3. Approve the plan **from the iOS app**, using the primary button — which is labelled "Exit and auto mode".
## Actual
The session leaves plan mode in `default`, i.e. manual approval, prompting for every tool call.
Debug log from the reproduction (`claude --debug`), trimmed to the relevant lines:
```
21:59:00.603 [INFO] [prepareContextForPlanMode] plain plan entry, prePlanMode=bypassPermissions
21:59:00.603 [DEBUG] Applying permission update: Setting mode to 'plan'
21:59:13.172 [DEBUG] executePermissionRequestHooks called for tool: ExitPlanMode
21:59:22.660 [DEBUG] bridge setMode 'auto' rejected (Cannot set permission mode to auto: auto mode disabled by settings); falling back to 'default'
21:59:22.682 [INFO] [Stall] tool_dispatch_start tool=ExitPlanMode permissionDecisionMs=9512
```
The CLI recorded `prePlanMode=bypassPermissions` on entry and would have restored it — `ExitPlanModeV2Tool` sets `mode: prePlanMode` on exit. The bridge's `setMode` arrives first and overrides that, and its rejection fallback is a literal `'default'` rather than the `prePlanMode` that is right there in the context.
On a host **without** `disableAutoMode` the `setMode 'auto'` is accepted instead, so the session silently lands in auto mode. Same defect, different landing spot.
## Contrast: the other approval surfaces are correct
Same CLI build, same settings, same plan, three approval surfaces:
| Approving from | Sends | Session lands in |
|---|---|---|
| CLI TUI | nothing — the dialog offers "Yes, and bypass permissions" first | `bypassPermissions` ✅ |
| Android app | nothing — its button is a plain "Approve plan" | `bypassPermissions` ✅ |
| iOS app | `setMode: "auto"` | `default` (or `auto` without the kill switch) ❌ |
The Android and CLI runs produce no `bridge setMode` line at all, and the CLI run logs `Applying permission update: Setting mode to 'bypassPermissions'`.
## Expected
The plan-approval path should restore the session's `prePlanMode`, as the CLI already does unaided.
Concretely, either of:
- iOS sends no `setMode` on a plain approval, matching Android, and offers the mode choice only as an explicit opt-in the way the CLI dialog does; or
- iOS requests `prePlanMode` rather than a hardcoded `auto`.
Independently of which, the bridge's rejection fallback should be `prePlanMode` rather than `default` — falling back to a mode the user never selected, and never had, is surprising in its own right.
## Impact
On iOS there is no permission-mode indicator and no dropdown, so the mode change is both invisible and unrecoverable from the phone. On desktop the dropdown at least lets you pick something else, though bypass is not among the options (see #79990).
For anyone who works in `bypassPermissions` as their default, approving a plan from the phone silently converts the session into one that prompts for everything, with no signal that it happened.
## Related
- #79990 — mobile/web mode dropdown can knock a session out of `bypassPermissions` with no way back. Same end state, but that is a manual dropdown tap; this one needs no tap on any mode control, just approving a plan.
- #80812 — Remote Control offers Auto on a host with `disableAutoMode`. Related root cause: the app's mode handling does not consult the host's gate.
---
*Written by Claude Opus 5 (1M context)*
Beitragsleitfaden
Für dieses Repository ist kein Beitragsleitfaden indexiert
Rechercherichtung
Start by locating the iOS plan-approval path and the bridge setMode handling described in the issue, then compare them with the Android approval path and ExitPlanModeV2Tool. Verify how prepareContextForPlanMode records prePlanMode and how rejected mode updates fall back. Done means plain iOS approval preserves prePlanMode, including when auto mode is disabled, with coverage for both approval and rejection paths.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- ios
- Bereich
- backend, mobile
- Issue-Typ
- Bug
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Aktivitätsstatus
- Aktiv
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 48/100