If you think agent loops are just simple while-loops that execute whatever the model spits out, you’re living in a naive fantasy. The latest deep dive into Claude Code’s internals reveals a sophisticated permission layer that sits between the LLM’s intent and actual system execution. Published on DEV.to on October 8, 2026, the analysis of Claude Code v2.1.220 shows that a tool_use response is merely a request, not a command. The real magic happens in the pre-execution approval phase, where the runtime decides whether to let the robot touch your filesystem or not.

The Tool Menu and the Illusion of Autonomy

Every iteration of the loop sends a three-part payload: the system prompt, the conversation history, and crucially, the tools array. The model doesn’t magically know what it can do; it only knows what’s on the menu. If Read isn’t declared in the tools section, the model can’t call it. Each tool declaration includes a name, a description, and an input_schema. The description is the primary signal the model uses to decide relevance, while the JSON Schema ensures the parameters are structurally valid. But here’s the kicker: even if the model generates a perfectly valid tool_use block for Bash rm -rf /, the runtime doesn’t necessarily pull the trigger.

The Six-Layer Permission Firewall

Claude Code employs a hierarchical rule engine to determine if a tool call proceeds automatically, requires user approval, or is denied outright. The system evaluates six sources in strict priority order: aborted states (highest), explicit deny rules, ask rules, automatic classifiers, remembered user approvals (alwaysAllow), and finally, the default mode. For example, a denyRule might permanently block Bash(rm -rf *) across the organization, while an askRule forces confirmation for git push. This policy stack is configured via CLI arguments, session state, local project settings, shared project settings, user-wide settings, and managed organizational policies. Once a higher-priority rule makes a decision, lower-priority rules are ignored.

Racing for Resolution and the Promise Block

When no automatic rule applies, the loop blocks on a JavaScript Promise, waiting for a decision. This isn’t just a UI delay; it’s a cost-saving mechanism. No LLM API call is in flight while the user thinks, so deliberation time doesn’t burn tokens. The resolution comes from a race between three sources: the user interface, programmable hooks (via settings.json or HTTP endpoints), and an AI classifier that judges safety. The first valid response wins. To prevent race conditions, the runtime uses a ResolveOnce mechanism, ensuring the promise is settled exactly once. This concurrency model turns potential bugs into features, allowing hooks to approve safe operations in milliseconds while the user is still reading the prompt.

The Subagent Trust Boundary

One of the most critical security features is how subagents handle trust. If a user grants alwaysAllow Bash in the main conversation, that permission does not cascade to subagents. When a subagent launches, Claude Code clears the parent’s session-level approvals. Subagents start with a clean slate, relying only on launch-level CLI settings and their own allowedTools policy. This deliberate conservatism prevents a trusted main agent from inadvertently granting unchecked autonomy to a spawned context that might behave differently. It’s a strict trust boundary that prioritizes safety over convenience, ensuring that each autonomous context must earn its execution privileges independently.

Key Takeaways

  • Requests are not executions: A tool_use block is just a proposal; the runtime decides if it runs.
  • The menu defines reality: The model can only call tools explicitly declared in the tools array of the API request.
  • Six-layer policy stack: Permissions are resolved via a strict hierarchy from aborts and deny rules down to default modes.
  • Zero-cost deliberation: The loop blocks on a Promise during approval, meaning no API tokens are consumed while the user decides.
  • Race condition as feature: User input, hooks, and AI classifiers race to resolve approvals, with ResolveOnce ensuring integrity.
  • Subagents are untrusted: Session-level permissions do not inherit to subagents, enforcing a conservative security posture.

The Bottom Line

Claude Code proves that true autonomy in AI agents is a myth; what exists instead is a sophisticated negotiation between intent and permission. By treating tool execution as a gated event rather than a reflex, Anthropic has built a safety net that saves costs and prevents catastrophic errors. Developers must stop treating agent loops as black boxes and start respecting the complex policy engines that actually control the keys.