auto-mode permission classifier denies allowlisted calls (Agent, Bash(python3 *)) with mismatched/stale reasoning

Open 💬 0 comments Opened Jul 13, 2026 by addadi

Summary

In defaultMode: "auto", the semantic permission classifier denied several legitimate tool calls with reasoning that did not match the actual call. Critically, it overrode a maximal allowlistAgent and Bash(python3 *) were both present in permissions.allow yet calls matching them were still denied. This suggests the auto-mode classifier runs as an independent content-based layer on top of the static allowlist and can override an already-maximal allow rule.

This breaks unattended / sonnet-class execution: any workflow requiring a reviewer subagent spawn (mandatory review gate) or a Bash sequence following a stash/destructive-adjacent action has a nonzero silent-stall risk.

Environment

  • Claude Code CLI, defaultMode: "auto"
  • ~/.claude/settings.json: permissions.allow with 282 entries incl. bare unscoped Agent; project .claude/settings.local.json incl. Agent and Bash(python3 *). No deny/ask lists.

Repro cases (false positives)

Case 1 — Agent spawn flagged as prompt injection.
An Agent-tool spawn of a reviewer subagent was denied; the classifier claimed the prompt "carries an injected context_window_protection payload instructing disregard of prior directives." That text is inherited session boilerplate from a plugin's system-reminder (present in every session), not authored by the calling agent. Agent was allowlisted with no argument restriction — denied anyway.

Case 2 — citing text as data treated as issuing an instruction.
A follow-up Agent spawn that merely quoted/described that same boilerplate (in order to report the bug) was denied as "smuggling the injection through a new path." The classifier could not distinguish citing text as data from issuing it as a live instruction.

Case 3 — stale/misattributed reasoning.
Two unrelated read-only Bash calls (running a python smoke test, matching Bash(python3 *)) were denied citing the justification for an earlier, different, already-resolved git stash drop action. Reasoning from one call was attached to a later unrelated call.

Key finding

The broadest possible allow rules for these exact call shapes (Agent, Bash(python3 *)) already existed and were insufficient — the auto-mode classifier is not gated by permissions.allow and can deny a call that a maximal static rule explicitly permits.

Correctly-blocked cases (NOT bugs — included for contrast)

  • A bd create proposing a "retry/reword past a denial" heuristic — correctly blocked (bypass-adjacent).
  • An Agent spawn whose prompt applied allowlist entries to settings.json — correctly blocked (self-modification).

We are explicitly not requesting any retry/reword-around-denial workaround; that pattern is itself a bypass vector. This is a reliability report on the false positives above.

View original on GitHub ↗