coldstart vs vector RAG

Code isn't a document. Stop searching it like one.

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

One of these is computed. The other is guessed.

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.

Structural — coldstart

What the parser already knows

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.

imports resolved exactly, not inferred
call graph built from real references
a changed file repatches in milliseconds
zero model calls, zero API key
the same query returns the same rank, always
Semantic — vector RAG

What embeddings approximate

Every file is chunked, embedded, and pushed into a vector store. A query becomes another embedding, compared by cosine distance to everything already indexed.

a vector store to run, pay for, and keep online
chunk boundaries that don't know where a symbol ends
re-embedding needed on every edit, so it's batched
an embed call plus an ANN search, every lookup
a similarity score, not a fact you can check
Neither of these is the "advanced" one. RAG is the right call when there's no structure to exploit — prose, tickets, a wiki, anything where your words and the target's words can legitimately differ. Source code is the opposite case: it already declares its own structure, in a form a parser reads deterministically. Approximating a fact you can compute is a trade, not an upgrade.

Four costs that show up on real repos, not benchmarks.

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.

Between a save and a rebuild
stale window

The index answers about code that's gone

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.

As the repo grows
rank drift

Yesterday's top result isn't today's

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.

At chunk boundaries
split symbols

A function cut in half, and returned as neither half

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.

On every single lookup
recurring cost

Paid again for a question already answered once

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.

None of this makes embeddings the wrong tool, full stop — it makes them a poor fit for a corpus that already has ground truth. If your codebase's vocabulary is genuinely inconsistent — legacy code where names don't match concepts, or you're searching for "the thing that does X" with no shared words at all — that's a real RAG win coldstart doesn't claim. Its own find covers a slice of that with a repo-wide grep-recall pass, not a semantic one.

A structural index answers where. Not why it matters.

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.

01

The index is a fact about the repo right now.

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.

02

A memory is a claim someone made, not a fact you can recompute.

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.

03

So the notebook keeps the same discipline as the index, applied one layer up.

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."

That's the actual comparison. It isn't coldstart-the-parser against RAG-the-search-engine — it's an index and a notebook that both stay honest about their own staleness, against a similarity score that has no way to tell you it's wrong. How the notebook works →

No vector store to stand up. No embedding bill.

One index, kept current in the background, plus notes that check themselves. Two commands and it starts working on whatever repo you're in.

npm install -g @cstart/coldstart
coldstart init