The Layered System Behind the Agent

Claude Code’s behavior does not come from one undifferentiated “AI layer.” This chapter examines how the product’s work surface, extensions, project rules, built-in components, and managed settings fit together. The practical goal is diagnostic: when Claude Code behaves in a surprising way, a reader should be able to ask which layer supplied the capability, which layer owns the decision, and which layer can still override or constrain it.

Four layers, four kinds of authority

The first layer is the user-facing work surface. Claude Code receives a natural-language request, works with a repository and Git state, and can use tools to inspect or change that environment. This is the layer people experience directly: an explanation appears, a file changes, a command runs, or a diff becomes available. It is tempting to treat everything that happens after the request as “the model,” but that collapses several different responsibilities into one label. The work surface is where intent becomes an attempted operation; it is not necessarily where every policy decision is made.

The second layer adds reusable capability. The repository defines plugins as extensions that can supply slash commands, specialized agents, hooks, skills, and MCP servers. An agent is a focused reasoning role, such as an explorer that traces a feature through a codebase. A hook is event-driven: it can react when a prompt arrives, a tool is used, a change is written, or a session tries to stop. MCP connections extend what the system can reach outside the core work surface. These are not merely different names for the same feature. They differ in when they run, what context they receive, how they are distributed, and what kind of state they can influence.

The third layer is local or project policy. A repository may carry instructions that shape how work should be performed, and a project can define rules that warn about or block particular commands and file changes. Hookify is a clear example: its markdown rules live in the project’s .claude directory and can match Bash, file, prompt, or stop events. A rule can show a warning or block a matched operation. That makes it useful for team conventions and local hazards, but its authority is still tied to the event and pattern it can observe. A project-local rule is not the same thing as a centrally managed policy, even when both can display a warning.

The fourth layer belongs to the organization or deployment environment. Managed settings can restrict which hooks, MCP servers, marketplaces, permission rules, plugins, skills, agents, and remote features are available. They can require approval for Bash, deny web tools, or require a sandbox. Gateway examples add another organizational boundary by putting identity, provider routing, secrets, and network controls around the model path. These controls do not simply add more functionality. They decide which local choices remain available at all.

This layered view is an interpretation assembled from the repository’s artifacts, not a published architecture diagram that exposes every internal component. That distinction matters. The evidence supports the relationships among the layers and the documented behavior of particular extensions or settings. It does not support an invented account of the proprietary engine’s internal call graph. A useful system model can be precise about boundaries without pretending to see through them.

classDiagram class ClaudeCode class Plugin class Agent class Hook class MCP class BuiltInMod class ProjectPolicy class ManagedPolicy class ModelEndpoint ClaudeCode --> Plugin : loads Plugin --> Agent : supplies Plugin --> Hook : registers Plugin --> MCP : configures ClaudeCode --> BuiltInMod : ships ProjectPolicy --> ClaudeCode : shapes behavior ManagedPolicy --> ClaudeCode : constrains choices ClaudeCode --> ModelEndpoint : requests BuiltInMod --> ManagedPolicy : protects precedence

The diagram is less a class hierarchy than an ownership map. Claude Code loads or hosts several kinds of extension. Project policy shapes behavior from the local environment. Managed policy constrains what the local environment may add or change. Model access is another relationship, not a synonym for the agent itself. The important question is not whether every arrow is implemented as a literal object reference; it is what crosses each boundary. A plugin supplies behavior. A hook waits for an event. A policy supplies a constraint. A model endpoint supplies a place where inference may occur.

A boundary is useful only if it answers three practical questions. What information crosses it? What decision crosses it? What state persists after the crossing? A plugin may bring a command or an agent into the session; a hook may add a message or block a tool event; an MCP server may make an external system reachable. A managed setting may prevent one of those components from loading or may restrict the permission choices available to it. These are different forms of influence. Treating them as equivalent makes it impossible to explain why a local change worked in one project but not another.

Ownership also predicts how a change is maintained. A project team can revise a local rule when its repository conventions change. A plugin maintainer can publish a new command or agent, but the project still decides whether to install it. A built-in mod changes with the product release and may affect every session that loads it. An organization owner controls managed settings and must communicate the developer experience those settings remove. The work surface is where these choices meet; it is not the owner of all of them. When a behavior needs to be changed, the correct fix is often to find its owner rather than to add another instruction to the model.

Consider a routine feature request. The feature-development plugin may add a structured command that asks questions, launches exploration agents, and waits for approval before implementation. A project-local Hookify rule may block a destructive Bash pattern while allowing ordinary edits. A strict managed setting may deny web tools or prevent an unapproved marketplace from supplying another extension. The request still looks like one conversation, but its authority is distributed: the workflow shapes how the task is reasoned about, the project rule reacts to a matched event, and the organization decides which capabilities can exist in the first place. If the request fails, “Claude did not follow the prompt” is an inadequate diagnosis. The failure may be a missing extension, a rule that did not match the event, a managed restriction, or a boundary outside the configured sandbox.

This model also explains why visibility and authority can diverge. A plugin may be able to explain its own findings without being able to change a managed permission. A project rule may know the text being written but not the network request made by another component. A policy may block a tool without understanding the user’s intent. The model endpoint may process content that a local operator thought of as “inside the terminal.” The person operating Claude Code needs both views: the visible conversation and the less visible paths by which context, decisions, and data move.

Why composition order is a security fact

If all extensions were equally trusted and all policies were equally authoritative, the layered model would be mostly a taxonomy. Claude Code’s built-in mods show why it is more than that. The mods documentation describes sec-default as keeping an organization’s classic hooks, prompt content, managed settings, tool policy, permission rules, and pinned environment out of reach of plugins a person installs. It adds no policy of its own; its role is to preserve an existing authority boundary while other components are composed around it.

That is a different responsibility from a user-installed security plugin. A security plugin can warn about a dangerous edit or ask a model to review a diff. A protected built-in component can determine whether the plugin is allowed to change the policy context in which that review operates. One mechanism analyzes work; the other protects the conditions under which analysis and work take place. Confusing those roles leads to a common mistake: assuming that installing more security-related behavior automatically prevents an extension from weakening the surrounding policy.

The same distinction appears in the other built-in mods. The diff mod provides a built-in view of uncommitted changes and refreshes it as work proceeds. The telemetry mod adds first-party event handling and refuses installed plugins, while sending nothing when Claude Code analytics are disabled. The agents-md mod changes how AGENTS.md can be loaded in relation to CLAUDE.md. Each mod changes the engine’s behavior, but they do so from a privileged, shipped position rather than from the same marketplace path as a project plugin.

This difference in position creates a lifecycle distinction. A shared plugin is something a project or team chooses to install and update. A project-local rule is something close to the repository and its current working practices. A built-in mod ships with the product and participates in the engine’s own event composition. A managed policy is an organizational decision that may intentionally prevent the first two categories from changing certain behavior. The repository does not say that one category is universally better. It shows that the categories have different owners and therefore different failure and maintenance responsibilities.

The lifecycle distinction becomes important during incident response. If a dangerous command is allowed, an operator needs to know whether the rule was absent, disabled, attached to the wrong event, or superseded by a higher-level setting. If a new plugin changes a workflow, the team must ask whether the change came from the plugin update, the project’s configuration, or a product release that altered built-in composition. If a developer cannot use an expected tool, the cause may be a deliberate managed restriction rather than a broken installation. Naming the owner and trigger turns a vague symptom into a testable hypothesis.

The built-in mod test model makes the interface problem visible. Tests receive the engine’s own $ interface and a plugin’s event hooks; a hook that does not receive an answer can fail with the event it expected. The documentation also describes “noun contracts”: when a mod adds a noun such as telemetry to the engine, its types live in one declaration contract that callers and tests share. This is a practical response to hidden coupling. If one extension calls another extension’s interface, copied assumptions can drift. A shared contract turns some of that drift into a type-checking failure instead of a runtime surprise.

But a contract is not the same as permanence. The repository labels the mod API early access and warns that the API may change without notice. The contract reduces one class of incompatibility while leaving release evolution, composition order, and operational behavior as continuing concerns. A team that builds on an internal extension point has to own the cost of tracking that surface. The right architectural conclusion is therefore conditional: built-in composition can provide stronger placement and integration than a project plugin, but it can also expose a team to a less stable interface.

That conditional judgment is easy to lose when technical diagrams make composition look static. In practice, the same named component can have a different effect after an update, a policy change, or a change in the event it receives. Tests and shared type contracts help detect drift, but they do not answer whether a policy is still appropriate or whether a new data path has become acceptable. Operational ownership must include release review and boundary testing, not only compilation. The question is not merely whether an extension still loads; it is whether the resulting system still enforces the authority and visibility that the team intended.

The managed-settings examples add another kind of precedence. A strict configuration can require managed hooks and managed permission rules, block marketplaces, and deny web tools. A sandbox configuration can require Bash to run inside a sandbox and disallow an unsandboxed fallback. These settings can make local customization narrower by design. They also have sharp scope limits: the repository warns that a Bash sandbox does not automatically apply to Read, Write, WebSearch, WebFetch, MCP servers, hooks, or internal commands. “The system is sandboxed” is therefore too vague to be useful. The correct statement must name the tool, the boundary, and the allowed path.

There is a corresponding usability cost. A managed-only configuration can stop a developer from loading a useful personal skill or adding a project-specific permission rule. An empty allowlist can block a marketplace or MCP server until an owner has reviewed it. That friction is not evidence that the policy is defective; it is the intended consequence of moving authority upward. But the organization must own the trade-off. A boundary that is so difficult to use that people seek untracked workarounds may produce a different operational risk than the one it was designed to reduce.

This is also why a surprising behavior should be traced from the outside in. First ask whether Claude Code itself supplies the capability or whether a plugin, agent, hook, or MCP connection adds it. Then ask whether a project rule changes the event’s outcome. Next ask whether managed settings restrict, replace, or preserve that local choice. Finally ask what built-in component or model endpoint participates in the path. The answer may involve several layers at once, but the diagnostic method prevents a user from blaming “the model” for a decision made by a hook or assuming that a local rule can override an organizational policy.

A team can use the same method before deployment, not only after an incident. For a proposed extension, identify the files or services it can read, the events that invoke it, the credentials or network paths it may use, and the policy that can disable it. For a new managed setting, identify the developer workflow it removes and the bypasses or exceptions that remain. For a built-in capability, identify what changes when Claude Code itself is upgraded. This is a small amount of architectural bookkeeping, but it makes hidden coupling visible before the system is relied on for work with higher consequences.

The architecture’s central lesson is not that more layers automatically produce more safety. Layers create leverage when their ownership and interfaces are explicit. They create confusion when a warning is mistaken for a block, a project convention for a managed boundary, or an early-access mod for a stable public API. Claude Code becomes more governable when the person operating it can identify not only what happened, but also which layer had the authority to make it happen. That static map is the foundation for the next question: how does a request move through those layers over time?

References