RAG earned its place doing something real: finding the passage that means what you mean without using your words. Applied to a codebase, it's approximating something that's already exact — imports, symbol definitions, a call graph the parser can compute for free. Here's what that approximation costs, and the one place it's still the right tool.
← back to the overview
A call graph isn't a fuzzy concept living somewhere in embedding space — it's a fact the
language already states. import, a function call, a class reference: the parser
reads the answer directly. RAG doesn't get to read it directly. It chunks the text, embeds the
chunks, and asks a vector store which ones sound related.
Tree-sitter reads the file once. Imports, exports, symbol definitions, and every call site resolve to a real edge — not a guess about what's related.
Every file is chunked, embedded, and pushed into a vector store. A query becomes another embedding, compared by cosine distance to everything already indexed.
None of these are exotic failure modes. They're the ordinary behavior of cosine similarity over a corpus that keeps changing under it — which a coding session does, by definition, on nearly every turn.
Re-embedding on every save is too expensive at agent-editing frequency, so it gets batched. Until that batch runs, the index is still describing the file's previous version — with no signal telling you it's out of date.
Cosine similarity gives no stability guarantee. Add ten unrelated files anywhere in the repo and a query that used to surface the right file at rank one can drift to rank four — with nothing about the query or the target having changed.
Fixed-size chunking doesn't know where a symbol starts or ends. A function that straddles a chunk boundary gets embedded as two unrelated fragments, and a query for it can retrieve either fragment without the context that makes it mean anything.
An embed call plus an ANN search happen on every query an agent makes — often dozens per session. A parser answers "who imports this file" once, patches in milliseconds when the file changes, and never has to ask an API for the answer again.
find covers a slice of that with a repo-wide grep-recall pass, not a semantic one.
That gap is real, and it's the one place teams reach for RAG a second time — embedding past conversations, summaries, or decisions as "agent memory." Which quietly re-imports both problems above, at higher stakes: now the stale, drifting thing isn't describing a file, it's describing a decision someone trusted.
It can be rebuilt from the source at any moment, so a wrong entry is just a bug — never a standing risk. That's true whether it's built by a parser or a vector store.
The reasoning behind it is gone the moment the session ends. An embedding of that claim doesn't fix that — it just makes the claim searchable while it silently goes stale.
Every note is pinned to a fingerprint of the exact files it's about, and re-checked against
them on every read. [fresh], or it says so — the same determinism the index
gives you for "where," extended to "why."
One index, kept current in the background, plus notes that check themselves. Two commands and it starts working on whatever repo you're in.