Andrej Karpathy’s LLM Wiki gist from April is right about the important part: for personal knowledge, a markdown wiki that Claude maintains beats a RAG pipeline. I’ve been running a version of this pattern in production against my own life for six months. Karpathy is right — and he stopped at exactly the point where the interesting problems start.
Here’s the pattern, briefly, then what actually breaks.
The claim, in one paragraph
You keep raw sources in one directory. You keep a wiki of markdown pages in another. A CLAUDE.md file tells Claude how the wiki works: naming, folder layout, what to do on ingest, how to handle contradictions. New source comes in, Claude reads it, writes or updates concept pages, links them with [[wikilinks]], updates an index. When you ask a question, Claude reads the wiki, not the raw sources — the synthesis has already been done at ingest time. The whole thing sits under ~100k tokens, so Claude can hold the index in context and reason over it directly. No embeddings, no vector store, no infrastructure. Two clean community implementations to start from: nvk/llm-wiki and Ar9av/obsidian-wiki.
That’s the whole thing. It works. It works better than you’d expect. And every problem I’ve hit with it shows up in the same three places.
My setup, so the failure modes are grounded
Mine is a NEXUS workspace: an Obsidian vault split into 000-inbox/, 200-knowledge/, 300-entities/, 400-daily/, plus per-domain context files under 200-knowledge/context/ — one each for finance, health, content, jobsearch. Every domain has its own schema and its own conventions. A top-level CLAUDE.md sets the operating rules; each domain file specializes them. Entities (people, companies, services, properties) are single markdown files named by their wikilink text, so [[Janney]] grep-searches straight to vault/300-entities/orgs/Janney.md. Daily logs at vault/400-daily/YYYY-MM-DD.md are the append-only session record. A MEMORY.md at the root holds hard-won lessons — 34 entries and counting.
Volume: several hundred wiki pages, four active domains, daily writes going back to February. Well past Karpathy’s toy scale, well under any RAG-scale problem. This is the middle band the original gist never addresses.
Where it broke: three failure modes, all the same shape
1. Drift between the “recent changes” section and the “how it works” section of the same file.
The gist assumes ingest and update. It doesn’t say what happens when something you documented in April is contradicted by what you did in July. In practice, Claude appends a new dated entry to a changelog at the top of the file, feels done, and leaves the reference section below still describing the dead system as current. A reader — human or agent — trusts the reference section for “how does this work today,” and it’s wrong. Writing a dated log entry feels like recording the change, so the present-tense prose that people actually read for “how does this work now” never gets revisited. The rule I now enforce: a migration isn’t done when the changelog says so; it’s done when the reference sections match.
2. Silent contradictions between pages.
Two entity cards for the same account — one calls the institution “Janney,” the other calls it “Janney Montgomery Scott LLC” — and a downstream aggregation double-counts because the dedup key is the name. RAG failures are silent because retrieval quietly misses a chunk. Wiki failures are silent for a different reason: two pages both exist, both are internally consistent, and neither knows about the other. Claude doesn’t spontaneously notice.
3. Orphan pages and dead wikilinks accumulate faster than lint catches them.
Karpathy’s original suggests running lint occasionally. At three hundred pages, “occasionally” is not a frequency — it’s a hope. Weekly lint recovers most of it. Nothing recovers a stale page that nobody links to and nobody re-reads.
All three are the same shape: the wiki has no immune system against its own past. It compounds knowledge, but it also compounds staleness, and it needs help distinguishing the two.
Three things I bolted on
A drift linter that fails the build. A Python check that walks the vault, cross-references any “retired” or “decommissioned” term against every file, and flags live mentions of dead systems in the canonical docs. It runs pre-commit and on a scheduled cron. When Ghost got replaced by an Astro static site, the linter caught nine files still describing the Ghost publish path as current — including the top-level project README. Without it, a reader would have reached for dead credentials.
Per-domain sub-schemas, not one giant CLAUDE.md. The gist treats the schema as one file. That doesn’t survive the second domain. Finance and health have almost no overlap in vocabulary, conventions, or what a “page” is. My top-level CLAUDE.md sets universal rules (voice, safety, wikilink format, verification protocol). Each domain has its own <domain>-context.md that specializes them. A session working on finance loads the domain file; a session working on content loads a different one. Same architecture as scoped CSS.
A vector index alongside the wiki, not replacing it. Once I passed roughly 100k tokens of wiki content plus 100k tokens of session logs plus tens of thousands of legacy conversations, Claude couldn’t hold the index in context anymore. The gist’s answer to this is “at that point, use RAG.” My answer is: keep the wiki as the authored, curated layer, and add a pgvector index over the whole corpus — vault plus session history plus legacy archives — for recall. Around 126k chunks on Postgres 17 with mxbai-embed-large embeddings running on a Mac mini. Not a replacement for the wiki; a way to find the right wiki page when your brain forgot which slug you wrote it under two months ago. The RAG layer is a retrieval tool for me and my agents. The wiki is still the canonical knowledge.
Where the pattern still wins
For synthesis, entity resolution, and the “how do these three things I noticed relate” question, nothing else comes close. RAG genuinely doesn’t do it — the structural argument about retrieval having no accumulation is correct. When I ask “what’s the through-line across the last month of daily logs on finance,” the answer draws on synthesis pages that were written when each log came in. The work happened once, at ingest.
Entity cards are the other win. A single file per person or company, named by its wikilink text, becomes the source of truth. Everything else references it. When a fact about an entity changes, you change one file. This is the boring pattern that pays for itself every day.
Where it stops — the honest edge cases
The wiki pattern breaks when any of these are true:
- Multi-actor writes. If two agents write to the same wiki page concurrently, there is no lock. Mine is single-writer by convention. If you fan out coding agents that all edit
SESSION-STATE.md, you need a lockfile — the gist has nothing to say about this. - Recall across the boundary of a Claude session. A wiki isn’t memory. Claude reads it fresh each session. Cross-session memory (“what did we decide last Tuesday”) needs a separate store, which is what daily logs plus the vector index are for.
- Anything past ~100k tokens of hot wiki content. The load-bearing assumption is that the index fits in context. When it doesn’t, either you shard by domain (my approach), add a retrieval layer, or accept degraded answers.
- Anything anyone else needs to read without Claude in the loop. A markdown wiki is legible to humans. A markdown wiki written by Claude, for Claude, over months has enough house-style shorthand that a new reader without the schema will miss context. The wiki is legible; the accumulated conventions are not.
What to actually do
Start where Karpathy says. One folder, one schema, five sources, in one domain you’re actively thinking about. Do it for two weeks before adjusting anything.
At the point it starts feeling like it’s working — around thirty or forty pages — put three things in before you regret not having them: a drift check that flags dead terms in live docs, one lint pass a week that actually runs, and a rule that any migration updates the reference section, not just a changelog. That’s the operating discipline the pattern needs and doesn’t ship with.
Add a vector index only when the wiki genuinely stops fitting. Not before. Karpathy is right about that too — most people reach for RAG when they should have written a schema.