← Hardik GoelWriting

Claude Code Is 1.6% Intelligence and 98.4% Plumbing

May 23, 2026 · by Hardik Goel

Subscribe now

On March 31, 2026, a security researcher named Chaofan Shou posted a few screenshots on X.

Anthropic had accidentally shipped Claude Code’s entire proprietary source code inside the npm package. Not the plugin shell they’d open-sourced (279 files). The full internal engine: 4,600+ files, ~512K lines of TypeScript, sitting there in a public package for anyone to run npm install and start reading.

The discourse that followed mostly focused on the leak itself. The drama, the accident, the “wow they really didn’t notice.”

That’s the boring part.

The interesting part is what was inside. Because what the source code revealed wasn’t a sophisticated AI reasoning system. It was an aggressively engineered context management infrastructure that happens to route through a language model at the center.

Here’s the number that should stop you: 98.4% of Claude Code’s codebase is deterministic infrastructure. Permission gates, context managers, compaction logic, tool routers, error recovery cascades. The agent loop itself, the part that “talks to the AI,” is a simple while-loop. The intelligence is 1.6% of the codebase.

And understanding that gap is the most useful thing you can do before building your own agentic system.

If you want to understand how Boris (the creator of claude code) works with claude, read this.


How Claude Actually Sees the World

Before the architecture, the mental model.

Most developers interact with Claude as if it’s a persistent entity that remembers things. It isn’t. Every API call is a fresh context assembly. The model wakes up, reads everything you’ve given it, responds, and immediately forgets all of it the moment the call ends. What feels like memory is actually very careful staging of what gets loaded back into the context window before the next call.

The structure upon reverse-engineering looks like this:

[0] System Prompt      ← static instructions, always present
[1] User Memories      ← stored facts about you, injected as XML
[2] Conversation History ← rolling window of current session
[3] Current Message    ← what you just sent

That’s it. Every response you get from Claude is the output of a model that just read these four layers in sequence and generated the next token. There’s no background process keeping track of you. No persistent reasoning thread. Just four layers of text, assembled fresh, every single time.

Plain XML. Distilled facts. No embeddings, no vector similarity search. The model reads it the same way it reads any other text: sequentially, from the top.

Here’s what’s worth sitting with: this is not how most developers imagine AI memory works. The mental model most people have involves something closer to a database: store a fact, retrieve it later, it’s just there. The actual architecture is more like writing everything on a whiteboard before every meeting, then erasing it when you leave. The meeting goes well because the whiteboard is good. The continuity is entirely in the preparation, not in the meeting itself.

This changes how you think about building on top of these systems. The question isn’t “will the AI remember this?” The question is “have I designed the preparation layer well enough that the AI will have what it needs?”


The Three-Layer Memory Architecture (and Where Each Layer Breaks)

When the Claude Code source leaked, what became immediately clear was that “memory” in an agentic context isn’t a single system. It’s three separate systems operating at different timescales, with different volatility and different failure modes.

Subscribe now

Layer 1: In-Context Memory

This is whatever is loaded into the active context window right now: the current conversation, recent tool outputs, the file being edited. It’s the fastest layer and the most obviously temporary. When the session ends, it vanishes. Even within a session, as context fills up, older in-context information gets compressed or evicted.

Claude Code treats this as a working scratchpad. Active reasoning happens here. But nothing important lives here permanently.

Layer 2: External File Memory (The memory.md Layer)

This is the architectural insight the leak made visible. Claude Code doesn’t try to squeeze everything into the context window. It writes structured information to files on disk, and memory.md acts as a pointer index to all of them.

The design choice that makes this elegant: memory.md never stores actual information. Only pointers. It’s a directory, not a diary.

When the agent needs to recall something, it reads memory.md first, identifies the relevant domain file, then loads just that file. A coding session focused on authentication doesn’t load the database schema memory. A session debugging the API doesn’t pull in the UI conventions. The loaded context stays minimal and relevant.

The “self-healing” aspect: the agent has explicit write tools. If a previous assumption was wrong, if a decision changed, if a prior entry became outdated, Claude Code rewrites the relevant file and updates the pointer. The human doesn’t have to maintain it. The agent does.

Here’s where I’d add a layer the original analysis understated: the self-healing mechanism is only as good as the agent’s ability to notice when something is wrong. Subtle drift, where a memory file is not exactly wrong but slowly diverges from reality, can persist undetected for a long time. The mechanism catches explicit contradictions reasonably well. It doesn’t catch gradual obsolescence. That’s the gap you’d need to engineer around in any production deployment.

Layer 3: Structural Project Memory (CLAUDE.md)

CLAUDE.md is qualitatively different from the other two layers. It’s not dynamic session memory. It’s the stable constitution of how Claude should behave in this specific project.

CLAUDE.md is reloaded on every single turn. Not once at the start of a session. Every turn. This makes it categorically different from a “project readme” or a “config file.” It’s more like an immutable law that applies to every decision the agent makes, re-read before every action.

The implication is significant: CLAUDE.md content has a direct cost. Every rule you put in it is re-injected into the context window, re-processed, and re-priced on every API call, for the entire life of the session. Lean CLAUDE.md files aren’t just a preference. They’re an economic constraint. Every line of CLAUDE.md is a line you’re paying for on every turn, indefinitely.

Most teams treat CLAUDE.md like a documentation file. It should be treated more like a critical path in a hot database query: every byte matters.


The Agent Loop: A While-Loop With Very Good Plumbing

Here’s the part that most developers find deflating when they first encounter it.

The agent loop, the actual core of Claude Code’s agentic behavior, is this:

while (model_wants_to_call_tools):
    response = call_model(context)
    if response.has_tool_calls:
        results = execute_tools(response.tool_calls)
        context.append(results)
    else:
        return response

That’s the intelligence. A while-loop that runs until the model stops requesting tools.

No separate planning engine. No complex reasoning graph. No multi-step orchestration algorithm. Just: call the model, run whatever tools it asked for, feed the results back in, repeat.

The “agentic” behavior emerges entirely from two things: the model’s trained ability to decide when to call tools and when to stop, and the accumulated context of everything that’s happened so far. The agent is the model. The model is the agent. The loop just keeps the conversation going until the task is done.

Here’s what the source code revealed about what actually does live in the 98.4%:

The turn pipeline has 9 distinct stages before an API call is even made. Context cleanup, tool budget enforcement, permission checking, cache optimization, conversation history compaction, error injection for testing, model selection, streaming fallback handling, and response validation. Every single turn.

The permission system has 7 modes operating as a deny-first policy. The default assumption is that a tool call is not permitted. Approval has to be explicitly granted. In auto-mode, a separate fast classifier LLM call runs with a two-stage filter (fast check, then chain-of-thought for ambiguous cases) to decide whether a command is safe to execute automatically. A tool that can modify arbitrary files requires a different trust level than one that reads a log.

Errors don’t crash the loop. They feed back as context. If a tool call is denied, the result is a ToolResult containing the denial reason, not an exception. The model reads the denial the same way it reads a successful result, then decides what to do next. This is a subtle but critical design choice: the agent loop is resilient by construction, not by exception handling. Error recovery is intelligence, not infrastructure.

The practical implication for builders: if you’re building agentic systems and your architecture collapses when a tool fails, you’ve designed the errors as infrastructure problems instead of intelligence problems. Feed them back into the model. Let the model recover.

Subscribe now


Prompt Caching: The Real Performance Engineering

Anthropic published a blog post about this directly, and the title says everything: “Prompt caching is everything.”

Not “prompt caching is important” or “prompt caching helps.” Everything.

Claude Code’s entire harness is structured around maximizing prompt cache hit rates. They run alerts when the hit rate drops. They declare SEVs if it gets too low. This is treated with the same operational seriousness as uptime.

The reason is economic and latency-based. Prompt caching works by prefix matching: the API caches everything from the start of the request up to each cache_control breakpoint, byte for byte. On subsequent requests, if the prefix matches, that computation is reused instead of recalculated. Lower cost, lower latency, more generous rate limits.

The ordering rule becomes obvious once you understand how it works:

[1] Static system prompt + tool definitions    ← globally cached
[2] CLAUDE.md                                   ← cached within a project
[3] Session context                             ← cached within a session
[4] Conversation messages                       ← new each turn

Static content first. Dynamic content last. Everything you want cached has to come before everything that changes between requests.

The violations that break this are more common than you’d think, and they’re all subtle:

Don’t put timestamps in your system prompt. It sounds obvious, but the moment you inject “current time: 14:32:05” into a static system prompt, you’ve guaranteed a cache miss on every single request. The prefix is different every time by definition.

Never add or remove tools mid-session. Tools are part of the cached prefix. If you add a tool at turn 5 because “the agent might need it now,” you’ve invalidated the cache for the entire conversation up to that point. You’re paying the full uncached cost for everything before it. The counterintuitive implication: give the agent all tools upfront and keep them consistent, even if many of them are irrelevant to the current task.

Don’t switch models mid-session without a plan. Prompt caches are unique to models. If you’re 100K tokens deep into an Opus session and decide to ask a simple question with Haiku, switching models means rebuilding the cache from scratch. The cheaper model becomes more expensive because of the cache rebuild cost. The math is genuinely counterintuitive.

For mid-session updates, use <system-reminder> tags in messages, not prompt edits. If you need to tell the agent something has changed (a file was updated, the time is relevant), inject it as a user message or tool result with a <system-reminder> tag rather than modifying the prompt. The prompt stays stable, the cache stays warm, the agent gets the update.

The Plan Mode architecture is probably the best illustration of how seriously Anthropic takes this. The intuitive implementation of Plan Mode would be: when the user enters planning mode, swap out the tool set to only read-only tools. Clean, logical. But it would break the cache every time Plan Mode is toggled.

The actual implementation: keep all tools loaded at all times, always. Plan Mode is implemented as two tools: EnterPlanMode and ExitPlanMode. When toggled, the agent gets a system message explaining the behavioral constraints (explore, don’t edit, call ExitPlanMode when done). The tool set never changes. The cached prefix stays valid.

Bonus: because EnterPlanMode is a real tool the model can call itself, it can autonomously enter planning mode when it detects a hard problem, without any human triggering it. A cache constraint became a capability by accident.


The Compaction Stack: Managing the Context Window at Scale

Here’s a problem that sounds theoretical until you’re actually running a long agentic session: the context window fills up. Every tool call adds more content. Every file read, every shell output, every model response grows the accumulated context. Eventually, you hit the limit.

The naive solution is to start fresh. But starting fresh means losing all the reasoning the agent has built up. The harder solution is compaction: intelligently summarizing older context to make room for new context, without losing critical information.

Claude Code has five compaction layers, applied cheapest-first in sequence:

Share

The sequence matters: always try the cheapest option before escalating. Tool result trimming costs almost nothing. Full LLM summarization costs a complete uncached API call on the entire conversation history.

The cache-safe forking insight is what makes Layer 5 work without becoming a cost trap. The obvious implementation of auto-compaction would be: take the conversation, send it to the model with a “please summarize” system prompt, get a summary. But that request uses a different system prompt than the main conversation. The prefixes diverge at the first token. None of the cache applies. You pay the full uncached input rate for the entire conversation history, which is exactly when it’s longest and most expensive.

The actual implementation: for the compaction request, use the exact same system prompt, user context, session context, and tool definitions as the parent conversation. Prepend the full conversation history, then append the compaction prompt as a new user message at the end. From the API’s perspective, this request looks nearly identical to the parent’s last request. The cached prefix is reused. Only the compaction prompt itself is new.

There’s a circuit breaker: if three consecutive compaction attempts fail (which can happen when even the summarization prompt exceeds the context limit), the system gives up cleanly rather than looping infinitely. Graceful degradation as an architectural principle, not just error handling.

The edge case worth knowing: compaction replaces older messages with a summary, which means specific instructions from early in the conversation may not survive. This is precisely why persistent rules belong in CLAUDE.md, not in early conversation messages. CLAUDE.md re-injects on every turn. Conversation history gets summarized away. Where you put something determines whether it survives.


The Hook System: 25+ Lifecycle Events Nobody Talks About

The hook system is the most underrated extensibility layer in Claude Code, and it’s also the one that separates “running Claude Code” from “architecting with Claude Code.”

There are 25+ lifecycle events where you can bind commands, functions, and workflows directly into the agent’s execution. PreToolUse, PostToolUse, PreWrite, PostWrite, OnCompact, OnSessionStart. Each one fires at a specific point in the turn pipeline, and each one can be used to inject behavior without modifying the core loop.

The practical patterns this enables:

Pre-write linting: before the agent writes a file, run your linter. The agent only ever writes already-valid code. No format fixup passes needed.

Post-edit test runs: after a file is modified, run the relevant tests automatically. The agent gets test results as context in the next turn without any prompting required.

Context injection from documentation: when the agent starts working in a specific module, automatically inject that module’s architectural docs. The agent has relevant context before it starts, not after it realizes it needed it.

Automated Slack updates on completion: when a task finishes, post to the relevant channel. No human polling required.

The philosophical shift this enables is the one worth naming explicitly. Most people using Claude Code are still thinking in terms of prompting: tell the agent what to do, evaluate what it did, adjust and re-prompt. The hook system lets you move from prompting to architecting: shape how the agent reacts within your environment at a structural level, not a conversational level. The agent adapts to your stack rather than you adapting your stack to the agent.

The gap most teams leave: they discover hooks, use them for one or two conveniences, and stop there. The compound value comes from treating the hook system as a full configuration layer, equivalent in importance to the tool set and the permissions model. If your PostToolUse hooks don’t include your linting, testing, and notification logic, you’re leaving a significant portion of the platform’s leverage on the table. Kind of similar to this is loop. If you wish to read more about it, go ahead and read it here.


Claude vs. ChatGPT Memory: The Architectural Bet

It’s worth surfacing the architectural divergence directly, because the tradeoff reveals something interesting about design philosophy.

ChatGPT injects lightweight pre-computed summaries into every prompt. Automatic, consistent, always there. The model always has some recent context, even for topics it hasn’t been asked about directly.

Claude uses on-demand retrieval: two tools (conversation_search and recent_chats) that are only invoked when the model determines past context is relevant. Not automatic. Not always injected. Only pulled when needed.

The tradeoff is real and the “correct” answer is context-dependent. ChatGPT’s approach sacrifices depth for seamless continuity: it always knows something about recent history, but the summaries are lossy. Claude’s approach sacrifices seamless continuity for depth: when it retrieves, it gets the full original content, not a summary. But it requires the model to correctly identify when retrieval is necessary.

Here’s the implication that matters for builders: Claude’s architecture trusts the model to make retrieval decisions. This means the quality of its historical recall is bounded by the quality of its judgment about when history is relevant. In most conversational cases, it performs well. But it can miss context that a human would have instinctively pulled. The system is more capable when it retrieves correctly, and less capable when it doesn’t notice it should retrieve at all.

Neither approach is universally better. They’re different bets about where to place the burden: on the infrastructure (ChatGPT, always inject, always pay the cost) or on the model (Claude, retrieve when needed, require good judgment). The right choice depends on your tolerance for missed context versus your tolerance for context window bloat.


Subscribe now

The Synthesis: What This Architecture Actually Teaches

Pull back far enough, and there are four architectural lessons here that transfer directly to anyone building agentic systems.

The intelligence is a small fraction of the system. The while-loop that drives Claude Code’s agent behavior is trivially simple. The 98.4% that surrounds it, the context management, the compaction pipeline, the permission model, the caching strategy, is where the real engineering lives. If you’re building agentic systems and spending most of your time on the “AI part,” you have the ratio inverted.

Memory is a preparation problem, not a storage problem. The entire memory architecture of Claude Code, from <userMemories> XML injection to memory.md pointer indexes to CLAUDE.md constitutional files, is fundamentally about preparing the right context before each API call. The model doesn’t “remember” anything. It reads what you’ve given it. Your job as the architect is to make sure the right things are always there to read.

Caching constraints produce better architecture. The Plan Mode implementation exists the way it does because of a caching constraint. The tool stubs and defer_loading pattern exists because of a caching constraint. The static-first prompt ordering exists because of a caching constraint. In every case, the constraint forced a design that turned out to be genuinely better than the unconstrained “obvious” implementation would have been. This is the kind of productive constraint that most engineers try to engineer away, when they should be engineering toward it.

Treat errors as context, not exceptions. The permission denial that feeds back into the model as a ToolResult instead of crashing the loop is a small implementation choice with large implications. An agent that crashes on denied tool calls requires external error recovery logic. An agent that reads its own denials as feedback can recover intelligently. The robustness of the system is a function of how you route failures, not just how you route successes.


The Strange Conclusion

What the Claude Code source leak actually revealed isn’t a sophisticated AI. It’s the most carefully engineered context plumbing you’ll find in a production system, built to give a language model the best possible conditions to do the small percentage of work that requires actual intelligence.

The model itself, the part that “thinks,” runs inside a while-loop. The other 98.4% makes sure the while-loop has exactly what it needs, every time it runs.

Most of the discourse around AI agents focuses on model capability: which model is smartest, which has the longest context window, which reasons best. That’s the 1.6%.

The teams that are actually shipping capable, reliable agentic systems are quietly obsessing about the 98.4%. The compaction strategies. The caching discipline. The hook system. The memory preparation layer. The permission architecture.

And the funny part? None of it requires a better model.

It requires better plumbing.

-Hardik

Support my work : buymeacoffee.com/HardikGoel

Thanks for reading Hardik's Substack! This post is public so feel free to share it.

Share

Thanks for reading Hardik's Substack! Subscribe for free to receive new posts and support my work.

Leave a comment

Originally shared on Substack. Canonical home: this page.
More at hardikgoel portfolio.