anthropics / anthropics/claude-code

CLAUDE.md / auto-memory block is never shared across sessions: it sits in messages[0] after the system breakpoint, with no cache_control of its own

Open
#94,417 0 comments 0 reactions 0 assignees View on GitHub
area:core bug has repro
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

## Summary

The prompt-caching docs and the "Prompt caching is everything" post describe project context (CLAUDE.md, auto memory) as its own cached layer, shared by parallel sessions in the same directory. In practice the block is emitted as the first content block of the first user message, with the user's prompt as a later block of that same message, and the next `cache_control` marker is on a session-context message that contains session-specific text. No request ever writes a cache entry ending at the CLAUDE.md block, so no other session can read it. Measured over 703 session starts: zero cross-session hits beyond the system prompt layer.

## Environment

- Claude Code 2.1.270 (also observed on 2.1.266), Windows 11, Opus 5, Claude subscription (1h TTL on the main conversation)
- Both `cc_entrypoint=cli` and the desktop app (`claude-desktop`)
- Tool search enabled (18 tools in the prefix, rest deferred)

## What the docs say

`code.claude.com/docs/en/prompt-caching`, layer table: *System prompt → Project context (CLAUDE.md, auto memory, unscoped rules) → Conversation*, and under Cache scope: *"Sessions you run in parallel in the same directory build matching prefixes and read each other's cache."*

Blog: *"Static system prompt & Tools (globally cached), CLAUDE.md (cached within a project), Session context (cached within a session), Conversation messages"*.

## What the request actually looks like

Captured with a local pass-through proxy (`ANTHROPIC_BASE_URL`), request body of the first turn of a fresh session:

```
tools[18] (no cache_control; DeferredToolPlaceholder present)
system[0] billing header
system[1] "You are Claude Code, ..."
system[2] core instructions cache_control
system[3] static instructions + memory path cache_control <- per-directory (auto memory path)
messages[0] role=user
[0] CLAUDE.md (global + project) + MEMORY.md
[1] userEmail
[2] commit attribution
[3..] the user's prompt (or /command + skill body)
messages[1] role=system cache_control
SessionStart hook output, Environment block (cwd, OS, and
"Scratchpad directory: ...\\scratchpad"),
deferred tool list, agent list, MCP instructions, skill list, date
```

So between the last system breakpoint and the next one there are: the CLAUDE.md block, the user's first prompt, and a block with a per-session UUID and hook output that includes elapsed time ("há 0.2 h"). The prefix ending at `messages[1]` can never match another session's, and nothing ever writes an entry that ends right after `messages[0].content[0]`.

## Measurement

For every session JSONL under `~/.claude/projects/*/`, take the first main-conversation assistant message and its `usage`, and the time gap to the most recent request of any *other* session in the same directory. 703 sessions, 474 of them started while another session in the same directory had been active within the last 60 minutes.

| `cache_read_input_tokens` on the first call | where it shows up |
|---|---|
| 46,328 | every directory, including a directory's first session ever and one whose previous session was 1,303 min earlier (another directory had kept the layer warm) |
| 49,760 / 49,769 / 50,595 / 52,548 | constant per directory (`system[3]` differs only by the auto-memory path) |
| anything larger | only on `--resume` (full history hit) |

Zero of the 474 warm starts read beyond the `system[3]` layer. `cache_creation_input_tokens` on those first calls is 23–36k, of which the CLAUDE.md+MEMORY block is roughly 4–7k tokens that would be byte-identical between sessions of the same directory.

## Expected

Parallel sessions in the same directory read the CLAUDE.md / auto-memory block from cache, as documented.

## Possible fixes

1. Put a `cache_control` marker on the CLAUDE.md block (`messages[0].content[0]`), or
2. Move the block to `system[4]` with its own marker, ahead of the first user prompt.

Either way the session-context message (`messages[1]`) stays per-session, which matches what the blog already says about it. If neither is intended, the doc's layer table and the "read each other's cache" sentence should say that only the system prompt and tool definitions are shared.

## Cost note

For one user this is small: ~4–7k tokens rewritten at the 1h write rate once per session. The reason to report it is that the documented design and the emitted request differ, and the `/usage` "likely cause" logic and the docs both reason from the documented layout.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.