Headless mode ignores explicit permissions.allow rules for .claude/ subpaths — denied as "sensitive file" with no operator override

Resolved 💬 2 comments Opened Jun 10, 2026 by haabe Closed Jun 14, 2026

Feature request

An operator-declared override that lets headless runs (claude -p) write to specific .claude/ subpaths — either by honoring explicit permissions.allow rules for those subpaths (excluding settings files themselves), or via a dedicated setting/flag that whitelists .claude/ subtrees for non-interactive sessions.

Problem

In headless mode, every write under a project's .claude/ directory is denied as a "sensitive file" — even when the project's .claude/settings.json contains an explicit allow rule for that exact path. There is no operator-side way to override the denial, because headless mode has no permission prompt to answer.

Exact denial message:

Claude requested permissions to edit <path>/.claude/evals/probe.md which is a sensitive file.

The sensitive-file protection outranks permissions.allow rules unconditionally in non-interactive sessions. Verified 2026-06-11, twice, interventionally (a full headless agent run plus an isolated direct probe). The denial applies across tool surfaces: Write, Edit, Bash (cp, redirects), and filesystem MCP writes.

Reproduction

  1. Fresh directory with .claude/settings.json:
{
  "permissions": {
    "allow": [
      "Write(.claude/evals/**)"
    ]
  }
}
  1. Run:
claude -p 'Use the Write tool to create .claude/evals/probe.md with the content "probe"'
  1. The write is denied with the "sensitive file" message above. The same write succeeds in an interactive session (the user approves the prompt) — interactively the path is grantable, so the headless denial is not protecting something that is otherwise unreachable.

Version: Claude Code 2.1.172, macOS (darwin).

Why this matters / use case

Agent frameworks that extend Claude Code conventionally keep their project state under .claude/ — canvas/state YAML, run ledgers, memory files, eval logs. That convention works interactively but breaks completely under claude -p: a scheduled or CI-driven agent run that legitimately needs to append to its own run ledger or update framework state files cannot, even when the operator has explicitly pre-authorized exactly those paths in settings.

The current workaround is mirroring state to a non-canonical directory (e.g. <project>/mycelium-state/) and re-integrating in the next interactive session — which forks the source of truth and adds a reconciliation step that exists only because the allow rule is ignored.

Concrete example: Mycelium, a product-development harness for Claude Code, documents this exact mirror-and-reintegrate fallback because headless runs cannot touch their own canonical state.

Proposed mechanism (any of these would resolve it)

  1. Honor explicit allow rules for .claude/ subpaths in headless mode, with settings files (settings.json, settings.local.json) carved out as always-protected. An explicit Write(.claude/evals/**) rule is an unambiguous operator declaration; silently overriding it is surprising.
  2. A dedicated setting, e.g. permissions.headlessSensitivePathAllow: [".claude/evals/**", ".claude/canvas/**"], so the override is opt-in and visibly scoped, separate from the general allow list.
  3. A CLI flag for the launching operator, e.g. claude -p --allow-sensitive-path '.claude/evals/**', keeping the override per-invocation.

Option 1 or 2 (settings-based) is preferable for scheduled runs; the key property in all three is that the override is declared by the operator who controls the settings file, not requested by the model at runtime.

What this is not asking for

  • Not asking to weaken the default: with no explicit rule, .claude/ should stay protected in headless mode exactly as today.
  • Not asking for .claude/settings.json / settings.local.json themselves to ever be writable headless — those should remain excluded even from an override, since they define the permission surface itself.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗