Skip to content

[Bug]: Claude Auto + Plan silently approves native plan-mode write requests (V2) #15503

Description

@rabesss

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Related but not the same:

Area

apps/server

Steps to reproduce

  1. In a scratch directory, create calc.py and make sure there's no probe.txt:
    def average(xs):
        """Return the arithmetic mean of a non-empty list."""
        total = 0
        for i in range(1, len(xs)):
            total += xs[i]
        return total / len(xs)
  2. From a V2 thread, call delegate_task with the Claude provider, model: "claude-sonnet-5-5", runtimeMode: "auto", interactionMode: "plan".
  3. Task: read calc.py, fix the off-by-one with the Edit tool, then run echo probe > probe.txt. Attempt each step even in plan mode, so the test shows whether the harness blocks the writes.

Expected behavior

While Plan remains selected, T3 should not silently authorize the write requests Claude routes through its plan-mode permission callback. If Auto + Plan intentionally permits implementation writes, that exception should be explained in the Plan UI and documentation.

Actual behavior

Both writes ran with no prompt:

  • The CLI's system/init message reported "permissionMode":"plan".
  • Edit returned "The file … has been updated successfully".
  • echo probe > probe.txt returned "(Bash completed with no output)" and created the file.
  • The final result had "permission_denials":[].

A Full access + Plan run started about 3 seconds later attempted neither write. The model chose to respect plan mode, so that run didn't test enforcement.

Where it seems to come from. Anthropic documents that native Plan mode routes file edits and shell writes to canUseTool, rather than denying them before the callback (Agent SDK permissions, Plan mode):

File edits are never auto-approved in plan mode, even when an allow rule matches. They prompt through your canUseTool callback instead. On Claude Code v2.1.212 or later, shell commands that modify files, such as touch and rm, reach your canUseTool callback the same way.

T3 selects permissionMode: "plan", but its Auto no-approval branch returns allow, which authorizes these requests. This explains the observed writes. ClaudeAdapterV2 always passes canUseTool (the query-open log shows hasCanUseTool: true). After the ExitPlanMode capture, the if (!requiresClaudeApproval(context)) branch returns allow without checking for plan mode. requiresClaudeApproval depends only on the access level and approval policy.

Impact

Major degradation or frequent failure

Version or commit

0.0.46-nightly.20261004.2644. The branch above is unchanged on main at ee7b49d.

Environment

Linux desktop app, Claude Code 2.1.289, claude-sonnet-5-5, stock Claude provider instance (no launch arguments)

Notes

  • I tested through delegate_task. A composer thread with Auto + Plan should take the same adapter path, but I haven't checked it in the UI.
  • There are no allow rules in user, project or local Claude settings.

Contribution

Once the intended Auto + Plan policy is confirmed, I'd like to contribute a focused fix with callback-level regression tests, preserving read-only investigation and plan capture.

Activity

  1. juliusmarminge commented on Oct 4, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the detailed report, @rabesss. I could confirm this on current main (c5a0c78), and it isn't a duplicate of #3744, #13786, or #15224.

    What I found

    • Plan is applied before the runtime mode. permissionModeForClaudeRuntimePolicy returns Claude's plan mode whenever interactionMode is plan, so Auto doesn't run as Claude auto and Full access doesn't run as bypassPermissions.
    • canUseTool is still attached on every query, but requiresClaudeApproval is only true for Supervised and Auto-accept edits (or an explicit approval policy other than never). For every other tool, once ExitPlanMode is captured, the callback just returns allow:
    // apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
    if (!requiresClaudeApproval(context)) {
      return {
        behavior: "allow",
        updatedInput: toolInput,
        toolUseID: callbackOptions.toolUseID,
      } satisfies PermissionResult;
    }
    • That matches the writes you saw. Claude's plan mode never auto-approves file edits, and on Claude Code 2.1.212+ it also routes file-modifying shell commands to canUseTool so an allow rule can't approve them. Answering allow there authorizes them with no prompt and no auto review. Read-only tools Claude approves before the callback aren't affected, and ExitPlanMode is still denied and stored as a proposed plan.
    • Full access goes through the same allow branch. Your Full access run didn't exercise it because the model never attempted the writes.
    • A composer thread with Auto + Plan takes the same path: RuntimePolicyV2 passes the thread's runtime and interaction modes through, and delegate_task stores them on the child thread.

    Likely fix area

    • ClaudeAdapterV2's canUseTool callback. One option is to treat edit and shell-write callback requests in Auto + Plan and Full access + Plan the way Supervised and Auto-accept edits already do, raising an approval request instead of allowing outright, while leaving read-only investigation and plan capture as they are.
    • The existing Auto-accept edits callback test looks like a good pattern for a regression: an Edit and a file-modifying Bash emitting a runtime request before any decision.

    A maintainer will decide on the fix direction.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions