A session cannot determine its own effective permission mode — the harness stamps it on peer messages but never exposes it to the session itself, causing repeated confidently-wrong self-reports

Status Open
Reported on v2.1.227
Maintainer reply None cached
Activity 0 comments · opened Aug 11, 2026

Claude Code 2.1.227, macOS. Observed across 3 sessions on 2 machines in a single day.

Summary

Claude Code knows each session's effective permission mode. It displays it to the user in the status bar (Bypass Permissions On · shift+tab to cycle) and it stamps it on outbound cross-session messages as from-mode="bypass". It does not expose it to the model running in that session.

With no access to the real value, the model does the only thing available: reads permissions.defaultMode from settings.json and reports that. When the session was launched with --dangerously-skip-permissions, the CLI flag overrides the setting, and the model reports the opposite of the truth — confidently, because it has a plausible source.

Reproduction

  1. Set "permissions": { "defaultMode": "auto" } in ~/.claude/settings.json.
  2. Launch: claude --dangerously-skip-permissions. The status bar correctly shows Bypass Permissions On.
  3. Ask the session what permission mode it is in.

Actual: it reports prompting/auto, citing defaultMode from settings.
Expected: it reports bypass, matching the status bar and the from-mode stamp.

Evidence this is systemic, not incidental

This occurred 7 times in one day, in 3 different sessions, on 2 machines, always in the same direction — model says prompting, ps -o command= -p <pid> shows --dangerously-skip-permissions. In every case the reasoning was sound and the input was wrong.

Two of those sessions independently produced incorrect bug reports from the false premise, each claiming the documented cross-session permission-class default was broken:

"A peer just caused a write to your repo, with no prompt, in a session that is not in bypass mode."

Both retracted after running ps on their own pid. The behavior was correct; only the self-knowledge was wrong.

Why this is worth fixing rather than working around

1. It is a safety problem, and it fails in the dangerous direction.

One session appended this to a message describing what it would do on a peer's behalf:

"my session is not in bypass mode, so treat anything I'd need to do as subject to my own permission prompts."

That was false. The session did not prompt for anything. A reader relying on it would believe an action was gated when it would execute immediately. The error consistently understates what a session will do without asking, because defaultMode in settings is the more conservative value being overridden.

2. The harness already has the fact and already shares it — just not with the session that owns it.

In cross-session messaging, every inbound message carries from-mode describing the sender's class, and that stamp was correct in every case we measured. So a session can reliably learn a peer's permission mode while being unable to learn its own. That asymmetry seems unintended.

3. It defeats a documented safety behavior.

Permission-class matching gates cross-session delivery, and users are advised to reason about it. Neither participant can accurately state its own class, so the reasoning has to be done externally with ps, by a human.

Suggested fix

Expose the effective mode (post-flag-resolution, matching the status bar) to the model — as environment context, or in the same channel that already carries from-mode. Note claude agents --json also omits it, though the registry at ~/.claude/sessions/<pid>.json carries version, name, and bridgeSessionId; permission mode would be a natural addition there too.

Today the only reliable check is external:

ps -o command= -p <pid>   # look for --dangerously-skip-permissions

Requiring a human to shell out to ps to learn something the UI is already displaying is the gap.

Related: #85678, #85679, #85690 — all four share a root shape: the harness has state the participants need and does not surface it.

View original on GitHub ↗