Text search answers structural questions by name.
The questions that matter about a codebase are structural: where is this used, what depends on it, what a change would touch. Text search answers them by name, and a name is not an identity. A function called parse and a variable called parse in an unrelated module read as one thing to a string match, and the answers come back wrong about scope and impact. symbol resolves the imports between files first, so a usage counts only when its chain leads back to the definition. That resolution step is the shape of every answer it gives.
A pure core, wrapped in adapters.
A single Rust crate built strictly inward: a pure domain wrapped in the layers that touch the world.
Parse each file, then connect them. Resolution is the step that earns the answers.
A scan walks the project once and parses every recognised file on its own. The differentiating work is the join after: the imports between files are resolved, so a usage in one file is wired to its definition in another. What lands in the cache is not a list of names but a graph.
One engine, a terminal and a machine.
A name match is a guess. A resolved reference is a fact.