July 9, 2026

Claude Code: everything comes back to the context window

For my first few weeks with Claude Code, I used it like a search engine that could type: ask a question, get some code, paste it into place. It worked well enough that I never questioned it.

Then my company introduced coffee chats: 15-minute one-on-one meetings for sharing AI knowledge across the organisation. The luck of the draw paired me with Dylan Harbour, our technical director, who was already using and testing the Research–Plan–Implement framework — a way of structuring work for Claude Code where you never just ask for code, you build up to it in phases. I hadn’t heard of it. That conversation sent me down a rabbit hole: I deep-dived into how Claude Code actually works, adopted RPI, and now use it daily.

This post is what I found at the bottom of the rabbit hole. The surprising part is how coherent it all turned out to be: nearly every feature in Claude Code — the permission modes, CLAUDE.md, skills, subagents, hooks, the compaction commands, and RPI itself — is an answer to the same question: what deserves space in the context window?

The route is five stops, and it’s the same question at every one:

  1. The machine — the loop, its RAM, and the cliff at the end.
  2. Working with it — the workflow, the permission modes, thinking triggers, and sessions.
  3. What lives where — CLAUDE.md, the extension mechanisms, and what each one costs.
  4. When it fills anyway — compaction and the scalpels.
  5. Across sessions — specs, spec-driven development, and RPI.

By stop five, you’ll see the whole industry has arrived at the same answer.

Claude Code is a loop, not a chatbot

The first thing that changed how I use it: Claude Code is not “question in, answer out”. It’s an agentic loop. It reads your codebase, forms a plan, executes changes across dozens of files, runs your tests, reads the errors, and tries again.

Hand-drawn loop of four boxes — read context, form a plan, take action, verify result — with “repeat until done (or broke)” in the middle

Once you see it as a loop, a natural question follows: what does the loop run on? What’s the resource that gets consumed each time it goes around?

The context window is the only resource that matters

The answer is the context window, and I think of it as RAM for the conversation. Every file Claude reads, every command output it captures, every tool result, every message you exchange — all of it accumulates in a fixed-size buffer. And when the buffer fills up, performance doesn’t degrade gracefully. It falls off a cliff. Instructions get forgotten. Work gets dropped. Claude starts re-solving problems it already solved. And it starts earlier than you’d think: the HumanLayer crowd calls roughly 40–60% utilization the “dumb zone” — degradation begins long before the buffer is anywhere near full.

Hand-drawn graph of quality vs context: the line stays flat, then falls off a cliff at ~95% — a stick figure mid-fall says “I got this”

Hand-drawn bar of colored segments filling the context window — CLAUDE.md, tool defs, files read, output, chat — with a small free slice labelled “your actual feature goes here”

The blocks on the left are loaded on every single request — more on that cost model in a bit. Everything to the right is your session slowly filling the buffer as you work. You don’t have to guess at this layout either: /context draws it for your live session — htop, but for the buffer.

There’s a safety net — auto-compaction kicks in when the window is nearly full and summarizes older content to make room — but the summarization is lossy. Things you cared about can disappear in the compression.

So the mental shift is: treat context like a scarce resource. Everything below is really just different ways of doing that.

The workflow that actually works: explore, plan, implement, commit

The workflow that consistently gives me the best results has four phases. Skipping the first one is the most common mistake I made early on, and it leads to Claude confidently solving the wrong problem with technically correct code.

Explore. Start in plan mode. Let Claude read your codebase. Point it at the relevant directories and ask it to understand the existing patterns, conventions, and architecture. This is cheap — plan mode restricts Claude to read-only operations, so it can’t accidentally modify anything while it’s figuring out the lay of the land.

Plan. Still in plan mode, ask Claude to outline its approach. Then actually read the plan. Push back on the parts that don’t match your mental model. This is where misunderstandings surface, and fixing a misunderstanding in a plan costs nothing. Fixing it in implementation costs you a revert and a burned context window.

Implement. Switch to normal mode. Claude now has the context from exploration and a plan you’ve reviewed. The quality difference is dramatic, because it’s working from understanding rather than assumption. Let it run.

Commit. Claude offers to commit when it’s done. Review the diff — if the earlier phases went well, the diff should be unsurprising. Commit frequently. Small commits are cheap insurance, and being able to roll back cleanly depends on having clean commit boundaries.

I used to think this ceremony was about being cautious. It’s not — it’s about being efficient. Exploration and planning are cheap. Implementation and debugging are expensive.

Permission modes are workflow selectors

Claude Code has six permission modes these days, and I initially read them as safety settings. They’re more useful than that: they’re a way of declaring which phase of the workflow you’re in.

  • Plan mode — read-only. For exploration and research.
  • Manual (the default) — asks before every write. For surgical changes where you want to approve each edit.
  • Accept edits — file edits and safe filesystem commands flow freely; still asks for other shell commands. The sweet spot for implementation when you trust the plan but want to supervise anything system-level.
  • Auto — the newest addition: a separate classifier model vets each action for scope creep, unfamiliar infrastructure, and injection-shaped requests before it runs. Not “no prompts” — prompts replaced by judgment.
  • Bypass permissions — the old “full auto”. No interruptions beyond a few hardcoded circuit-breakers. Only worth it when you have strong verification in place: a good test suite, a linter with teeth, pre-commit hooks that enforce standards.
  • Don’t ask — auto-denies anything not pre-approved. Built for CI and locked-down environments.

Thinking triggers: the magic words are literal

Extended thinking is Claude reasoning to itself before it acts, and you control the budget with what might be the least dignified interface in modern software: think < think hard < think harder < ultrathink. Each phrase, dropped anywhere in your prompt, buys a bigger reasoning budget — ultrathink originally allocated a very literal, hardcoded 31,999 tokens.

Hand-drawn reasoning dial with ticks for think, hard and harder, the needle buried past ultrathink — captioned &ldquo;the dial goes to 31,999&rdquo;

The newer, cleaner control is /effort: it sets the reasoning effort for the whole session — low, medium, or high — instead of relying on per-prompt incantations. Set it once for the kind of work you’re doing rather than remembering the magic word every time.

When to reach for them: match the budget to the decision, not the size of the diff. Exploration, plan reviews, architecture calls, and debugging that survived two attempts all benefit from a higher setting. Mechanical work — renames, formatting, executing an already-approved plan — doesn’t; keep it low and fast. Thinking tokens are output tokens, so the currency here is latency and money rather than window space — but it’s the same question as always: what deserves the budget?

Sessions don’t persist (and that’s fine)

Each new session starts with a fresh context window. Everything Claude learned about your codebase, your preferences, your project’s quirks — gone. This bothered me at first, but it’s not a bug. It’s a design constraint that shapes how you should work.

You have four options when coming back to work:

  • Continue — resume the last session in the current directory, full history restored. For when you were interrupted mid-task.
  • Resume by ID or name — for long-running projects with parallel workstreams. Name your sessions meaningfully so you can find them later.
  • Fork — new session that starts with the full history of an existing one, then diverges. The original is preserved. Good for exploring an alternative approach without losing your current progress.
  • Fresh session — start clean. Often the right choice when the previous session’s context was mostly spent on exploration that no longer matters.

The rule I’ve settled on: persistent knowledge goes in CLAUDE.md and in files in the repo, not in session history.

Claude now hedges against its own amnesia, too: Auto Memory is a MEMORY.md file Claude writes to itself — corrections, preferences, build quirks — with the first ~200 lines auto-loaded at each session start, separate from the CLAUDE.md you curate by hand. The goldfish started keeping a diary.

Hand-drawn goldfish with a question-mark thought bubble, captioned &ldquo;…have we met?&rdquo;

CLAUDE.md: the always-on file

CLAUDE.md is not documentation, and it’s not a README. It’s instructions injected into every single request Claude processes in your project. That distinction matters, because it means every line has a cost: it consumes context window space on every turn, reducing the space available for actual work.

Hand-drawn CLAUDE.md invoice page with a red rubber stamp reading &ldquo;rent due: every turn&rdquo;

Claude Code loads CLAUDE.md files from multiple locations, additively:

  • one in your home directory applies to every project
  • one in your project root applies to that repository
  • subdirectory CLAUDE.md files apply when Claude works in those directories
  • path-scoped rules in .claude/rules/ load only when Claude touches matching files (and a gitignored CLAUDE.local.md holds personal preferences)
  • organization-managed files in system directories apply to everyone on the machine

Claude sees all of them, layered together. So the home-level file gets preferences that apply everywhere (your testing approach, commit message style), the project-level file gets repository-specific knowledge, and subdirectory files get context relevant only to that corner of the codebase.

If you’re starting from scratch, /init writes the first draft for you — it analyzes your codebase, detects build systems, test frameworks, and directory structure, and generates a starter CLAUDE.md. Not perfect, but a solid foundation to refine.

One guideline worth respecting: keep it under ~200 lines. Past that, instructions start competing with each other. Claude doesn’t ignore a bloated CLAUDE.md — it dilutes attention across too many directives, and the ones that matter most get the same weight as the ones that barely matter. A concise CLAUDE.md isn’t a nice-to-have; it’s a performance requirement.

What everything costs

Beyond CLAUDE.md, Claude Code has a handful of other extension mechanisms: skills (on-demand instruction files), subagents (Claude instances with their own context window), MCP servers (structured connections to external services), and hooks (external scripts that run on events). I’ll get to when each one makes sense in a moment — but first, the part that took me longest to appreciate: they have wildly different context costs. Understanding the cost model is the difference between a session that stays sharp for 200 turns and one that compacts after 40.

Feature When it loads What loads Context cost
CLAUDE.md Session start Full content of all CLAUDE.md files Every request
Skills Session start + when used Descriptions at start; full content when invoked Low (descriptions every request)*
MCP servers Session start + on demand Tool names at start; full schemas only when needed Cheap by default
Subagents When spawned Fresh context with specified skills Isolated from main session
Hooks On trigger Nothing (runs externally) Zero, unless the hook returns context

* By default, skill descriptions load at session start so Claude can decide when to use them. You can hide a skill from Claude entirely in its frontmatter — zero cost until you invoke it manually.

Where should this information live?

This was the question I kept getting wrong: I have some knowledge or capability I want Claude to have — is it a CLAUDE.md entry, a skill, a subagent, an MCP server, or a hook? The answer comes down to when the information is needed and how much it costs to keep available.

CLAUDE.md when the information is needed on every turn. Build commands, style rules, architectural constraints, environment quirks. This is the always-on channel — keep it under 200 lines.

A skill when the information is needed for specific task types. Database migration procedures, deployment checklists, API integration guides. Skills load on demand: their descriptions cost a little on every request, but the full content only loads when invoked. A skill with 200 lines of migration instructions costs you almost nothing until Claude actually needs to do a migration.

A subagent when the task involves reading many files, processing verbose output, or exploration that would bloat your main context. Subagents run in their own context window and only the summary comes back. This one surprised me: subagents are a context management strategy, not just a parallelism tool.

MCP when you need structured access to external services. These days only the tool names load at session start — full schemas are fetched on demand — so the ongoing cost is modest. The structured interface and permission integration make it the right choice for sensitive or frequently-used integrations; for everything else, a plain CLI tool (gh, aws) is often even cheaper.

Hooks when you want to extend behavior at zero context cost. Hooks run as external processes: a PreToolUse hook that validates file paths, a PostToolUse hook that logs actions, a SessionStart hook that sets up the environment. None of them consume any context, which makes hooks the most context-efficient extension mechanism available.

Getting the classification wrong hurts in both directions. Put reference material in CLAUDE.md and you pay for it on every request. Put always-needed rules in a skill and Claude will violate them whenever the skill isn’t loaded.

When the context fills up anyway

Even with good habits, long sessions fill the window. Here’s what happens, and what you can do about it.

Auto-compaction. At roughly 95% capacity, Claude triggers automatic compaction. It’s not a crash — older tool outputs get cleared and the conversation history gets summarized. The files it read twenty turns ago, the command outputs from early exploration, the intermediate reasoning: compressed into a summary. Space is freed, detail is lost. One nuance worth knowing: the project-root CLAUDE.md is re-read from disk and survives compaction, but nested subdirectory CLAUDE.md files don’t reload until Claude next touches those directories.

Hand-drawn compactor squeezing a tall thirty-turn document into a small card labelled &ldquo;a summary&rdquo; — one important scrap escapes, labelled &ldquo;the bit you needed&rdquo;

/compact with focus. You can compact manually, and — the part I didn’t know for ages — you can direct what survives: /compact Focus on the API changes. Without focus instructions, compaction makes its own judgment about what matters, which is often reasonable but occasionally drops exactly the wrong thing.

/rewind for surgical compaction. Sometimes you don’t want to compact the whole conversation, just a portion. Press Escape twice (or /rewind) and you get a list of your prompts as checkpoints. Select one and there are five options:

  1. Restore code and conversation — revert everything to that point
  2. Restore conversation only — rewind the chat, keep the current code
  3. Restore code only — keep the chat, revert the file changes
  4. Summarize from here — condense everything after that point into a summary, keeping everything before it at full fidelity
  5. Summarize up to here — the mirror image: compress everything before that point, keeping the recent work intact

Those last two are the interesting ones. If you spent thirty turns exploring a dead end and then found the right approach, you can summarize the dead end (freeing context) while keeping the productive work at full detail. Much more precise than whole-conversation compaction.

Forking as an escape hatch. When a session’s context is well and truly polluted — too many dead ends, an auto-compaction that ate critical instructions — claude --continue --fork-session creates a new session with the full history of the old one, diverging from that point. The original is untouched. You keep the valuable context (project setup, decisions made) without re-explaining everything, and take the work in a new direction. It’s not a substitute for good context management, but it’s a useful escape hatch when things have drifted despite your best efforts.

Specs: context that survives everything

CLAUDE.md is persistent but static. For work that spans multiple sessions, there’s a complementary trick: write a spec document first.

A spec is a detailed description of what you’re building, written before implementation, living as a file in your repository — spec.md, design.md, whatever fits. You reference it from CLAUDE.md with an @path import or tell Claude to read it at the start of each session.

The power of a spec is that it survives everything that kills conversational context. Session compacted and lost the nuances of your earlier discussion? The spec is still there, unchanged, in a file Claude can re-read. Starting a new session tomorrow? The spec provides full context without you re-explaining anything.

Hand-drawn spec.md page wearing a superhero cape, labelled &ldquo;immortal&rdquo;

This matters most for projects spanning more than two or three sessions. Without a spec, each new session starts with a lossy re-explanation of intent. With one, each session starts with a precise, version-controlled description of the goal.

Spec-driven development

There’s a named workflow built around this idea, and it contrasts sharply with the default AI-coding loop of prompt → code → debug → repeat — where context fills up with failed attempts, there’s no source of truth, and each iteration starts from scratch in the same cluttered window.

Spec-driven development goes: research → spec → refine → tasks → done.

  1. Research. Spawn subagents to investigate the relevant parts of the codebase in parallel — architecture, API surface, test patterns, dependencies. The findings flow back to the main context.
  2. Spec. Ask Claude to write a specification based on the research: “Your goal is to write a report. Produce a technical spec covering architecture, data model, error handling, and rollback strategy. Do not write any code yet.” The spec becomes a file in the repository.
  3. Refine. Before implementing, flip the interview around: “Do you have any questions about the spec before we implement it?” Claude asks about ambiguities, design decisions, edge cases. You answer, the spec gets updated. This catches misunderstandings that would have become bugs.
  4. Tasks. Break the spec into atomic tasks, each implemented by a subagent with a fresh context, each followed by a commit. The main context stays lean — it’s orchestrating, not implementing.
  5. Done. Completion criteria, defined in the spec, decide when the work is finished.

The spec file isn’t just a planning artifact — it’s the recovery point for the whole workflow. Subagent fails? The spec is still there; start a new one on the same task. Context compacts? The spec survives on disk. Resuming tomorrow? The spec tells the new session everything it needs.

Some teams take this all the way: specs are markdown files in the codebase, written by Claude, reviewed by humans through normal code review, then executed by Claude. When the implementation lands, the spec remains as documentation of the design decisions. Specification-as-code — not a throwaway planning doc, but a versioned, reviewed, executed artifact.

Research–Plan–Implement: spec-driven development, packaged

This brings me back to the framework from that coffee chat: Research–Plan–Implement, from HumanLayer. It’s the most worked-out version of this idea I’ve found, and it’s worth understanding even if you never install it, because it takes everything above — specs, subagents, fresh contexts, human review — and wires them into one pipeline.

The problem it attacks is one you’ve probably felt: a long Claude conversation slowly curdles. By the seventh or eighth exchange, the model isn’t responding to your codebase anymore — it’s responding to the wreckage of its own earlier attempts. A wrong file path mentioned once gets quoted back three turns later as ground truth. A function renamed in turn four reappears under its old name in turn nine, because turn nine sits closer in the context window to where the old name was introduced than to where the rename happened. Every new turn is conditioned on every prior token, and the prior tokens include the model’s own failures.

Hand-drawn ghost with a speech bubble quoting oldName(), captioned &ldquo;…as I said in turn four&rdquo;

Dex Horthy, who runs HumanLayer, calls the fix intentional compaction: don’t wait for auto-compaction to lossy-summarize your conversation — deliberately forget everything except what was approved. RPI does this with three slash commands, each running in a fresh context window, each producing a small artifact that a human reviews before the next phase starts:

  1. /research_codebase compresses truth. It dispatches subagents to read the actual code and folds their findings into a short markdown document: which files are involved, function names with line numbers, the behavior actually observed rather than what stale docstrings imply. You review it before anything else happens — if the research is wrong, the plan will be wrong, and the errors compound from there.
  2. /create_plan compresses intent. It takes the approved research document and produces a tactical plan: every file that will change, every validation step, in order. No narrative, no motivation — those belong to research. You review this too. The plan is the contract between you and the next agent.
  3. /implement_plan opens a fresh window seeded with only the two approved documents. The implementing agent never sees the messy conversation that produced them. The chat history that argued the plan into shape is, by design, gone — a fresh window can’t be poisoned by mistakes that were never written into the artifacts.

Hand-drawn pipeline of three boxes — /research reads the code, /plan writes the plan, /implement executes — each in a fresh window, joined by arrows labelled research.md and plan.md, with a red &ldquo;you review&rdquo; stamp under each

Notice what the diagram is really saying: the conversation never travels between windows. Only the artifacts do, and only after you’ve approved them.

The research phase leans on six specialized subagents: three for code (codebase-locator finds where things live, codebase-analyzer traces data flow with file:line references, codebase-pattern-finder locates similar existing implementations to model after), two for prior research artifacts in a thoughts/ directory, and one for web research. Most of the surface is code, which is the point — context isolation matters most where context grows fastest.

The artifacts stay deliberately small. A research document for a real change runs maybe four to eight pages; a plan is similar in shape but different in tense — every section is something that will happen, with a verification step attached. That smallness is what makes the human gates practical: a six-page research document can be skimmed in five minutes and edited in twenty. A six-thousand-line patch cannot.

A year on, this has stopped being a practitioner’s trick. GitHub’s Spec Kit formalizes the same move — spec → plan → tasks → implement — as a toolkit that works across dozens of coding agents; Amazon’s Kiro builds a whole IDE around it; Martin Fowler compares the tooling like a product category, because it now is one. The common move everywhere: small, reviewed, versioned documents carrying intent between fresh context windows. The honest caveat hasn’t changed either — Anthropic’s own numbers put multi-agent systems at roughly 15× the tokens of chat, and the new agent teams at ~7× just when teammates plan together. What the spend buys is protection from the quietest failure mode: work that looks done but stopped one step short — no test exercising the new behavior, no changelog entry, acceptance criteria written and graded by the same author.

Hand-drawn doodle of three arrows labelled spec kit, kiro and qrspi, all converging on one small checked document

The framework itself kept moving, too. In a March 2026 follow-up talk — pointedly titled Everything We Got Wrong About Research-Plan-Implement — Horthy revised his own creation into QRSPI, splitting the three phases into eight smaller ones: question, research, design, structure, plan, worktree, implement, PR. The trigger was yet another budget: frontier models reliably follow only around 150–200 instructions, and a phase prompt that stacks more than that doesn’t error — it silently skips steps, usually the human-verification ones. Every revision points the same direction as the rest of this post: smaller contexts, fewer instructions per phase, and review gates shifted further left, where the leverage is — in Horthy’s hierarchy, a bad line of a plan becomes hundreds of bad lines of code, and a bad line of research becomes thousands. He also reversed one piece of advice outright, and it tempers the skim-the-artifact pitch above: “Don’t read the plans. Please read the code… the code is the thing that actually ships.” The artifacts carry intent between windows; they don’t exempt the diff from human eyes.

Trying it is cheap — a one-line installer pulls just the .claude/ command set into your project:

curl -fsSL https://raw.githubusercontent.com/acampb/claude-rpi-framework/main/setup-rpi-framework.sh | bash

The setup is more than the three headline commands. It scaffolds the thoughts/ directory — research/, plans/, handoffs/, tickets/ — so every artifact lives version-controlled next to the code, installs the six subagents, and adds the supporting commands: /validate_plan checks an implementation against the plan it came from, /iterate_plan revises a plan that met reality, /create_handoff and /resume_handoff pass work between sessions, and /commit and /describe_pr handle the git ceremony.

Wrapping up

If I could send one sentence back to the me from those first few weeks — the one using Claude Code as a search engine that could type — it would be this: the context window is the machine, and everything else — the workflow phases, the permission modes, CLAUDE.md, skills, subagents, hooks, specs, RPI — is a different tool for deciding what gets to be in it.

Once that clicked, the individual features stopped feeling like a pile of commands to memorize and started feeling like one coherent design. I owe Dylan a coffee.

Hand-drawn steaming coffee cup, labelled &ldquo;for: Dylan&rdquo;

References

Powered by Hugo & Kiss.