CI Release Licence Standard Runtime dependencies Slides

Rip'n Fast. Fewer Tokens. Better Code.

Give your coding agent a map before it reads the repo.

ripwire is the ripgrep of AI context. Point it at any repository and your agent gets a ranked, deterministic call graph — what to touch, what it breaks, which tests to run — instead of grepping around and reading whole files.

The goal: one question, one complete answer.

Terminality is the objective. Ask the codebase a question and the answer should carry everything you need — no follow-on grep, no three more whole-file reads to fill in what it left out. A call followed by three greps is the same search paid for twice: it does not save you tokens and it does not make the coding faster.

Two things make that reachable in practice, and neither is the destination. Answers are honest about their own limits — a count that cannot be a total is labelled a floor, a zero means "none found" and never "none exists", every truncation is disclosed — so an answer never looks more complete than it is, and the map never degrades the code by guessing. And an answer can be given a token budget, so what one costs is something you ask for rather than discover; where a complete answer will not fit, it says it went over rather than silently dropping the row you needed.

Those two are the stair-steps: honest about what is missing, priced in what it spends. The step they climb toward is a question fully answered in one call, which is not always trivial to reach — and where it is not, the output says so rather than pretending otherwise.

Beside those sits a labelled survey of 237 tools that contributed nothing and says so. The two sets are disjoint by construction, so they add rather than nest — a tool that gave a lesson is never counted twice.

Both halves are load-bearing, and they are doing different jobs. The settled results are what make the quality lens trustworthy: McCabe on complexity (1976), Halstead on volume (1977), Spärck Jones on term specificity (1972), Nagappan & Ball on churn. Fifty years of replication means those are not opinions, and a tool that measures your code should be built on the ones that survived.

The recent work is what makes it current: seventeen of the folded papers are from 2026, seven published in the last two months and three in the last thirty days (dates as of 2026-09-08; every row carries its arXiv id, so the claim is checkable rather than atmospheric). Retrieval for coding agents, context-compression cost, placebo-controlled localization — that literature is months old, not decades, and several rows were folded within weeks of the paper appearing.

Neither half alone would be enough. A tool built only on the classics would not know what an agent needs; one built only on last month's preprints would have nothing underneath it. And the newest row is a result that failed when it was tested here — which is the point of writing them down. All three counts are re-derived from that document's own tables by test/readmedriftcheck.sh on every run, which fails if this page and those tables disagree, so the claim cannot quietly drift. The row-by-row ledger is docs/LINEAGE.md.

Languages: Rust · C++ · Objective-C/C++ · C · Metal · CUDA · Python · Go · Swift · TypeScript · JavaScript · Java · Ruby · PHP · Lua · Elixir · Bash · C# · JSON · TOML · YAML · Markdown — see language support and limits.

No API key. No embeddings. No index server. No daemon.

Measured on 48 matched questions across django, webpack and this repository. Across all three, ripwire indexes in 0.25–0.45 s and 6.6–16.5 MB against that server's 23–52 s and 391–623 MB. The full method, the wins named one by one and the losses included, is in Against the leading graph-database code-context MCP server and docs/EVALS.md.

One self-contained binary on your own machine, offline, installed in one line — and the same line installs and activates the task-shaped skills that teach your agent when to reach for it, not just how, for every agent it finds on the machine. If your agent can run shell commands — Claude Code, Codex, Cursor, Windsurf, Gemini, opencode, aider — it is set up the moment the install finishes; the MCP server is the optional second interface. Install it and ask it something before you finish reading this page:

RIPWIRE_REPO=redhat-et/ripwire bash -c "$(curl -fsSL https://raw.githubusercontent.com/redhat-et/ripwire/main/scripts/install.sh)"
export PATH="$HOME/.local/bin:$PATH"      # where it installed; the installer prints this line if you need it
cd your-repo
ripwire . --for="<the change you are about to make, in words>"

Reach for the CLI first — it is the cheaper interface. The MCP server is the optional second way in, and its convenience has a cost the shell pipe does not carry: its verb schemas sit in your agent's context every session, whether or not it calls them.

Same answer, a fraction of the tokens — read this table first if your agent is on a budget

Ten everyday moments, re-measured on this repository, 2026-08-08. Figures are ~tokens (≈ bytes/4); every ratio comes from a real run, reproduced by the command in its row — raw byte counts and exact commands in docs/EVALS.md §5.

Ordered understand → navigate → review-the-change:

Ask it Command ripwire naive read token savings
"Orient me in this repo" ripwire . ~5.6K tok ~20K–25K tok — read README.md (+docs/ARCHITECTURE.md) 3.6×–4.5×
"Where is X handled?" ripwire . --for="…" ~2.1K tok ~4.9K–20K tok — grep -rn <term> src/, then read the file it points at 2.3×–9.3×
"What do I already know?" ripwire . --recall="…" ~15K tok ~445K tok — read all 119 markdown docs this repo carries 29.2×
"Set me up for this task" ripwire . --pack-task="…" ~2.1K tok ~16K–80K tok — read every relevant file, whole 7.7×–37.7×
"Show me this one function" ripwire . --expand=SYM --top-k=0 ~260–16.5K tok body (+~5.7K for the ranked-neighborhood bundle) ~43K–174K tok — read the whole file it lives in 2.6×–670×
"Who calls this function?" ripwire . --callers=SYM ~580 tok ~40K–52K tok — grep -rn SYM src/ (mostly noise), then open 2–3 files to sort real calls from mentions 69.2×–89.1×
"Is it safe to change this?" ripwire . --impact=SYM + --uses=SYM ~1.3K tok ~18K tok — open every direct-use file, whole 14.4×
"I have a stack trace" ripwire . --from-trace=FILE ~1.4K tok ~124K–298K tok — grep all 7 frame names, then open the innermost file(s) 86.9×–208.6×
"I changed these files — tests? blast radius?" ripwire . --situ ~410 tok ~3K–132K tok — git diff + grep -rn <syms> test/, then open the candidates 7.3×–324.2×
"Review this PR/diff" ripwire . --pr-context=REF ~1.9K tok ~4.8K–51K tok — git diff REF, then open the touched files 2.6×–27.5×

These aren't summaries that gamble with information. Each row is scored same-correct-answer-or-it-doesn't-count, and both sides were checked, not assumed: orient surfaces this repo's own pipeline files (ingest.cpp, graph.h, serialize.h) in the first screen, the same three docs/ARCHITECTURE.md names as central; the --for row lands mcpStale (src/mcpindex.h:633), the actual staleness check, 5th-ranked; --recall lands the container-rule doc (AGENTS.md) that states, verbatim, the same "no std::map" rule CONTRIBUTING.md explains in full; --pack-task names the same three touch points a human would — cachelint.h, mergeCachePack (src/main.cpp:1787), the lintrules.h helpers it reuses; --expand --top-k=0 hands back the requested function's complete, unmodified body — the ranked-neighborhood addition costs the same ~22.6 KB regardless of which function you ask for, confirmed on two (a fixed floor, not per-function variance); --callers on langOfPath names its 2 real callers, the same ones a grep hit-list buries under 5 files of comment-only mentions; --impact+--uses on coversOrEquals names the same 2 direct call sites --uses alone would, plus (disclosed) a transitive reach --uses doesn't cover at all; --from-trace resolves all 7 frames of a real call chain by name to the same definitions a per-frame grep would eventually find, mixed with call sites and comments; --situ on a 2-file diff names the same 6 real test harnesses, 2 of which a filename grep across test/ cannot find even after opening every one of its 41 candidates — a completeness gap, not just a byte one; --pr-context surfaces co-change partners (test/regression.sh, src/main.cpp) a raw git diff has no way to know were usually touched and weren't this time — Fowler's Shotgun Surgery checked rather than merely named (the backtest is in docs/EVALS.md). The map ranks and discloses — it never paraphrases your code — and every truncation is disclosed in the header.

The honesty line, made concrete: the same auto-selection behind the --expand row also runs the other way. On a small file (pageRankDouble in src/pagerank.cpp, 5,559 B) the ranked bundle would cost 27,916 B — nearly 5× more than the file — so ripwire serves the file itself instead, disclosed as mode="whole-file" on the response, not silently. docs/EVALS.md §7 lists that and the other counterexamples this project publishes against itself.

The shape: plan the work, then run a loop that matches each task to the model that fits it. Every thread it spawns opens with an empty context on a repository it has never seen. Orienting an empty context to a large repository is the most repeated cost in the whole system, and the one this tool was built for — it is also the cost that grows with the size of the tree, which is why the pattern matters more the bigger the repository gets.

--for and --pack-task answer it in a single call, at the per-call rates in the table above, instead of a grep-and-read tour that every lane pays over again from scratch.

The return path matters as much. --quality-delta, --test-gate and --edit-check answer what did I make worse, which tests must run, did I change a contract — quantitative answers a lane can hand back as a verdict, rather than a transcript the orchestrator has to read to find out what happened.

What is and is not claimed here. Every figure on this page is a single-agent measurement. That the saving compounds with the number of cold orientations follows from the fixed-cost mechanism, but it is pre-registered and unrun — the reason the pattern is worth trying, not a result this project has published.

See the map — not just the numbers

ripwire path/to/django/db/migrations --rank-by=rrf --top-k=120 --color-by=cx --html=map.html

One self-contained HTML file (--html[=FILE]), no server, no CDN, no external asset. --color-by=lang|community|cx|churn|tested sets the initial colour; the page embeds all five and keeps a live selector, so switching lens costs no second run.

Read from the figures above, which state their own rules in a sidecar saved beside each image:

  • arrow points caller → callee — the graph is directed, and the page draws it that way.
  • 31 of 183 shafts dashed in this view = the resolver could not choose between same-name definitions and split the call over all of them — per edge, not per symbol. A symbol-level "this function makes some ambiguous calls" would mark every one of its edges, which would be a lie about most of them.
  • labels: top 24 by in-view degree, one per name — one label per distinct name, so a picture of a container class stops crowding out the functions you asked about.
  • shapes: ● fn ■ cls ✚ var — kind is nominal data on a nominal channel; complexity never uses shape.
  • module outlines: 7 of 12 modules with 3+ nodes in view (cap 12; 3 dropped as too thin to read as a region; 2 dropped as enclosing mostly other modules) — three separate truncations, each with its own count and its own reason.

The cx and churn ramps share one five-stop scale, ordered so lightness rises with the value — it survives greyscale printing, and every adjacent pair stays separable under protanopia, deuteranopia and tritanopia. Thresholds are fixed rather than per-corpus quantiles, so a hot node cannot be manufactured by a cold repository.

churn needs real git history: a shallow clone reports every file as one commit, and a directory with no repository says churn unavailable rather than drawing zeros.

One deterministic answer: the relevant symbols, their callers, the change risks, and the tests that reach them. The task is yours to phrase — ask about your code, not ours. Run on this repository (2026-08-30), ripwire . --for="incremental cache invalidation" produced about 4.3K estimated tokens, not an enforced token budget. It includes:

This is what the output looks like, not the token-savings recipe. A bare --for on every question is the most expensive way to use this tool — see Where it pays most and the three controls below it.

  • The ranked symbols, in rank order — the cache-header constant kCacheMagic first (with its doc comment quoted in place and the one next= call that opens it), then spanTierMemoPath (the cache-path composer), ingestCommitTree, … ingest — each row with its file, line, and signature.
  • Risk, annotated in place — complexity, git churn (ingest shows 128 recent edits), change amplification (touch ingest and 266 graph nodes feel it), purity and test coverage. The fragile spots are visible before anything touches them.
  • One-hop call contextspanTierMemoPath calls shaKeyedCachePath, headSnapRepoHex, exclConfigHex; no second query needed to see the neighbourhood.
  • Its own confidence — this answer says confidence="high" with the score margin attached; a flat ranking says low, so it reads as a starting point instead of masquerading as an answer. confidence= measures how clearly the ranking separates its head from the rest, not whether the head is what you meant: ask a repository about a concept it does not contain and the best lexical matches still rank, confidently. Phrase the task in your code's own words.
<ctx task="incremental cache invalidation" confidence="high" margin_pct="20"
     bundle="compact" bodies="0" reason="compact-route" est_tokens="3995">
  <sigs shown="23" total="40" capped="1">
    <d l="106" n="kCacheMagic" p="src/ingest_cache.h" cx="0" in="0" churn="11" amp="71" pure="1" r="1"
       next="--expand=src/ingest_cache.h:kCacheMagic">
      <doc>incremental cache (--cache): per-file content hash + raw facts so a
           re-run re-parses ONLY …</doc>constexpr std::uint32_t kCacheMagic = …</d>
    <d l="1307" n="spanTierMemoPath" p="src/ingest_astquery.h" cx="1" in="2" churn="5" amp="44" r="2"> … </d>
    <d l="247" n="ingestCommitTree" p="src/dmm.h" cx="6" in="1" churn="6" amp="27" r="3"> … </d>
    …
    <d l="191" n="ingest" p="src/ingest.cpp" cx="4" in="14" churn="128" amp="266" tested="1" r="13"> … </d>
    … </sigs>
  <hops shown="2" total="6" capped="1" noedge="2">
    <h l="1307" p="src/ingest_astquery.h" n="spanTierMemoPath">
      <calls total="3"><c n="shaKeyedCachePath" l="1621"/> … </calls></h> … </hops>
</ctx>

cx= complexity, churn= git edit frequency, amp= change amplification, r= rank; <hops> rows carry the one-hop call context, caps disclosed. Every attribute is defined in the one legend at the top of the real output, which also self-reports the bundle's cost — est_tokens="3995" here.

The agent without a map The agent with ripwire
greps a common word, gets hundreds of hits across dozens of files one ranked answer — est_tokens="3995" on this repository (re-derived 2026-09-05, the run above)
reads whole files to find the symbols that matter those symbols, with complexity, churn and test coverage inline
finds the callers only if it thinks to grep for them too callers, blast radius and the tests to run, in the same bundle
pays for every line it read, right or wrong 5.0% of what that grep-and-read pass spends — on a 12-question set where it strictly satisfied 5 to the naive arm's 11 (re-derived 2026-08-23)

Both halves of that sentence, because one without the other is an overclaim. The 5.0% is context compression, not equal task completion: on the five questions both arms strictly satisfied, ripwire spends 5.2% of what the naive pass spends. Cheap context that answers less is not a saving if the agent then retries. The full adjudication, the four ranking defects behind the eleven-to-five gap, and the run where this number got worse are in Measured.

And against five retrieval competitors on a held-out LocBench slice, it finds all gold files in the top 10 on 58.3% of instances — the best alternative lands 40.0% — while indexing in 0.31 s. The full leaderboard, losses included ↓

Against the leading graph-database code-context MCP server

Won 27 · lost 7 · tied 14 on 48 matched questions across django, webpack and this repository, spending ~77K tokens against its ~486K for the whole sweep.

The 48 questions span symbol lookup, conceptual search, blast radius, and one-call task orientation. ripwire indexes the same three repositories in 0.25–0.45 s and 6.6–16.5 MB, against that server's 23–52 s and 391–623 MB, and answers a warm query in a median 197 ms against its 1,082 ms. Its seven wins are real and named one by one in the method.

Both arms warm with a pre-built index, median of 3 timed calls, stdout to a file rather than a pipe. The competitor ran in its stronger retrieval configuration; its numbers were recorded once and then frozen, and ripwire's side was re-run after the fixes the first pass produced. Per class, as a share of the competitor's bytes on totals: symbol lookup 1.35×, conceptual search 1.23×, blast radius 0.39×, task orientation 0.06×.

Where it loses: on a plain one-symbol lookup the competitor answers in about a kilobyte carrying callers and callees, and ripwire spends roughly three times that to also hand back the body. It ranks a chunk-id plugin first on one webpack query where ripwire never surfaces the directory at all — a ranking miss, and a fix for it was built, met its pre-registered band, and was reverted anyway for failing a separate standing requirement. Its depth-labelled blast radius and its import edges are both better presentations than ripwire's flat reaching-set.

Full method, pins, per-class tables, the carried-versus-re-judged ledger, and the complete list of what the competitor does better: docs/EVALS.md §2.

Nothing it is unsure about reaches your agent unlabelled — and nothing it could not see goes unnamed. Every guess is marked in the output, and every mark has a next step — up to handing it a compiler-grade index. Point it at a repository whose main language it has no grammar for and the map's first line says so (unindexed="ml:793,mli:607,…" on a facebook/infer clone); a file it indexed but cannot vouch for carries a parse-health row; every file the crawl passed over is itemized with its reason. A confident-looking map that lies by omission is the failure mode this tool refuses.

Graph-Ranked Retrieval: It finds the right files more often than the alternatives

58.3% against 40.0% for the best tool tested — and it answers before they finish indexing. Every arm below was re-run in full on 2026-08-08 — one ripwire binary (the profile-guided release build that now ships), one evaluator, one 60-instance held-out LocBench slice: paired, zero exclusions, same gold set, and the metric code imported unmodified into every arm. Strict file@10 = all gold files inside the top 10, which is whether your agent starts in the right place at all.

Round 4 — LocBench, Python-dominant strict file@10 any@10 index (median) query (median)
ripwire --for 58.3% 85.0% 0.31 s 0.108 s
codebase-memory-mcp 0.9.0 40.0% 63.3% 1.24 s 0.075 s
repowise 0.37.0 33.3% 53.3% 34.0 s 1.159 s
graphify 0.9.34 31.7% 46.7% 7.82 s 0.614 s
Aider repo-map 0.86.2 20.0% 35.0% (inside query) 2.920 s
codeseek 0.1.31 (better of its two arms) 15.0% 20.0% 3.37 s 0.040 s

Ripwire leads every arm on both accuracy metrics and in both strata. Paired, the losses are small and they are published: 2 instances to codebase-memory-mcp, 2 to repowise, 1 each to graphify and aider. Cold from nothing to an answer — parse, rank, reply, no cache — ripwire takes 0.213 s, against a ~35 s index-then-query for repowise; its worst single index in this run was 352 s.

Three things this table costs us, said plainly. codebase-memory-mcp is the real runner-up at 40.0%, not repowise — an earlier round credited it with 26.7%, and re-running it fairly raised it. The margin over the best competitor is therefore 1.46×, not the 1.75× two separately-dated tables used to imply. And multi-file gold is hard for everyone: ripwire leads the stratum at 21.4% strict, but its own any@10 there is 78.6% — it finds a gold file and misses the siblings, and no arm in this table solves that.

Held out wider — 243 instances across 78 repositories — ripwire lands 60.9% against 27.6% for its own pre-routing baseline: +33.3pp paired, clustered-bootstrap 95% lower bound +25.0pp, bought for +3.4% warm latency and −39.4% tokens. Full provenance, the losing instances one by one, and a third round against a compression-layer competitor: Measured and bench/headtohead/r4-2026-08-06/, whose harness is committed so anyone can re-run the whole comparison.

Test scaffolding does not pollute the ranking from inside source files either: #[cfg(test)] mod tests, describe() blocks, Test* classes and [Fact] attributes are detected syntactically, wherever they live — not by file path alone. On an astral-sh/ruff clone (5,945 files), --ignore-tests removes 23,907 test symbols where path rules alone caught 18,532 (ripwire <ruff> --ignore-tests, 2026-08-14; the per-language fixtures are pinned by test/testscopecheck.sh).


The table above, read two other ways. Every figure in both is one of its measured numbers; only the manners and the cynicism are editorial.

A Survey of the Neighbourhood's Eligible Instruments — being an account of five gentlemen of retrieval, and one lady of no pretension whatsoever.

It is a truth universally acknowledged, that an engineer in possession of a large repository must be in want of a map.

Mrs. Codebase-Memory must be named first, for she has risen a great deal in the estimation of the neighbourhood — two-fifths of her answers entirely correct, which is more than any other caller can say, and she is ready in a second and a quarter. It must nevertheless be recorded that her card announces accomplishments in the semantic line; that upon enquiry the semantic line is not at home; and that the household denies all knowledge of it. One is left with the impression of a capable woman ill-served by whoever prints her cards.

Mr. Repowise is by common consent the most substantial of the party, and no one who has waited upon him would dispute it. He is possessed of a handsome index and a manner of great thoroughness; but he must be seen to. One does not simply address Mr. Repowise. One sends word, and dresses, and waits — three-and-thirty seconds on an ordinary morning, and upon one memorable occasion in the country, seven minutes and four seconds — during which interval a less consequential neighbour has answered the question, taken her leave, and thought no more about it. He answers creditably when at last he arrives, one time in three; whether that is worth the toilette, each family must determine for itself.

Mr. Graphify enjoys a great many admirers. He does not rank his acquaintances; he calls upon them in whatever order his walk happens to take him, and reports the order of the walk as though it were an opinion. He has been known to arrive carrying a hundred and thirty megabytes of correspondence. Pressed once for any answer at all, he replied that no matching nodes were found, and considered the matter closed.

Mr. Aider is the most gentlemanly of the company and by far the most difficult to consult. He cannot be asked a question — the thing is simply not done. One may mention names in his hearing and hope he takes the hint; he does take it, and is fully ten points the better for it, which says more about the hint than about Mr. Aider. But he forms his view of the neighbourhood before you speak and retains it after, and one cannot escape the feeling that the conversation was never truly with you.

Mr. Codeseek is a young gentleman of quick habits who suffers from an affliction of address. Speak to him plainly, in the language of ordinary complaint, and he will regard you with perfect composure and say nothing whatever — nothing, upon sixty occasions out of sixty. Name a person precisely as that person is named, and he grows animated directly. It is not stupidity; it is a want of imagination in the matter of introductions.

And there is ripwire, of whom nothing is said in the drawing rooms, because she has already gone home. She was asked; she answered, in thirteen hundredths of a second; every gold file within the first ten, in eight-and-fifty cases of the hundred. She keeps no establishment, corresponds with no distant authority, and has never once been indexed at a party. Mr. Repowise finds her abrupt.

She is.

Every figure above is a measured number from the round-4 table on this page: the index medians (1.24 s, 34.0 s, 3.37 s) and repowise's 352 s worst case, the 40.0% and 33.3% strict file@10, the 0-results-on-60/60 fallback arm, the absent semantic_query tool, graphify's 129 MB largest graph and its 1-of-60 empty ranking, aider's +10 pp personalization delta, and ripwire's 0.108 s / 58.3%. Provenance in bench/headtohead/r4-2026-08-06/ and docs/EVALS.md; only the manners are editorial. These are other people's real work, and the joke is aimed at the trade-offs, never at the authors.

The Long Index — in which a man asks six informants one simple question, and only one of them has the decency to answer it.

It was a million lines of somebody else's mistakes, and I needed one file out of it before the coffee went cold. So I did what you do. I went and talked to the people who say they know the neighborhood.

Codebase-Memory had the best record in the room and she knew it — two answers right out of every five, handed over in a second and a quarter, which in this business is practically a kindness. Trouble was the card. Right under her name it said semantic query, real classy, real expensive-looking. I asked to see it. She said it wasn't in. I asked the house. The house had never heard of it. I've known a lot of good people ruined by whoever printed their cards.

Repowise was the heavyweight — everybody told me so before I got through the door. Big index, good tailoring, thorough as a tax man. Only you don't just ask Repowise a question. You send word. You wait. Thirty-three seconds on a good day, and one bad morning out in the country, seven minutes and four seconds — long enough to get the same answer somewhere else, drive home, and forget his name. He came through one time in three. For some outfits that's worth the wait. I've got a metabolism.

Graphify never met a fact he wouldn't hand you. Ask him one thing and he turns up with a hundred and twenty-nine megabytes of everything, unsorted, in whatever order he tripped over it — and he'll report that order like it's a considered opinion. It isn't. Leaned on him once for anything at all; he looked me dead in the eye, said no matching nodes, and figured we were square.

Aider was a gentleman, which is another way of saying you couldn't file a straight question into him in triplicate. Wouldn't be asked. You mention things, loud, and hope — and sure enough, drop the right names and he's ten points sharper, which tells you everything about the names and nothing about Aider. He'd made up his mind about the place before I opened mine, and kept it after. You never did feel the conversation was with you.

Codeseek was young and had a condition. Talk to him like a human being — plain, tired, the way a man actually asks for help — and he'll look clean through you and say nothing. Sixty times out of sixty, nothing. But name the thing exactly, badge number and all, and the kid lights right up. It isn't that he's slow. He just never learned how people knock on a door.

And ripwire. Nobody at the table brought her up, on account of she'd already left. Took the question, answered it in thirteen hundredths of a second — every file I needed inside the first ten, fifty-eight times out of a hundred — keeps no office, wires no head branch, never once got herself indexed at a party. Repowise says she's abrupt.

She is. That's why I hired her.

Every figure above is a measured number from the round-4 table on this page: the index medians (1.24 s, 3.37 s, 34.0 s), repowise's 352 s / six-minute worst case, the 40.0% and 33.3% strict file@10, codeseek's 0-of-60 plain-language arm, codebase-memory's advertised-but-absent semantic tool, graphify's 129 MB largest graph and its 1-in-60 empty return, aider's +10 pp name-drop delta, and ripwire's 0.108 s / 58.3%. Provenance in bench/headtohead/r4-2026-08-06/ and docs/EVALS.md; only the cynicism is editorial. These are other people's real work, and the joke is aimed at the trade-offs, never the authors.


Name a symbol and it is the first hit — and it is never a mystery which ranker answered. Every --for query is served by one of three lanes; a confidence-gated router picks by reading the query's shape, discloses its choice on the output (route=), and --no-route overrides it. Routing lifts recall@1 on name-shaped queries 61.1% → 91.3% in src/, 59.2% → 85.5% at the repository root — and the gate is load-bearing in both directions: route everything to the name lane and prose queries collapse from 0.967 MRR to 0.016. Both numbers ship together. Reproduce with ripwire <dir> --eval-retrieval, which grades EVERY doc-commented symbol in the corpus and prints its own population=/scored=/rule=; the full per-ranker tables are in bench/ANSWERQUALITY.md.

Lane Built for Why it wins there Where it loses
name-exact identifier-shaped queries (chooseForRanker, pack task) whole-name match ignores body noise: 91.3% recall@1, 0.960 MRR in src/ scores zero on any word that is not literally a name — forced onto prose it dies (0.016 MRR)
subtoken+body prose and task queries ("where is the content hash computed") the only lane that matches vocabulary living in doc comments and bodies exact names drown in shared subtokens (61.1% recall@1 on name queries)
mention anchor a pasted path, Type.method, or issue URL a literal mention is lifted above any score — paste the ticket, don't paraphrase it adds nothing when the query names no artifact

How the conceptual lane finds what you didn't name. The subtoken+body lane is why a query with no symbol name in it still lands:

  • Both sides are split into subtokens. SplitChunksPlugin becomes split+chunks+plugin, and so does your query — so words match pieces of names you never typed.
  • Three evidence fields, not one. A symbol is scored on its name subtokens, its doc comment, and its body — vocabulary that only exists in a comment or an implementation still finds its symbol.
  • BM25 with per-query IDF. Rare, discriminating words dominate the score; words the whole corpus shares contribute almost nothing. Type the three words only the right function uses and they carry the query.
  • Lookalikes are down-weighted, not hidden. Fixture, test-data, and generated paths score at a fraction, so a test vocabulary-twin cannot outrank the real source (adversarial-class pollution@5: 28% → 0%, docs/EVALS.md §4) — but they stay in the index and are still found when asked for.
  • The list ends at a cliff, not a quota. The cut is adaptive: output stops where the scores drop off, so a sharp answer is a short list and a diffuse one is disclosed as such, instead of a fixed top-k pad