CodeNib treats a code repository as a database with three materialized views (lexical, dense, structural) tied to one commit, then updates each view along its own path. Vector reuse matches a fresh rebuild on 90% of source-changing transitions at ~25× median speedup; graph repair matches on 45% at ~9×.
You’ve shipped a coding agent that, on every ticket, runs grep and read_file a dozen times to figure out where in a 200k-line repo it should even look. When the user files the next ticket, that discovery starts over from zero. Every task pays the exploration tax, and the token bill quietly balloons because those tool observations sit in the trajectory for the rest of the run.
The current baselines pick one shape of “repository context” and commit to it. Serena exposes a live language server as tools. Aider ships a repository map. Retrieval-first systems like RepoCoder alternate embed-search with generation. Each treats retrieval, symbol navigation, and prompt bookkeeping as one abstraction. CodeNib argues that’s the wrong shape: they have different freshness rules, different failure modes, and different costs, and collapsing them hides all three.
The core reframe is data-systems: a Git commit is immutable base data, and everything an agent wants (chunks, embeddings, call graphs, symbol occurrences) is a derived view over that commit. CodeNib builds three view families per commit, each with its own physical layout:
•
a lexical view (BM25 postings, optional Zoekt trigrams),
•
a dense view (embeddings of files or callables in FAISS),
•
a structural view (a symbol graph with typed containment, reference, and import edges, backed by SCIP or clangd output).
A per-commit manifest records what got built, which builders succeeded, and which capabilities are available. Every result, whether a ranked snippet or a jump-to-definition, is mapped back to a repository-relative source range (path, start_line, end_line, kind). That shared address is what lets independently-built views compose without a shared storage engine.
When files change, each view has its own maintainer. The structural view uses LSP-assisted repair: it classifies each symbol in the diff as deleted, shifted, unchanged, or added, and only re-issues definition/reference queries for the ones whose relationships could have changed. The dense view uses content-addressed embedding reuse: unchanged chunk text keeps its vector, changed chunks re-embed, and FAISS gets a delta update. BM25 currently rebuilds. Crucially, the paper’s speedup claims are gated by an offline output-equality check against an independently-rebuilt target; transitions that don’t match are reported but excluded from the headline speedup.
At serve time, the agent runtime reads the manifest, opens only the views its selected skills need, and exposes them as tools. Context delivery is a separate policy layer with three arms compared in the paper:
# per-agent-turn, all arms share tools, budget, turn cap
H0 = [system, issue] # grep/read baseline
H0 = [system, issue, top10_L2_snippets] # eager
# compact: same start as eager, then ONE rewrite
after_first_successful_read:
Hj = [system, issue, dedup_paths,
last_read_full, assistant_msg[:600]] # discard exploration
# subsequent turns append to Hj normally
The compact rewrite happens exactly once. Every later turn extends that fixed prefix so the KV cache can be reused.
The prevailing move in agent frameworks is to hide retrieval, navigation, and context under one “give the model the right stuff” abstraction, and to report one quality score for the whole pipeline. This paper does the opposite: it insists that ranked search, symbol lookup, incremental maintenance, and prompt delivery have different output contracts, different validity boundaries, and different failure modes, and it refuses to average across them. A static symbol index that matches a live language server on 87% of definition requests is a conditional replacement, not a drop-in. A graph repair that’s 9× faster on the transitions where it matches a fresh rebuild is not “9× faster” globally. The load-bearing evidence is the per-operation output-equality gating throughout Q3 and Q4, not any single benchmark number.
The finding that makes the thesis stick is the asymmetry between views’ incremental-update reliability. Vector reuse matched the independent rebuild on 28 of 31 source-changing transitions (90%), at a ~25× median speedup. Graph repair matched on only 15 of 33 (45%), at ~9×. Same system, same idea (“update in place instead of rebuild”), but graph semantics for Rust and TS/JS were fragile enough that no transition in those languages passed both the whole-graph and the serving-replay check, even though edge-F1 was above 97%. Reporting one “incremental speedup” number would have buried that.
Secondary findings, all with the same gating discipline:
•
Static vs live navigation: across 1,000 requests to five language servers, the static index reproduced the live server’s normalized (path, start_line) set on 63.2% overall, but split as 87% for definitions and 39% for references. On the matching subset, static was ~4.7× faster median per request. The paper explicitly refuses to call this a workload speedup.
•
Retrieval plans: dense retrieval finishes in ~26–295 ms; adding a pointwise reranker buys a few points of recall at seconds of latency (Jina+4B reranker: +4.6 points file recall at 46.6× the latency of dense alone). Adding graph expansion to dense retrieval gives per-embedder deltas from −4.8 to +7.1 points, all with confidence intervals crossing zero.
•
Context policies across five agent models: relative to a grep/read baseline, the best-selected policy (eager for Haiku, compact for the rest) used 50–87% fewer trajectory tokens while keeping localization recall within a 0.05 lower-bound margin. Compaction is not uniformly better than eager: on Haiku it actually costs 23% more tokens.
Reach for this decomposition when you’re building an agent that hits the same repo over and over: a code-review bot, a migration assistant, or an IDE-adjacent “answer a question about this codebase” tool. Instead of one monolithic “repo context” service, keep the lexical, dense, and structural indexes as separate artifacts with a manifest, and let each one update on its own schedule. When a PR lands, re-embed only the changed chunks and re-issue LSP queries only for the symbols whose neighborhoods changed. Keep the live language server around for the reference queries that your static graph misses; the paper is candid that you can’t route around it safely.
On the compaction side, the reusable pattern is the one-shot history rewrite after the first successful read: keep the injected candidates and the retrieved file content, throw away the exploration transcript, and let the KV cache take over from there. The paper doesn’t link a public GitHub repo in the text provided, and it references two Hugging Face datasets (fishmingyu/codenib-base-dataset and sysevol-ai/codenib-synthesis) with pinned revision hashes as the evaluation splits. Whether the CodeNib code itself is released isn’t stated in the excerpt.
Repository context isn’t one thing; it’s three views with three freshness rules, and pretending otherwise is what makes agent codebases feel slow and stale. The design lesson generalizes past code: any time you’re serving derived state to an LLM agent, the honest question is not “how fresh is my index” but “which of my indexes matches a fresh rebuild on which kinds of change, and where do I keep a live fallback for the rest.”
•
The evaluation is localization, not patch success. CodeNib measures whether the agent finds the right files and symbols, not whether it produces a working fix. If your bottleneck is patch quality rather than exploration cost, the token savings may not translate.
•
Incremental graph repair is language-dependent and brittle where it matters most. Go and Python passed all symbol-repair transitions; Rust and TypeScript passed none of the strict equality checks. If your target language is in the fragile set, you’re closer to “fast when it works, rebuild when it doesn’t” than to a reliable speedup.
•
The static/live navigation gap is real and unlikely to close from indexing alone. References mismatched 61% of the time, so any system that swaps in the static provider without a compatibility oracle will silently return the wrong answer on workspace-semantic queries. The paper keeps the live LSP in the loop for exactly this reason.