Repository navigation
Add retrieval evaluation, fix the bugs it found, add TypeScript - #10
Merged
Merged
Conversation
TokenHack had ~20 hand-tuned scoring constants and no way to tell whether any of them helped. This adds the measurement and then acts on it. ## tests/ — the harness 72 questions across 6 OSS repos (spring-framework, netty, Signal-Android, DuckDuckGo iOS, ownCloud Android, a small JS app), each paired with the file that answers it. Built blind: the questions and answers were derived by reading the repos only, with the router and its index off-limits, so the set is not shaped around what this tool happens to be good at. 45 of 72 are deliberately vocabulary-mismatch questions. Public repos are pinned in tests/repos.json and tests/fetch_repos.py reproduces the whole thing from scratch. Every scoring constant now reads an optional TOKENHACK_<NAME> environment override so a change can be ablated instead of argued about. ## What the first run found hit@5 = 0.19. Four questions in five got nothing useful into the top five. 1. graph_prop was 94% of the winning score. On a netty query the winner scored 354.76, of which 333.01 was import-graph propagation against a lexical score of 22 — and the file that answered the question ranked 1,716th of 3,522. The filename gate meant to bound this is trivially cleared by a natural-language question, which contains enough words that any well-connected file matches two of them. Propagation is evidence that a *neighbour* matched, which is weaker than matching yourself: it is now always log-compressed and capped at the file's own query-derived score. 2. The prose channel was empty. ETA is the highest weight in the scoring function and only the Python adapter ever populated it — Java, Kotlin, Swift and JavaScript returned nothing, so the highest-weighted term multiplied zero across 0/3512 netty files, 0/9487 Spring files, 2/5534 Signal files. Doc-comment extraction (javadoc/KDoc/Swift doc/JSDoc) now ships in _base.py and all adapters use it; coverage is 80% on Spring, 60% on netty. With the channel actually populated, ETA 1.2 was too low — measured 0.181 at ETA=0, 0.208 at 1.2, 0.264 at 2.4, flat above that. Default raised to 2.4. 3. Tests outranked implementations. Tests restate a concept's vocabulary more densely than the code does, which is what BM25 rewards; netty's pipeline question returned two testsuite files and a microbenchmark, with DefaultChannelPipeline.java absent. Test/benchmark/fixture paths are now demoted unless the question is about tests. Paired results were also labelled backwards — the implementation was printed as "test for" its own test. 4. Structural matches ignored IDF. filename_match and path_affinity counted 1.0 per token regardless of the token, so `read` and `socket` scored the same as `idempotency` and `backoff`. Both are now IDF-weighted; worth ~1.4 points of hit@5 on its own. Query tokens below MIN_QUERY_TOKEN_IDF are dropped as corpus-wide filler. ## Result hit@5 0.19 -> 0.26, recall@5 0.16 -> 0.19, MRR 0.13 -> 0.17. On the vocabulary-mismatch slice, where lexical retrieval is expected to lose, 0.13 -> 0.22. Baseline committed so --check can catch regressions. These are in-sample numbers on six repos and most questions still miss. That is now a fact with a number attached rather than an unknown. ## Also - TypeScript/TSX adapter. The JS walk moved into walk_source() and typescript.py reuses it, adding interface/type-alias/enum declarations and dispatching .tsx to the JSX grammar. Removes the single biggest adoption blocker. - The router now warns when more than a quarter of a repo's source files had no language adapter. The indexer had always recorded this and nothing read it, so an unsupported-language repo got confident rankings over a fragment of itself. - Light plural folding on structural matches, so "bytes" reaches ByteBuf and "credentials" reaches Credential. - A workflow_dispatch/PR-triggered CI job that clones the pinned repos and fails on regression against the baseline.
rahulr85r
added a commit
that referenced
this pull request
Aug 8, 2026
The README led with "~95% of the tokens your AI assistant spends finding files" and put "~25% session-level token savings" as the honest single number. Both predate any measurement of whether retrieval returns the right file, and the evaluation added in #10 shows it does so 26% of the time — so a savings model that implicitly assumes retrieval works cannot be right. Reworked to the actual expected value, with the hit rate carried through: on a hit (26%): 495 staged + ~545 to read the top result's spans on a miss (74%): 495 staged, and Claude explores anyway E[saving] = 0.26 x (B - 1040) - 0.74 x 495 which breaks even at B ~= 2,400 tokens of unaided exploration, reaches ~14% at B = 5,000 and ~20% at B = 10,000, and is NEGATIVE below break-even. Netting off the ~1,150-token skill body and the share of a session that is actually navigation, that is single-digit percent session-wide for a typical engineer. B — what the unaided grep-and-read expedition costs — is the one input never measured end-to-end, so this ships as a range with the assumption named rather than a headline number. Also removes the n=1 DuckDuckGo trace that produced the 95% figure. It was one query on one repo with the baseline adjudicated after the fact by the author, and the hero image drawn from it shows the correct file ranked 5th of 5.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TokenHack had ~20 hand-tuned scoring constants and no way to tell whether any of them helped. This adds the measurement, then acts on what it found.
The harness
tests/— 72 questions across 6 OSS repos, each paired with the file that answers it. Built blind: questions and answers were derived by reading the repos only, with the router and its index explicitly off-limits, so the set isn't shaped around what this tool is good at. 45 of 72 are deliberately vocabulary-mismatch questions, where the question's words don't appear in the answer's name.python3 tests/fetch_repos.py # clones + indexes the pinned public repos python3 tests/eval.pyPublic repos are pinned to the commit their gold set was written against, so the numbers are reproducible. Every scoring constant now reads a
TOKENHACK_<NAME>env override, so a change can be ablated instead of argued about.First run: hit@5 = 0.19
Four questions in five got nothing useful into the top five. What that found:
1.
graph_propwas 94% of the winning scoreEvery real lexical signal summed to 22 and got shouted down 15-to-1. The filename gate meant to bound this is trivially cleared by a natural-language question — enough words that any well-connected file matches two. Propagation is evidence a neighbour matched, which is weaker than matching yourself: now always log-compressed and capped at the file's own query-derived score.
2. The highest-weighted channel in the formula was empty
ETAis the largest coefficient in the scorer, and only the Python adapter ever populated the prose channel:Java, Kotlin, Swift and JS returned nothing — and an empty channel doesn't error, it just contributes zero forever. On repos that are famously javadoc'd. Doc-comment extraction now ships for all adapters (80% coverage on Spring, 60% on netty), and with the channel actually populated
ETA=1.2was too low: 0.181 at ETA=0, 0.208 at 1.2, 0.264 at 2.4, flat above. Default raised to 2.4.3. Tests outranked the code they test
Tests restate a concept's vocabulary more densely than implementations do, which is exactly what BM25 rewards. netty's pipeline question returned two testsuite files and a microbenchmark with
DefaultChannelPipeline.javaabsent — and when the pairing did surface it, it printed the implementation as(test for ...)its own test. Test paths are now demoted unless the question is about tests.4. Structural matches ignored IDF
filename_matchandpath_affinitycounted 1.0 per token regardless of the token, soread+socketscored the same asidempotency+backoff. Now IDF-weighted (worth ~1.4 pts of hit@5 alone), and sub-threshold query tokens are dropped as corpus filler.Result
Baseline committed so
--checkcatches regressions.These are in-sample numbers on six repos, and most questions still miss. That's now a fact with a number attached rather than an unknown.
Also in this PR
walk_source();typescript.pyreuses it and adds interface/type-alias/enum declarations, dispatching.tsxto the JSX grammar. Biggest adoption blocker removed.bytes→ByteBuf,credentials→Credential).workflow_dispatch+ on PRs touching the ranker) that clones the pinned repos and fails on regression.