- Book: LLM Observability Pocket Guide
- Also by me: Thinking in Go (2-book series) — Complete Guide to Go Programming + Hexagonal Architecture in Go
- My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools
- Me: xgabriel.com | GitHub
Claude Code's prompt-caching docs have one sentence you should read before you open five agents in five worktrees: "In Claude Code, the cache is effectively scoped to one machine and directory" (Cache scope).
A git worktree is a different directory. So five sessions on five branches of one repo don't share a cache the way two terminals in the same folder do. Each one pays to write its own copy of the start of the conversation.
You can check this on your own disk. Claude Code writes the token counts of every API response to a transcript, and one jq query shows how much of each session's first request came from the cache and how much was written fresh.
How the cached prefix is built
Anthropic's cache matches a prefix. The API reads a request in a fixed order, tools, then system, then messages, and "cache hits require 100 percent identical prompt segments" up to the cache breakpoint (prompt caching docs). If one byte differs, the API processes everything after it again.
Claude Code orders each request so the parts that rarely change come first (how Claude Code uses prompt caching):
| Layer | Content | Changes when |
|---|---|---|
| System prompt | Core instructions, tool definitions | The set of loaded tools changes |
| Project context | CLAUDE.md, auto memory, unscoped rules | Session start, /clear, /compact
|
| Conversation | Your messages, Claude's replies, tool results | Every turn |
Two details in the docs settle the worktree question.
First, the system prompt embeds your auto memory location. Auto memory lives at ~/.claude/projects/<project>/memory/, and that path comes from the git repository, so "all worktrees and subdirectories within the same repo share one auto memory directory" (memory docs). Worktrees of one repo can send the same system prompt. Separate clones can't, because the default location is "named for the repository's path on disk, so it differs across users, machines, and checkouts" (Agent SDK docs).
Second, the environment travels in the conversation. CLAUDE.md content and "environment details such as the working directory, platform, shell, and OS version" go in the conversation, not the system prompt (same Agent SDK page). The conversation also carries "the branch and recent commits" from a git status snapshot taken at startup (Cache scope).
Put those together. Take two worktrees of one repo on the same machine, with the same Claude Code version and model. The tool definitions and the system prompt can match byte for byte. Then the first message names a different working directory, and the two prefixes split from that point on.

Worktrees of one repo can share the head of the request. The working directory is where they split.
What else splits the prefix, and what a split costs
The directory isn't the only thing that splits it. The same docs list more:
- Model. "Each model has its own cache" (switching models). On most models, each effort level does too. On Opus 5.5, Sonnet 5.5, Haiku 5.5 and Fable 5.1, with an API key or a Claude subscription, changing effort keeps the cache (changing effort level).
- Speed. Fast mode and standard speed keep separate caches (fast mode).
- Timing. "A cache entry only becomes available after the first response starts" (API docs). Start five sessions in the same second and every one of them writes, even in the same folder. That's why Claude Code holds all but the first agent of a same-prefix workflow fan-out "for up to 5 seconds by default" (subagents and the cache).
- Account. "Caches are isolated between organizations," and on the Claude API, between workspaces too (API docs).
- Version. "A new Claude Code version typically updates the system prompt or tool definitions" (upgrading).
Here's why a split matters. Per the API pricing rules, a cache write costs 1.25 times the base input price with the five-minute TTL and 2 times with the one-hour TTL. A cache read costs 0.1 times on most models, 0.05 times on Opus 5.5 and Sonnet 5.5, and 0.025 times on Fable 5.1. So on Opus 5.5, each token your new worktree writes costs 25 times what reading it would have (1.25 / 0.05), or 40 times on the one-hour TTL.
The split premium is paid once per session, on the part of the first request that wasn't already cached. After that, each session reads from its own cache turn after turn. Which TTL you get depends on how you sign in. On a Claude subscription, within plan usage, the main conversation gets one hour. With an API key it gets five minutes (TTL table). On a subscription, the cost shows up as plan usage, not dollars.
Measure it: each session's first request
Set up one repo with a second worktree:
cd ~/code/app
git worktree add ../app-b -b cache-test
Open three terminals. In the first, start claude in ~/code/app and send a short prompt. Wait until the answer starts. In the second, start claude in the same folder and send the same prompt. In the third, start it in ~/code/app-b. Finish all three within five minutes so even the short TTL holds. Then run the queries below from a plain shell, not from inside a Claude Code session, so a fourth transcript doesn't become the newest.
Claude Code stores transcripts at ~/.claude/projects/<project>/<session-id>.jsonl, where <project> is the working directory path with non-alphanumeric characters replaced by - (sessions docs). Each worktree gets its own folder, so the folder name tells you where a session ran. This prints the first API response of your three newest sessions:
for f in $(ls -t ~/.claude/projects/*/*.jsonl | head -3); do
jq -c -s --arg dir "$(basename "$(dirname "$f")")" '
map(select(.type == "assistant" and .message.usage))
| first // empty
| {dir: $dir,
model: .message.model,
t: .timestamp,
write: .message.usage.cache_creation_input_tokens,
read: .message.usage.cache_read_input_tokens,
fresh: .message.usage.input_tokens}
' "$f"
done
In current versions, the first assistant entry in a file is normally the session's first API response. One response can be written as several entries, all with the same message.id and the same usage. A Claude Code collaborator called that shape "intentional and documented" (issue #87303), so for the first request, the first entry is enough. The model field flags a session that ran on a different model.
Compare read with write. The second session in the same folder should report most of its first request as read, since the docs say parallel sessions in one directory "build matching prefixes and can read each other's cache" (Cache scope). The worktree session should show a larger write than that second session, since everything from the working-directory line on is written fresh. Its read is likely the tools and system prompt it shared with the others, so how the two split depends on how big those are next to your CLAUDE.md and first message. Check t as well: two sessions that started within a second of each other can both show a large write, even in one folder.
I haven't run this myself, and I won't guess sizes for you: your CLAUDE.md, MCP servers and Claude Code version all change them. The docs also warn that the entry format "is internal to Claude Code and changes between versions" (sessions docs). And jq -s stops at the first line it can't parse.
Session totals, each message id once
To see the whole session, count each message.id once, or cache tokens get counted several times. I covered why in Summing Claude Code's Transcript Usage.
for f in $(ls -t ~/.claude/projects/*/*.jsonl | head -3); do
jq -c -s --arg dir "$(basename "$(dirname "$f")")" '
map(select(.type == "assistant" and .message.usage))
| group_by(.message.id) | map(.[0].message.usage)
| {dir: $dir, requests: length,
write: (map(.cache_creation_input_tokens // 0) | add),
read: (map(.cache_read_input_tokens // 0) | add),
fresh: (map(.input_tokens // 0) | add)}
' "$f"
done
Total input is read + write + fresh (API docs), so read divided by that total is your cache share. Inside Claude Code, /usage shows the same thing as a Prompt cache (main) line: the share of input served from cache, the misses, and from v2.1.260 a likely cause for the last miss (costs docs). That line covers the main conversation only. The jq totals cover whatever assistant entries the file holds.
What you can control
- Keep the head identical. Run the same Claude Code version in every session, and restart all of them after an upgrade. Use the same model and speed everywhere.
- Prefer worktrees to clones. Worktrees of one repo share the auto memory location, so they can share the system prompt. Clones in different paths can't.
- Stagger launches. Start the next session only after the first one's response has begun.
For scripted runs across machines or clones, claude -p --exclude-dynamic-system-prompt-sections moves the auto memory location "out of the system prompt and into the first user message" (CLI reference). Back-to-back sessions in one folder share a prefix only when the git status snapshot matches. includeGitInstructions: false or CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS=1 drops the snapshot, but it also drops the built-in commit and PR instructions (Agent SDK docs).
One thing you can't change: the working directory line. A worktree's conversation layer belongs to that worktree, and every worktree session writes that layer fresh on its first request.
Checking it against Hermes' per-session spend
I build Hermes IDE, so weigh this section with that in mind. In Hermes, ⌘N (Ctrl+Shift+N on Windows and Linux) starts each task on its own hermes/<name> branch in its own worktree. By the docs above, that means every Claude Code task in Hermes builds its own conversation prefix. I'd rather pay one cache write per task than have two agents editing the same checkout, but you should know the trade exists.
In the screenshot below, the sidebar lists each session's status, context use, cost and branch. The cost is the one the agent reports, or an estimate from its transcript marked as estimated, with token totals (README). Since 2.2.9, each session's usage is counted once.

Two agent sessions side by side, each in its own hermes/ worktree, with status, context use, cost and branch in the sidebar.
To check, run the totals query on the same sessions and compare it with what Hermes shows. The transcript splits the tokens into reads and writes; Hermes shows what each session spent. If one task's spend is out of line with similar tasks, run the first-request query to see whether a cold first request is part of the reason. If the transcript and Hermes disagree, open an issue.

Claude Code's /context breakdown, the sidebar's context gauges, and the status strip with the token total and estimated cost.
The dollar figures are estimates anywhere you look. Claude Code computes them locally at list price by default (costs docs), and on a subscription they aren't what you're billed. For tokens, the transcript is the record.
If this was useful
Hermes IDE is free, source-available under BSL 1.1, and runs on macOS, Windows and Linux. Download it, give two tasks to Claude Code with ⌘N (Ctrl+Shift+N on Windows and Linux), and run the queries above on both sessions. If it's useful to you, star the repo so other developers find it. Hermes IDE is not affiliated with Nous Research, Anthropic or any agent vendor.
Reading token usage out of a transcript is LLM observability in miniature: you get the raw counts, and you have to know what each one means before you trust a total. The LLM Observability Pocket Guide covers that work at team scale, including tracing, evals, and which tools fit, so cache reads and writes end up on a dashboard instead of in a shell loop.

Top comments (0)