Blog
Claude /init and CLAUDE.md vs. Kognita: How Static Context Files Quietly Teach Your AI to Hallucinate
11 min read
You ran claude /init, you committed the CLAUDE.md it generated, and you assumed Claude was now grounded in your codebase. Then it confidently wrote a PR against an API you migrated off two months ago — no warning, no hedge, just clean-looking code that imports a module you deleted. The frustrating part is that it did exactly what you told it to. The file said the old thing was true, so it believed the old thing.
This is the failure mode nobody warns you about when they tell you to "just add a CLAUDE.md." The/init command and the file it produces are genuinely useful for getting Claude oriented. But they are a static document, not a live representation of your code — and on any team that ships continuously, a static document does not merely go out of date. It becomes a source of confident hallucinations.
What /init and CLAUDE.md actually are
It helps to be precise about the mechanics, because the marketing language of "giving Claude context about your codebase" hides what is really happening. /init is a one-time scan that produces one markdown file:
The /init -> CLAUDE.md pipeline
1. You run claude /init
2. Claude scans the repo once: structure, package files, conventions
3. It writes a CLAUDE.md and commits it to the repo root
4. From then on, that file is loaded into the system prompt every session
What it is NOT:
-> not a re-scan (it runs once, on demand)
-> not an index (no embeddings, no behavioral graph)
-> not self-updating (a human edits it, or it rots)
-> not aware the code moved under itThat is the whole model. Claude reads your repo once, summarizes what it saw into prose, and writes it to CLAUDE.md at the project root. From that point forward the file is loaded into the system prompt at the start of every session. There is no embedding store, no behavioral graph, no re-scan. The file reflects the codebase as it existed the moment you ran the command, and it stays frozen there until a human edits it.
Why a stale CLAUDE.md is worse than no CLAUDE.md
The intuition most teams start with is "some context is better than none." For grounding files, that intuition is backwards. A model with no CLAUDE.md falls back on reading the actual repository before it answers. A model with a wrong CLAUDE.md trusts the document it was handed and skips the verification step:
Missing context vs. wrong context
No CLAUDE.md:
-> Claude searches the live repo before answering
-> finds StripeClient, async_session, AuthV2
-> answer reflects current code
Stale CLAUDE.md:
-> Claude trusts the document it was handed
-> never re-checks the moved code
-> answers confidently against last quarter's API
-> no uncertainty flag, no "I should verify this"
Wrong context outranks no context. The model has no way
to know the file lying to it is out of date.This is the same dynamic we cover in the failure mode that's worse than hallucination: an agent that remembers wrong is more dangerous than one that admits it does not know, because wrong context arrives with the same confidence as right context. CLAUDE.md is loaded as authoritative system-prompt text. The model has no signal that the file is six weeks behind the code.
How drift turns into a hallucinated PR
Drift is not abstract. Here is a concrete timeline for a single service — a payments backend at a mid-size fintech — where the CLAUDE.md was accurate the day it was generated and quietly poisonous seven weeks later:
billing-service / CLAUDE.md drift
WEEK 0 claude /init runs. CLAUDE.md captures a TRUE snapshot:
payments -> stripe.Charge.create(amount=...) # legacy resource API
db write -> with db_session() as s: s.commit() # sync SQLAlchemy
auth -> verify_token(req) [app/auth/helpers.py]
service -> "billing-service"
WEEK 2 Stripe migration merged. CLAUDE.md unchanged.
payments -> client.charges.create(...) [StripeClient]
WEEK 4 Service goes async. CLAUDE.md unchanged.
db write -> async with async_session() as s: await s.commit()
WEEK 6 Rename + auth v1 -> v2. CLAUDE.md unchanged.
service -> "payments-core"
auth -> AuthV2.require_scope("payments:write")
WEEK 7 New engineer + Claude ship a refund endpoint.
Claude reads CLAUDE.md, trusts the week-0 snapshot, writes:
- stripe.Charge.create(...) -> AttributeError (legacy API gone)
- with db_session() as s -> symbol deleted, not awaited
- verify_token(req) -> import error, helper removed
- imports from "billing-service" -> package no longer exists
WHY IT SURVIVED 7 WEEKS:
every PR merged green -> every test passed -> nothing broke
a stale CLAUDE.md has no failing build -> no one was told to fix itNothing in that timeline is exotic. A Stripe SDK migration, a sync-to-async move, a service rename, an auth helper replaced — these are ordinary weeks on a healthy team. Each change merged green and passed its tests. None of them touched CLAUDE.md, because the file is not part of the build and breaking it breaks nothing. The cost lands later, on the engineer who trusted the agent that trusted the file.
The failure is silent by design
The reason CLAUDE.md drift is so persistent is that there is no trigger to fix it. A failing test tells you to fix the test. A type error stops the build. A stale grounding document produces neither — it just sits in the system prompt being subtly wrong. As we describe in CLAUDE.md team drift, the changes feel too minor to document when you make them, and no PR reviewer is checking a markdown file for factual accuracy against the diff. So it never gets updated, and the gap compounds with every merge.
At team scale this multiplies. Six repositories means six CLAUDE.md files, each at a different and unknowable level of staleness, each maintained by whoever last cared enough. There is no dashboard that says "this file is now lying." The first signal you get is a hallucinated PR.
It is not just CLAUDE.md
If you assume the fix is to switch tools, look at what the other tools offer: the same thing under a different filename. AGENTS.md, .cursorrules, and copilot-instructions.md are all hand-edited prose files that describe code which keeps moving:
Every static rules file shares the same defect
CLAUDE.md (Claude Code) -> hand-edited markdown, one repo
AGENTS.md (cross-tool) -> hand-edited markdown, one repo
.cursorrules (Cursor) -> hand-edited rules, one repo
copilot-instructions.md (Copilot) -> hand-edited markdown, one repo
Shared weakness:
-> written once, trusted indefinitely
-> no freshness guarantee, no update trigger
-> describe code in prose; the code keeps moving
-> none of them re-read the codebase on their ownWe unpack this pattern in why static context files can't keep up. The defect is structural, not specific to Anthropic's implementation. Any approach that asks a human to maintain a prose description of a living codebase inherits the same drift, and therefore the same hallucinations.
What changes with a managed semantic index
The fix is not a better-disciplined document — it is removing the document from the critical path. Instead of a human writing prose about the code, Kognita indexes the repositories directly from source and maintains a semantic representation that re-runs as the codebase changes. The grounding is derived from the current code, not from a file that recorded what the code looked like when someone last ran /init:
Grounding that maintains itself
CLAUDE.md (from /init):
Freshness -> as of the last manual edit
Scope -> one repo, one file
Form -> prose a human wrote about the code
On change -> someone must remember to edit it
Audience -> developers running Claude Code
Managed semantic index (Kognita):
Freshness -> re-indexed automatically as code merges
Scope -> all connected repos, cross-service
Form -> embeddings + behavioral relationships from source
On change -> the index re-runs; nothing to remember
Audience -> the whole team, via one managed connectionBecause the index is rebuilt automatically as code merges, there is no staleness problem and therefore no confident-wrong problem from drift — the representation reflects the renamed service, the async session, the v2 auth scope, without anyone remembering to write it down. Because it is semantic rather than textual, it captures behavioral relationships across services, not just a flat list of conventions one repo at a time. And because it is managed, the whole team queries the same current ground truth through one connection — no per-developer setup, no local index, no six files at six levels of decay. This is the difference between documentation and real context grounding: one requires humans to keep it accurate, the other does it automatically.
Where CLAUDE.md still earns its place
None of this means delete your CLAUDE.md. It is the right tool for the things that genuinely are stable: build and test commands, lint setup, team norms, architectural principles you want baked into every session. Those change rarely and benefit from being declared explicitly. The mistake is asking a static file to be the primary source of truth about current codebase structure — file locations, API surfaces, service boundaries — the things that move every week. That job belongs to an index, not a document. Run /init for conventions; ground the moving parts in something that stays current.
Final take
claude /init and the CLAUDE.md it produces are a fine starting line and a poor finish line. They get Claude oriented on day one at no maintenance cost — and then, on any team that ships, they quietly age into a document that tells the model confident, specific, out-of-date things about your code.
A stale CLAUDE.md never throws an error. It just teaches your AI to write last month's code with this month's confidence. The only durable fix is grounding that updates itself — an index, not a document.