CLAUDE.md
loads on every Claude Code session: trim it to a static bootstrap, split memory by volatility, and compile .kb/
in byte-stable order so multi-turn Key-Value (KV) prefix hits survive.
Why
/doctor
and the memory docs now treat longCLAUDE.md
files as a liability, not a badge of rigor.Five modernization steps: diagnose, offload, dateless model IDs, prune generic standards, condense memory sync rules.
Prefix caching mechanics: left-to-right hash match, block boundaries, and why one byte change at token N invalidates everything after N.
Local failure modes: dynamic headers, shuffled file order, CRLF vs LF, trailing whitespace.
Volatility hierarchy: keep
CLAUDE.md
static; put architecture in low-churn files; append session logs at the bottom.Production scripts:
build-context.sh
with alphabeticalLC_ALL=C
sort, LF normalization, and a prefix-integrity audit.Repo safety net: pre-commit CRLF fix plus
.gitattributes
eol=lf
for memory markdown.Session automation: real
.claude/settings.json
SessionStart
hooks, plus amake claude
wrapper; verification order and production takeaways.
Claude Code loads project memory into the context window at session start. Anthropic's memory guidance targets under about 200 lines per CLAUDE.md
, prefers path-scoped rules for specialized guidance, and uses /doctor
(v2.1.206+) to propose trims for checked-in content the model can derive from the codebase. See the official pages on memory, best practices, and configuration diagnostics.
Prefix caching is a separate constraint. Anthropic's prompt cache keys on the exact rendered prefix up to each breakpoint. One changed byte before a breakpoint invalidates every later cached segment. Put stable instructions first; put volatile content last. The production guide is Prompt caching.
Figure 1 shows the session prefix Claude Code effectively assembles: system and tool definitions, then static project memory, then conversation history, then the current user delta. Cache reuse only holds while the left side stays byte-identical across turns.
Figure 1: Claude Code session prefix order
Run /doctor
inside the repository first. Current Claude Code builds treat /doctor
as an actionable checkup: installation health, unused extensions, duplicate local memory against checked-in files, slow hooks, and (from v2.1.206) proposals to trim checked-in CLAUDE.md
material Claude can recover by reading the tree. Confirm before applying any proposed edit.
Next, offload non-essential rules. Deep tool manuals, environment bootstrap essays, and architecture tours belong in docs/
, ARCHITECTURE.md
, or path-scoped .claude/rules/
files. Keep the root bootstrap file limited to what must apply on every turn: build/test commands, non-default conventions, and project-specific workflow gates.
Update model references. Prefer family aliases (sonnet
, haiku
) or dateless major IDs such as claude-sonnet-5
where your provider documents them. Dated snapshot IDs pin weights; convenience aliases for older generations can move. The authoritative maps live in Claude Code model configuration and model IDs and versions.
Prune implicit coding standards. Lines such as "always handle errors gracefully" or "use descriptive variable names" burn prefix tokens and dilute the few rules that actually differ from model defaults. Keep rules that encode your team's feature loop, review gates, and forbidden operations.
Condense memory tracking. Replace multi-paragraph logging liturgies for AUDIT.md
, progress-report.md
, or USERS.md
with one outcome rule: after code changes, update the tracking files so they match the tree. Specificity belongs in a short template at the bottom of each tracking file, not in the always-on bootstrap.
Figure 2 orders those five steps as a single maintenance loop you can run after every model or harness upgrade.
Figure 2: Five-step CLAUDE.md modernization loop
When the client sends a turn, the backend sees one structured token sequence. Conceptually:
[System / tools] → [CLAUDE.md + static docs] → [Conversation history] → [Current input / code delta]
The inference stack scans left to right and looks up whether the cryptographic hash of the prefix matches a recent precomputed KV matrix. On a hit through token N, the engine reuses attention states for those N tokens instead of recomputing them, which cuts time-to-first-token for the shared prefix.
Providers allocate cache state on token-block boundaries (commonly discussed in 1024-token chunks for many serving stacks; Anthropic also documents model-dependent minimum cacheable prefix lengths). If the first 5,000 tokens of project instructions stay identical, early blocks hit. If token 4,100 changes (a timestamp, a reordered file, a CRLF insertion), the block that contains that token misses, and every later block must recompute because attention depends on all prior tokens.
Figure 3 shows that failure: a one-token edit in block 5 collapses reuse for blocks 5 through the end of the request.
Figure 3: Block boundary miss after a mid-prefix edit
Three local workflow bugs destroy that prefix even when the prose "looks the same":
Dynamic injections at the head of memory (dates, git branch, coverage percentages, "Last updated" stamps).
Non-deterministic file order when a script concatenates
.kb/*.md
without a fixed locale sort.Line-ending drift (
\n
vs\r\n
) or trailing whitespace changes that alter tokenizer IDs without changing visible text.
Figure 4 maps those three failure modes onto the same prefix timeline.
Figure 4: Three local prefix-matching failure modes
Do not pack bootstrap rules, architecture, and live session notes into one file. Isolate change rates:
Table 1 assigns each memory surface a volatility class and the cache behavior you should expect.
Table 1: Memory file volatility and cache behavior
Figure 5 restates the hierarchy as a stack: static head stays hot; high-churn logs live only at the bottom of domain-specific files.
Figure 5: Volatility stack for CLAUDE.md and .kb memory
Pin formatting with a repo-wide LF policy. Never put incrementing build versions or wall-clock stamps in the first lines of a memory file. If you need session metadata, append a trajectory entry at the end.
The production pattern is a compiler that (1) audits .kb/
for CRLF and volatile headers, (2) compares raw discovery order against LC_ALL=C
sort, and (3) emits one LF-normalized artifact. Keep warnings on stderr so a SessionStart hook does not inject noisy audit text into the model context.
Audit first. This fragment scans each .kb/*.md
file, flags CRLF, and warns when volatile header keys sit in the first five lines:
set -euo pipefail
KB_DIR=".kb"; OUT="scripts/.compiled_context.md"
RED=$'\033[0;31m'; YEL=$'\033[0;33m'; NC=$'\033[0m'
mkdir -p "$(dirname "$OUT")"
while IFS= read -r file; do
grep -q $'\r' "$file" && echo -e "${RED}CRLF:${NC} $file" >&2
if head -n 5 "$file" | grep -Ei '(date:|time:|updated:|version:)'; then
echo -e "${YEL}volatile header:${NC} $file" >&2
fi
done < <(find "$KB_DIR" -maxdepth 1 -name "*.md" | LC_ALL=C sort)
Then compile. The same sorted list is concatenated with trailing spaces stripped and carriage returns removed so the artifact is LF-only:
{
echo "<!-- GENERATED STATIC CONTEXT - DO NOT EDIT DIRECTLY -->"
find "$KB_DIR" -maxdepth 1 -name "*.md" | LC_ALL=C sort | while IFS= read -r f; do
echo -e "\n<!-- START FILE: $f -->"
sed 's/[[:space:]]*$//' "$f" | tr -d '\r'
echo -e "\n<!-- END FILE: $f -->"
done
} > "$OUT"
echo "OK: wrote $OUT" >&2
Compare unsorted discovery order to the C-locale sort with two md5sum
hashes when you want an explicit shuffle warning. The Layer-2 guard is the volatile-header scan: date:
/ version:
in the first five lines threaten early cache blocks even when the rest of the file is stable.
Wire the artifact into the static prefix with an import or an explicit include from CLAUDE.md
, or have a SessionStart hook print only a short hash confirmation while Claude reads the committed memory files directly. Do not dump the entire compiled blob through SessionStart stdout on every turn unless you intend that blob to become conversation context.
.gitattributes
Hooks only protect machines that install them. Still use a local pre-commit to normalize staged memory files, and commit .gitattributes
so every clone checks out LF.
set -euo pipefail
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM \
| grep -E '^(\.kb/|CLAUDE\.md$)' || true)
[ -z "$STAGED_FILES" ] && exit 0
while IFS= read -r file; do
[ -f "$file" ] || continue
if grep -q $'\r' "$file"; then
echo "Normalizing CRLF → LF: $file"
# portable LF rewrite without GNU-only sed -i assumptions
tmp=$(mktemp)
tr -d '\r' < "$file" > "$tmp" && mv "$tmp" "$file"
git add "$file"
fi
done <<< "$STAGED_FILES"
Mark the hook executable with chmod +x .git/hooks/pre-commit
. Add repository policy:
CLAUDE.md text eol=lf
*.md text eol=lf
.kb/**/*.md text eol=lf
Figure 6 shows the enforcement chain: compiler audit → LF lock → SessionStart or Makefile entrypoint.
Figure 6: Compiler, Git LF lock, and SessionStart chain
Several circulating snippets invent fields such as autoExecuteHooks
, onSessionStart
, and allowList
under a ~/.config/claude-code/settings.json
path. Claude Code does not use that schema. User settings live at ~/.claude/settings.json
(on Windows, %USERPROFILE%\.claude\settings.json
). Shared project settings live at .claude/settings.json
. Hooks nest under the documented hooks
object. See Settings and Hooks.
Use a SessionStart
command hook with matcher startup
(add resume
/ compact
if you need re-compile after those events):
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "bash scripts/build-context.sh >/dev/null"
}
]
}
]
}
}
Stdout from SessionStart commands is injected into Claude's context. That is why the compiler prints audits to stderr and why the hook redirects stdout to /dev/null
after writing scripts/.compiled_context.md
on disk. Keep SessionStart hooks fast; Anthropic documents them as startup/resume automation, not as a place for heavyweight builds.
For a belt-and-suspenders local workflow that does not depend on hooks:
.PHONY: claude
claude:
@bash scripts/build-context.sh
@claude
Verification order on every cold start:
Alphabetical
LC_ALL=C
ordering of.kb/*.md
.LF normalization and trailing-space strip.
Clean overwrite of
scripts/.compiled_context.md
.Claude Code launch with a predictable static prefix ahead of the growing conversation tail.
Optional hardening: emit a SHA-256 of the compiled file to stderr and fail CI if the hash drifts when only whitespace or file order changed in the source tree.
Treat
CLAUDE.md
as a cold, short bootstrap. Target the official under-200-line guidance and let/doctor
propose trims for discoverable content.Offload tool manuals and architecture into docs or path-scoped rules so the always-on prefix stays small and stable.
Prefer documented model aliases or dateless major IDs over hard-coded dated strings that force rule churn.
Delete generic coding platitudes; keep only project-specific workflow and safety rules.
Split memory by volatility: static head, low-churn architecture, append-only session logs at the bottom.
Prefix caching is exact-byte matching. One mid-prefix edit, CRLF drift, or shuffled concat order invalidates every later block.
Ship
build-context.sh
with C-locale sort, LF rewrite, volatile-header warnings on stderr, and a single generated artifact.Enforce LF with a pre-commit hook plus
.gitattributes
; do not rely on hooks alone for teammates and CI agents.Automate with real
SessionStart
hooks in.claude/settings.json
or amake claude
wrapper. Do not copy inventedautoExecuteHooks
/onSessionStart
schemas.Keep SessionStart stdout quiet; write compiled context to disk and load it through the static memory path you control.
Anthropic*Manage Claude's memory.Claude Code Docs.code.claude.com/docs/en/memory.AnthropicBest practices for Claude Code.Claude Code Docs.code.claude.com/docs/en/best-practices.AnthropicDebug your configuration (/doctor, /context).Claude Code Docs.code.claude.com/docs/en/debug-your-config.AnthropicModel configuration.Claude Code Docs.code.claude.com/docs/en/model-config.AnthropicModel IDs and versions.Claude API Docs.platform.claude.com/.../model-ids-and-versions.AnthropicPrompt caching.Claude API Docs.platform.claude.com/.../prompt-caching.AnthropicClaude Code settings.Claude Code Docs.code.claude.com/docs/en/settings.AnthropicHooks reference.Claude Code Docs.code.claude.com/docs/en/hooks.AnthropicAutomate actions with hooks.*Claude Code Docs.code.claude.com/docs/en/hooks-guide.