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'

Offen
#93,207 0 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area:permissions bug platform:ios
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

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.