# How Claude Code assembles context

Tyler Youk · updated 2026-10-31 · https://tyleryouk.com/writing/context-assembly

Many module prompts on this wiki say *"find the always-on instruction file this harness loads every
session."* This page answers that instruction. Every module here targets Claude Code as its one
harness, so the answer is for Claude Code, with real paths.

**Researched 2026-08-30 against primary sources.** Each claim is marked CONFIRMED, with the page or
source file it came from, or UNCONFIRMED. Where a doc contradicts the shipped source, we trust the
source and record the contradiction.

**Read the marks.** We use this file to write commands that must actually work, so a named gap is more
useful than a plausible guess.

## Before you trust any of it: the doc tree moved in 2026 {#before-you-trust-any-of-it-the-doc-tree-moved-in-2026}

Older guides point at URLs that now return 404. An agent that fetches them gets nothing and reports that.

| Old location                                                    | 2026 location                                                        |
| --------------------------------------------------------------- | -------------------------------------------------------------------- |
| `code.claude.com/docs/en/docs/claude-code/<page>` (returns 404) | `code.claude.com/docs/en/<page>`, with the index at `/docs/llms.txt` |

***

# Claude Code (CLI) [#claude-code-cli]

We measured against **v2.1.251** on Linux/WSL2 as well as reading the docs.

## A. Context assembly {#a-context-assembly}

Claude Code loads these scopes from broadest to most specific (CONFIRMED, `/docs/en/memory`):

| Scope          | Path                                                                                                                                                   |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Managed policy | macOS `/Library/Application Support/ClaudeCode/CLAUDE.md`, Linux and WSL `/etc/claude-code/CLAUDE.md`, Windows `C:\Program Files\ClaudeCode\CLAUDE.md` |
| User           | `~/.claude/CLAUDE.md`                                                                                                                                  |
| Project        | `./CLAUDE.md` or `./.claude/CLAUDE.md`                                                                                                                 |
| Local          | `./CLAUDE.local.md`                                                                                                                                    |

The files concatenate. None of them overrides another. The docs say "All discovered files are
concatenated into context rather than overriding each other… ordered from the filesystem root down to
your working directory." Within each directory, `CLAUDE.local.md` is appended after `CLAUDE.md`. It is
not deprecated (CONFIRMED, the docs still describe it as live).

Claude Code delivers CLAUDE.md as a user message after the system prompt, not as part of the system
prompt (CONFIRMED). Output styles are what modify the system prompt.

Claude Code walks upward at launch and downward only on demand. It loads files from the working directory
"and every directory above it." A file in a subdirectory loads only when Claude reads a file in that
directory with the Read tool. It does not load at launch, and it does not load when Claude writes or
creates files there (CONFIRMED, `/docs/en/debug-your-config`).

The import syntax is `@path/to/import`. Imports follow at most four hops. A relative path resolves
against the file that contains the import, and `@~/.claude/foo.md` works. Imports expand at launch, so
they do not save any context.

`.claude/rules/*.md` is the 2026 mechanism for rules. A rule file without `paths:` frontmatter loads at
launch with the same priority as `.claude/CLAUDE.md`, and user rules load before project rules. A rule
file with `paths:` loads only when Claude reads a matching file. The brace-expansion budget is 1,000
patterns or 4 MiB. Claude Code uses an over-budget pattern unexpanded, and it then matches nothing.

The docs split what loads every time from what loads on a trigger (CONFIRMED, `/docs/en/context-window`;
the token counts are the doc's own). Every session loads the system prompt (\~4,200), auto memory
`MEMORY.md` (\~680), environment info (\~280), MCP tool names only (\~120, with schemas deferred), skill
descriptions only, and then user and project CLAUDE.md. A full skill body loads only when the skill is
used. Once loaded, it stays in the conversation for later turns.

These are the documented limits:

| Limit                        | Value                                           |
| ---------------------------- | ----------------------------------------------- |
| CLAUDE.md hard limit         | 4 MiB. Claude Code skips a larger file entirely |
| CLAUDE.md advisory           | under 200 lines                                 |
| Auto memory `MEMORY.md`      | first 200 lines or 25 KB                        |
| Skill description in listing | truncated at 1,536 chars                        |
| Skill listing budget         | 1% of context window, fallback 8,000 chars      |
| MCP tool description         | 2 KB each                                       |
| Number of skills             | no documented cap. The budget is the real limit |

### Compaction {#compaction}

After compaction, Claude Code re-injects project CLAUDE.md, unscoped rules, auto memory and the plan from
disk. It brings back files Claude read, for up to five files. Invoked skill bodies come back at 5,000
tokens each, with a 25,000 total. Context added by hooks is summarized away.

The skill listing is the surprise. Claude Code does not re-inject it after `/compact`, so from then on
Claude can auto-invoke only the skills it already used in that session (CONFIRMED). No page we fetched
mentions "microcompact", so that term is UNCONFIRMED.

### Showing what is loaded {#showing-what-is-loaded}

Use `/context [all]`. It shows a colored grid that breaks out the system prompt, MCP tools, subagents
*with the source each one loaded from*, memory files and skills. `/memory` lists *locations*, including
files that do not exist yet, rather than what actually loaded. `/status` shows the active settings
sources. `/doctor` is now a bundled skill rather than a screen.

## B. Inputs and outputs {#b-io-surface}

The permission modes are `default` (alias `manual`), `acceptEdits`, `plan`, `auto`, `dontAsk` and
`bypassPermissions` (CONFIRMED against the binary). Claude Code evaluates deny rules, then ask rules,
then allow rules, and the first match wins. A more specific rule does not change that order, so a broad
`Bash(aws *)` deny beats a narrow `Bash(aws s3 ls)` allow.

Two syntax traps are worth memorizing. `Bash(ls *)`, with a space, does not match `lsof`, but
`Bash(ls*)` does. In Read and Edit rules, `/path` is relative to the settings source and not to the
filesystem root. The absolute form is `//path`.

Sandboxing works on macOS (Seatbelt) and on Linux and WSL2 (bubblewrap). Native Windows and WSL1 are
unsupported. By default the sandbox allows writes to the working directory plus any `--add-dir`. It
allows reads of the entire computer except denied paths, which still includes `~/.aws/credentials` and
`~/.ssh/`.

MCP servers have three scopes. `local` is the default and lives in `~/.claude.json`. `project` lives in
`.mcp.json` at the repo root. The third is `user`. The transports are http, sse (deprecated), stdio and
ws. Each machine must approve a project server once.

Subagents live in `.claude/agents/` and `~/.claude/agents/`, and each one gets its own context window.
The precedence is inverted between the two. For skills, a personal skill overrides a project skill. For
subagents, a project subagent outranks a user one. The built-in Explore and Plan subagents skip CLAUDE.md
and git status, while custom subagents load it as usual.

Claude Code has 33 hook events (CONFIRMED, `/docs/en/hooks`). One of them is `InstructionsLoaded`, which
fires when a CLAUDE.md or rules file loads. The docs name it as the way to "log exactly which instruction
files are loaded, when they load, and why." Exit code `2` blocks absolutely. It overrides JSON output, and
not even `permissionDecision: allow` can override it. On exit 0, stderr goes only to the debug log and
never reaches Claude. `PreToolUse` fires before any permission check in every mode, including
`bypassPermissions`. Even so, a hook `allow` cannot loosen a deny in settings.

For headless runs, use `claude -p` with `--output-format text|json|stream-json`, and `--json-schema` for
structured output. The exit code is 0 on success, non-zero on failure and 143 on SIGTERM. `--bare` skips
discovery of hooks, skills, commands, subagents, plugins, MCP and CLAUDE.md, and it never reads the
keychain. The docs say it "will become the default for `-p` in a future release." The SDK packages are
`@anthropic-ai/claude-agent-sdk` and `claude-agent-sdk`. The package `@anthropic-ai/claude-code` is not
the SDK.

## C. State on disk {#c-state-on-disk}

We checked these paths directly on a live install as well as in the docs.

```
~/.claude/CLAUDE.md                              user memory
~/.claude/settings.json                          permissions, hooks, env, model
~/.claude/settings.local.json                    written BY Claude Code on "don't ask again"
~/.claude/.credentials.json                      mode 0600 — Linux/WSL/Windows (macOS uses Keychain)
~/.claude/projects/<munged-cwd>/<uuid>.jsonl     full transcript: every message, tool call, result
~/.claude/projects/<project>/memory/MEMORY.md    auto memory, keyed by GIT REPO (worktrees share it)
~/.claude/history.jsonl                          every prompt ever typed, with timestamps
~/.claude/{skills,agents,plugins,shell-snapshots,file-history,state}/
~/.claude.json                                   MCP servers, trust decisions, OAuth account — NOT settings
```

`~/.claude/todos/`, `statsig/` and `logs/` are legacy, and Claude Code no longer writes to them. Tooling
that reads them reads stale data. `CLAUDE_CONFIG_DIR` moves all of these paths.

Never commit `.claude/settings.local.json`, `CLAUDE.local.md`, `.credentials.json` or anything under
`~/.claude/`. One catch is that Claude Code adds `settings.local.json` to your global git excludes, not
to the repo's `.gitignore`. If you create the file by hand, you have to exclude it yourself. Transcripts
are also not encrypted at rest. If a tool reads a `.env`, its values end up in the session `.jsonl`.

## D. The most common silent misconfiguration {#d-the-most-common-silent-misconfiguration}

If a skill's description names a topic rather than an occasion, the skill never fires. It shows up in
`/skills`, loads and appears in the listing, and does nothing, with no error output. The docs say to fix
it by including "keywords users would naturally say."

Four more fail just as quietly:

1. Claude Code truncates skill descriptions without warning, or drops them. When the listing overflows
   its 1% budget, Claude Code "drops descriptions starting with the skills you invoke least." A skill
   that worked can stop working only because you added other skills.
2. If a skill's frontmatter is malformed, Claude Code loads the body with empty metadata. `/skill-name`
   still works, but auto-invocation stops.
3. `~/.claude.json` is not a settings file. Claude Code never reads `permissions`, `hooks` or `env`
   written there.
4. In `-p` and the SDK, the trust gate fails open in the wrong direction. Claude Code silently skips the
   repo's `permissions.allow`, but it still runs the repo's hooks, `env` and `.mcp.json` servers.

## What this page does not establish {#what-this-page-does-not-establish}

We list these because a named gap is more useful than a guess:

* We do not know whether Claude Code's CLAUDE.md walk stops at the repo root or continues to `/`. No
  current doc mentions "microcompact".
