Coding agents normally put a human between repository-authored content and execution: opening a folder in an interactive session triggers a workspace trust dialog, and each MCP server must be approved individually. In headless / non-interactive mode (-p, print mode) no dialog can be shown, so the trust gate does not merely get bypassed — it has nowhere to live. The result is that hooks, MCP servers, project instruction files, skills, and plugins authored inside an untrusted repository are discovered and can execute with no human approval.
Claude Code's own documentation discloses this hazard and ships a containment flag, --bare, that skips auto-discovery entirely. The established handling is not to prompt anyway (impossible) but to make a named, explicit decision about what executes untrusted, contained by a single switch. The same failure shape appears in other agent CLIs (e.g. a project-instruction-file loader gated only by --safe-mode/--restricted, with no trust check at all, running in both headless and REPL modes).
Key Points
The trust gate is human-in-the-loop, not a technical control. Its purpose is to interpose a person between repo-authored content and execution. Remove the interactive session and the control has no place to run — prompting is not an option, so "prompt anyway" is not a fix. Headless mode has no human to prompt, so the gate is not merely bypassed; it has nowhere to live.
Claude Code's disclosure: "Without --bare, Claude Code runs the hooks in a project's .claude/settings.json even in a folder you have never trusted, because a -p session shows no workspace trust dialog. It also connects the servers in the project's .mcp.json, because a -p session can't show the per-server approval prompt either."
What leaks into a headless session: hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md / project instruction files.
Containment shipped:--bare skips auto-discovery of all of the above. It is recommended for CI/scripts and is stated to become the default for in a future release.
-p
Correct design principle: not an impossible prompt, but a named, explicit decision about what is allowed to run untrusted — an explicit opt-in/opt-out check the user can disable in a single move, rather than silent execution.
Parallel case (issue #902, "the seventh trust door"): project instruction files are read into every prompt and no gate sees them. Verified at the author's head (2026-09-23) at src/cli.rs:2659: instruction files are loaded into the system prompt (via load_project_context) gated only by --safe-mode/--restricted, with no trust check — and that code path runs for -p as well as the REPL, so the door is open in headless mode exactly as in Claude Code.
Concepts
Workspace trust dialog — interactive prompt that gives the user authority over whether a folder's configuration may execute; an interactive confirmation that a folder's project-defined content may execute.
Headless / print mode (-p) — non-interactive session where trust and per-server approval prompts cannot be displayed or rendered.
Auto-discovery — automatic loading of repo-scoped configuration: .claude/settings.json (hooks), .mcp.json (MCP servers), skills, plugins, auto memory, CLAUDE.md / instruction files.
.claude/settings.json hooks — project-defined commands that execute automatically.
.mcp.json MCP servers — project-declared servers connected at session start.
Containment flag (--bare) — opt-out that disables auto-discovery; explicit configuration only; the intended default for -p going forward.
Safe/restricted mode (--safe-mode / --restricted) — existing gate that governs instruction-file loading but is not a trust check; the only gate observed around project instruction loading in the sibling case.
load_project_context — the code path injecting project instructions into the system prompt.
Named explicit decision — the design alternative to an impossible prompt: an auditable, one-switch choice about untrusted execution.
Details
The mechanism of the bypass
In an interactive session, opening an untrusted repository raises a trust dialog before project-scoped configuration is honored, and each MCP server is approved individually. A -p session has no surface for either prompt, so the agent proceeds to:
Read the project's .claude/settings.json and run its hooks.
Read the project's .mcp.json and connect its servers.
Auto-discover skills, plugins, and auto memory.
Load CLAUDE.md and other project instruction files into the prompt.
Each of these is repository-authored content that reaches execution (or the model context) without any human gate. The documentation frames this per kind of repository content, describing "what runs before you trust a folder under -p, and how to keep it out."
Auto-discovery treats repository content as trusted configuration. In an interactive session, a dialog interrupts that assumption. Under -p, the dialog is structurally absent, so the same auto-discovery proceeds silently — hooks execute and MCP servers connect in a folder the user never approved. The documented per-content guidance exists precisely because the trust decision has moved from a prompt to configuration.
Omitting --bare therefore means trusting the repository by default. Because -p is the mode used by CI and scripted automation, this default exposes exactly the environments least able to inspect or interrupt what runs.
Rendering diagram…
Containment
--bare is the shipped mitigation: it skips auto-discovery of hooks, skills, plugins, MCP servers, auto memory, and CLAUDE.md. It is recommended for CI and scripts, and is slated to become the default for -p in a future release — i.e. the safe behavior is being promoted from opt-in to default.
Rendering diagram…
Why "prompt anyway" is the wrong fix
Because headless mode is precisely the mode in which prompting is impossible, restoring the gate as a prompt is not a design. The proposed design is instead a named, explicit decision about what is permitted to run untrusted, which the user can disable in one move — mirroring what --bare does by making the untrusted-execution posture a deliberate, visible switch rather than an accident of the mode.
Two responses follow when no human can be prompted, so that the gate does not simply fail open and has no place to stand:
Prompt anyway is impossible in headless mode and is not a design.
A named, explicit decision about what runs untrusted is the correct design — one that a user can turn off in a single move. --bare is that switch: it names the policy, defines the blast radius, and inverts the unsafe default.
The generalized / parallel case
The same hazard recurs in other agents' implementations. Issue #902 ("the seventh trust door") identifies project instruction files as a channel that is read into every prompt with no gate observing it. Investigation at the author's head (2026-09-23) confirmed src/cli.rs:2659 loads project instruction files into the system prompt with the only condition being --safe-mode/--restricted; there is no trust check on that path, and the path executes for -p as well as the REPL. The trust door is thus open in headless mode there too.
The same failure shape recurs wherever project-authored text reaches the model or the shell without a gate. In the sibling codebase, project instruction files are loaded into the system prompt with no trust check, gated only by --safe-mode/--restricted, and that path runs in headless mode too. The conclusion generalizes: any auto-discovered, repo-authored input — instructions, hooks, server definitions, memory — needs one explicit, nameable containment switch in headless runs.
Sources
Claude Code headless documentation: https://code.claude.com/docs/en/headless