8. Memory and Context
8.1 Three persistent stores
Claude Code has three distinct long-lived stores that survive across sessions:
| Store | Location | Who writes | Who reads | Purpose |
|---|---|---|---|---|
| Auto-memory | ~/.claude/projects/<slug>/memory/ |
The model, via a stop-hook fork | Every turn (via loadMemoryPrompt) |
Durable facts about user, feedback, project, references |
| CLAUDE.md | Repo / ~/.claude/ / /etc/claude-code/ |
Humans (mainly) | Every turn | Project rules, conventions, workflows |
| Session transcript | ~/.claude/sessions/<sessionId>/transcript.jsonl |
The system as messages arrive | --resume path |
Exact message history for resumption |
8.2 Auto-memory
Structure
~/.claude/projects/<project-slug>/memory/
├── MEMORY.md # Index, always loaded, capped at 200 lines / 25 KB
├── user_role.md # One memory per file
├── feedback_testing.md
├── project_merge_freeze.md
├── reference_linear_ingest.md
└── auto/ # Auto-extracted by session memory
└── <topic>.md
Each memory file has YAML frontmatter:
---
name: user role
description: user is a data scientist, currently focused on observability/logging
type: user
---
Body text here, optionally with **Why:** and **How to apply:** lines for feedback/project types.MEMORY.md is a curated index — not a memory itself. Entries are one line under ~150 characters: - [Title](file.md) — one-line hook.
Guards
- 200-line / 25 KB cap on
MEMORY.md(memdir/memdir.ts:35-38). If exceeded, line-truncated first (natural boundary) then byte-truncated at the last newline before the cap, with a warning appended (memdir/memdir.ts:57-103). - Directory exists guarantee.
ensureMemoryDirExists()runs once at prompt build; the model is told the directory exists so it doesn't waste a turn onmkdirorls.
Four types, four discipline levels (memdir/memoryTypes.ts)
| Type | When it's valuable | Body structure |
|---|---|---|
user |
User role, knowledge level, preferences | Free-form |
feedback |
Corrections AND confirmations from user | Rule + **Why:** + **How to apply:** |
project |
Who/what/when about the work | Fact + **Why:** + **How to apply:** |
reference |
External system pointers (Linear, Grafana, Slack) | Free-form |
The "**Why:** + **How to apply:**" scaffolding is the single cleverest part of this prompt: it forces the model to capture the reason behind a rule, which later lets it judge whether the rule applies in an edge case (or whether the rule is now stale).
When NOT to save
The exclusion list (reproduced in doc 07) bans saving anything derivable from the code itself — patterns, architecture, file paths, git history, debugging solutions. The explicit clause is:
These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping.
This resists the failure mode where users treat memory as scratchpad.
Recall-side drift
A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it: if the memory names a file path, check the file exists. If the memory names a function or flag, grep for it.
The recall prompt explicitly flags that git log is authoritative for recent change history and that code is authoritative for current state — memory should be verified when about to act on.
Extraction path (services/extractMemories/)
Auto-memory extraction runs as a Stop hook. When the model finishes a turn without tool calls:
- Check token delta and tool-call count since last extraction (gate in
services/SessionMemory/). - Build an extraction prompt (
buildExtractCombinedPrompt/buildExtractAutoOnlyPrompt). - Spawn a perfect fork (shares cache) via
runForkedAgent(). - Fork uses FileWrite / FileRead / FileEdit to update
MEMORY.mdand topic files. - Log metrics (tokens, count of memories added/updated).
Session memory (services/SessionMemory/)
A separate, background track: periodic forked subagent extracts highlights to a session-scoped transcript. Gated by shouldExtractMemory() with config DEFAULT_SESSION_MEMORY_CONFIG (token threshold + tool-call count since last run).
8.3 CLAUDE.md — utils/claudemd.ts
Project rules, loaded every turn (cached until /clear or /compact). Priority order (reverse applied so closest wins):
/etc/claude-code/CLAUDE.md— managed by the org, global across users.~/.claude/CLAUDE.md— user, all projects.CLAUDE.md,.claude/CLAUDE.md,.claude/rules/*.md— checked into the repo.CLAUDE.local.md— per-project private.
Supports @include path directives for cross-file composition.
Nested CLAUDE.md
When FileReadTool reads a file inside a directory that has a CLAUDE.md sibling, it records a nestedMemoryAttachmentTriggers entry (tools/FileReadTool/FileReadTool.ts:27). On the next turn, that CLAUDE.md is auto-attached so the agent picks up local rules without the user having to ask.
Why read-only agents skip it
omitClaudeMd: true on BuiltInAgentDefinition (loadAgentsDir.ts:128-132). The Explore and Plan agents don't write, so the commit / PR / lint guidance in CLAUDE.md is noise. Dropping it saves tokens.
Cached break-cycle
bootstrap/state.ts holds cachedClaudeMdContent to break a load-order cycle: yoloClassifier → claudemd → filesystem → permissions → yoloClassifier. Without the cache, the permission system can't decide whether to allow the claudemd read because its own evaluation needs the claudemd content.
8.4 Context injection points
System prompt
Composed once per turn (cached sections unless mutated). See doc 04 §4.5 for the full structure.
User context (prepended to conversation)
utils/queryContext.ts:fetchSystemPromptParts returns a userContext dict. The keys are flattened into a user-role message that includes:
Working directory: <cwd>Is directory a git repo: Yes/NoGit status:(if repo)Git branch:Recent commits:(limited count)- Additional working directories
- Platform info
- Model description
- Knowledge cutoff
See constants/prompts.ts:606-643 for the exact <env> block format.
Attachments (per-turn <system-reminder> messages)
Pushed on specific triggers:
- Nested CLAUDE.md: when Read lands on a directory with
CLAUDE.md. - Skills relevant to your task: if
EXPERIMENTAL_SKILL_SEARCHis on, surfaced on each turn after a hybrid scoring. - Agent list: if
tengu_agent_list_attachis on — agent list moves out of the tool description (tools/AgentTool/prompt.ts:59-64) to keep the schema block stable. - Deferred tool announcements: list of
defer_loadingtools appears in reminders; the model must callToolSearchto fetch schemas. - MCP instructions delta: when MCP servers connect/disconnect mid-session, persisted deltas keep the static prefix cacheable.
Function Result Clearing (FRC) and summarize-tool-results
Two late dynamic sections (constants/prompts.ts:522-526):
getFunctionResultClearingSection(model)— instructs the model about the "clear old tool results" API beta if enabled.SUMMARIZE_TOOL_RESULTS_SECTION— tells the model to summarize long tool results rather than dumping them.
Memory prompt body
loadMemoryPrompt() returns the composed text the model sees about how to use memory: the four types, the "what not to save" section, the "when to access" guidance, and the "before recommending from memory" drift caveat. Also includes MEMORY.md content and relevant topic files selected by findRelevantMemories().
8.5 File history (utils/fileHistory.ts)
Every FileEditTool / FileWriteTool call records a snapshot so /rewind can restore. Persisted as fileHistoryState in AppState and serialized into the session log. Not in the prompt — purely client-side recovery.
8.6 Attribution (utils/commitAttribution.ts, utils/attribution.ts)
Tracks whether commit messages should be co-authored by Claude and which exact suffix to use. Derived from settings and fed into the git-commit prompt section (tools/BashTool/prompt.ts:79-80 for external; ant-only variants configure differently). Under certain modes (e.g. undercover, ant internal) the attribution is suppressed or altered.
8.7 The read-file state cache (utils/fileStateCache.ts)
Per-conversation FileStateCache tracks every file Read has visited. Used by:
- FileEditTool to enforce read-before-write.
- Compaction — captures pre-compact state, clears on compaction boundary, and is used post-compact to decide which files to re-attach.
- Subagent forking — cloned into the child so edits made in a fork don't corrupt the parent's view.
This is the mechanism that prevents the most common LLM-editor failure mode: editing a file based on a stale cache.