# code-review-graph: 63× token reduction vs whole-corpus baseline

CRG serves 2,200–3,900 tokens of targeted context to MCP coding agents via a local Tree-sitter graph.

Canonical page: https://makerportal.ai/blog/itria-github-1167788341
Author: Joshua Hrisko, Principal Engineer — MakerPortal
Published: 2026-09-19
Section: Readout / Local-First Architecture · 6 min read
Tags: itria, privacy-arch

---

code-review-graph ships a local Tree-sitter-based structural graph that serves ~2,200–3,900 tokens of targeted context to MCP-connected coding agents. The headline number — 63× median token reduction — is measured against a "dump the whole repo" baseline. A tuned RAG pipeline is the comparison that matters in practice, and that comparison is not in the announcement.

## What it is

CRG is a Python 3.10+ tool that parses a repository into a node-edge graph using Tree-sitter, maintains it incrementally via file-watcher hooks, and exposes 30 MCP tools over a local HTTP server on localhost:5555. The idea is to replace the pattern where an agent receives the entire codebase (or a coarse vector-similarity retrieval) with something narrower: a subgraph of search hits plus neighbour edges, sized to fit in a few thousand tokens.

The graph stores structural relationships — function definitions, calls, imports — and optionally vector embeddings (default local model: all-MiniLM-L6-v2; optional Voyage voyage-code-3 at 1024 dimensions). Community detection uses Leiden clustering with a seed of 42. Communities above 25% of the graph get split recursively.

| Fact | Value | Condition / Source |
|------|-------|--------------------|
| Cold build time | ~40 s | ~3,000-file repo, Quick Start section |
| Incremental re-index (2-file edit) | ~2.5 s | django, ~3,000 files, hook path; ~1.4 s is process start-up |
| Median token reduction | 63× | 6 repos, whole-corpus baseline vs graph query |
| Max token reduction | 357.6× | fastapi, snapshot 22381558, 948,793 naive tokens → 2,653 graph tokens |
| Average F1 (blast radius) | 0.693 | 13 commits across 6 repos |
| Average precision (blast radius) | 0.546 | same evaluation set |
| Recall (blast radius) | 1.000 | all 13 commits |
| Graph tokens per question | 2,200–3,900 | typical agent question |
| MCP tools exposed | 30 | default configuration |
| Python requirement | 3.10+ | Quick Start |
| Default blast-radius depth | 2 | `CRG_MAX_IMPACT_DEPTH` |
| Default max impact nodes | 500 | `CRG_MAX_IMPACT_NODES` |
| Default BFS depth | 15 | `CRG_MAX_BFS_DEPTH` |
| Default embedding model | all-MiniLM-L6-v2 | local; `CRG_EMBEDDING_MODEL` |
| Default churn window | 90 days | `CRG_CHURN_WINDOW_DAYS` |

## The token reduction numbers and what they measure

The benchmark runs an evaluation runner against 6 open-source repositories at specific commit snapshots. The baseline is the total token count of the entire source corpus at that commit. The comparison is the average tokens the graph returns per question. Simple ratio.

| Repo | Snapshot | Naive corpus (tokens) | Avg graph tokens | Reduction |
|------|----------|----------------------|-----------------|-----------|
| fastapi | 22381558 | 948,793 | 2,653 | 357.6× |
| code-review-graph | 84bde354 | 208,821 | 3,190 | 65.5× |
| flask | a29f88ce | 143,594 | 2,196 | 65.4× |
| gin | 5c00df8a | 166,868 | 2,766 | 60.3× |
| httpx | b55d4635 | 142,356 | 2,661 | 53.5× |
| express | b4ab7d65 | 136,052 | 3,936 | 34.6× |

CRG's announcement explicitly notes that the 358× maximum is one repository (fastapi, the largest corpus) and "not the typical result." The median across the six is about 63×.

One other number in the announcement deserves a separate read. The claim that the token estimate is within ~1% of real tokens in aggregate across 222 sample files is a statement about the tool's own counter. It says nothing about the quality of the context the graph returns.

<figure class="my-8 rounded-2xl border border-border bg-card-bg p-6" data-quantity-figure="comparison">
  <div class="flex items-center justify-between text-xs font-mono uppercase tracking-widest text-primary-cta">
    <span>Reported by tirth8205/code-review-graph</span>
    <span>nodes<span class="ml-2 sm:hidden">· scroll →</span></span>
  </div>
  <div class="mt-4 overflow-x-auto">
  <svg class="w-full min-w-[600px]" viewBox="0 0 600 262" role="img" aria-label="Figure: values as tirth8205/code-review-graph reports them, drawn from quantities extracted and quote-verified for this post. Bars are proportional within their shared unit; condition cells are exact values and are not scaled against one another. The studio has not re-measured them." xmlns="http://www.w3.org/2000/svg">
    <rect width="600" height="262" rx="12" fill="var(--mp-canvas, #0d0f12)"/>
    <text x="20" y="40" fill="var(--mp-text, #fff)" font-size="13" font-family="monospace">node count for fastapi</text>
    <rect x="268" y="24" width="292" height="22" rx="4" fill="#1e40af" opacity="0.85"/>
    <text x="550" y="40" fill="#fff" font-size="12" font-family="monospace" text-anchor="end">6,287 nodes</text>
    <text x="20" y="86" fill="var(--mp-text, #fff)" font-size="13" font-family="monospace">node count for express</text>
    <rect x="268" y="70" width="92" height="22" rx="4" fill="#1d4ed8" opacity="0.85"/>
    <text x="350" y="86" fill="#fff" font-size="12" font-family="monospace" text-anchor="end">1,990 nodes</text>
    <text x="20" y="132" fill="var(--mp-text, #fff)" font-size="13" font-family="monospace">node count for gin</text>
    <rect x="268" y="116" width="74" height="22" rx="4" fill="#2563eb" opacity="0.85"/>
    <text x="352" y="132" fill="var(--mp-text, #fff)" font-size="12" font-family="monospace">1,589 nodes</text>
    <text x="20" y="178" fill="var(--mp-text, #fff)" font-size="13" font-family="monospace">node count</text>
    <rect x="268" y="162" width="67" height="22" rx="4" fill="#3b82f6" opacity="0.85"/>
    <text x="345" y="178" fill="var(--mp-text, #fff)" font-size="12" font-family="monospace">1,446 nodes</text>
    <text x="20" y="224" fill="var(--mp-text, #fff)" font-size="13" font-family="monospace">node count for flask</text>
    <rect x="268" y="208" width="66" height="22" rx="4" fill="#60a5fa" opacity="0.85"/>
    <text x="344" y="224" fill="var(--mp-text, #fff)" font-size="12" font-family="monospace">1,415 nodes</text>
  </svg>
  </div>
  <div class="mt-5 border-t border-border/70 pt-4">
    <div class="flex items-center justify-between gap-3">
      <p class="text-[0.65rem] font-mono uppercase tracking-widest text-muted-text">Conditions around the comparison</p>
      <span class="shrink-0 font-mono text-[0.6rem] uppercase tracking-wider text-muted-text sm:hidden">scroll →</span>
    </div>
    <div class="mt-3 overflow-x-auto">
    <dl class="grid min-w-[600px] grid-cols-4 gap-px overflow-hidden rounded-xl border border-border bg-border">
      <div class="min-w-0 bg-card-bg p-4">
        <dd class="font-mono text-lg font-semibold text-primary-text">40 seconds</dd>
        <dt class="mt-1.5 text-xs leading-5 text-muted-text">cold build time</dt>
      </div>
      <div class="min-w-0 bg-card-bg p-4">
        <dd class="font-mono text-lg font-semibold text-primary-text">2.5 seconds</dd>
        <dt class="mt-1.5 text-xs leading-5 text-muted-text">re-index time for a two-file edit, a ~3, 000-file project</dt>
      </div>
      <div class="min-w-0 bg-card-bg p-4">
        <dd class="font-mono text-lg font-semibold text-primary-text">1.4 s</dd>
        <dt class="mt-1.5 text-xs leading-5 text-muted-text">process start-up time</dt>
      </div>
      <div class="min-w-0 bg-card-bg p-4">
        <dd class="font-mono text-lg font-semibold text-primary-text">208,821 tokens</dd>
        <dt class="mt-1.5 text-xs leading-5 text-muted-text">source tokens</dt>
      </div>
    </dl>
    </div>
  </div>
  <figcaption class="mt-3 text-xs leading-5 text-muted-text">Figure: values as tirth8205/code-review-graph reports them, drawn from quantities extracted and quote-verified for this post. Bars are proportional within their shared unit; condition cells are exact values and are not scaled against one another. The studio has not re-measured them.</figcaption>
</figure>

## What it is useful for

**Agent context compression for mid-size repos.** If your AI coding agent is currently receiving 100k+ tokens of code context per question because your retrieval is coarse (whole files, no structural filtering), CRG's subgraph approach will cut that to a few thousand tokens. The 2.5-second incremental re-index on the hook path is fast enough that the graph stays current without manual intervention.

**Blast-radius analysis for PR review.** The tool recovers every ground-truth file on all 13 evaluation commits (recall = 1.0). If you use it to flag "which files might be affected by this change," it will not miss one. The cost is precision: at 0.546 average, roughly half the flagged files are false positives. For a human reviewer that is a manageable noise level. For an automated gate, it is not.

**What it is not useful for:**

- Replacing a well-tuned RAG pipeline. The 63× number is against a whole-corpus baseline. If your RAG already returns 3–5 relevant files totalling a few thousand tokens, the marginal savings from CRG are small.
- Very large monorepos. The largest tested repo is fastapi at 6,287 nodes. A monorepo with 50k+ files is outside the evaluated range.
- Semantic queries that go beyond call/definition structure. The graph is AST-based; it does not capture data flow, type inference, or runtime behaviour.

## What the announcement does not tell you

The F1 and token-reduction numbers are measured against ground-truth file lists from commits and against a whole-corpus token count, respectively. What neither metric captures is whether an agent actually produces a better patch, a more correct answer, or a shorter conversation when given CRG's subgraph context versus a well-tuned RAG retrieval. The announcement does not include an end-to-end agent quality comparison.

The blast-radius precision of 0.546 means the tool over-reports. The announcement does not characterise what kinds of edges produce false positives — whether they are indirect callers, import-chain neighbours, or community-membership artefacts. Without that, it is hard to know whether tuning `CRG_MAX_IMPACT_DEPTH` (default 2) or `CRG_MAX_TRANSITIVE_FRONTIER` (default 50) would meaningfully improve precision without sacrificing recall.

This is adjacent to the problem discussed in [ShikumiMiner's AST features failing cross-project](/blog/itria-arxiv-260902789v1): structural features extracted from one codebase do not automatically transfer to another, and CRG's per-repo graph construction is one way to sidestep that transfer problem entirely.

---

*Method: this note was drafted by qwen/qwen3.8-27b from a single source — the published README of [tirth8205/code-review-graph](https://github.com/tirth8205/code-review-graph). Before publication an automated gate re-checked every extracted claim against the source document (49 claim(s) and 75 quantity(ies) verified) and every claim-shaped number in the draft against that evidence (1 derived from it, no rows from our own tables were supplied to the draft). The studio has not re-run tirth8205/code-review-graph's benchmarks; figures attributed to it are its own.*

## Questions this note answers

### Does it work with my MCP client?

CRG serves over Streamable HTTP on localhost:5555. Any MCP client that supports that transport can connect. The default 30 tools can be filtered via configuration if you only need a subset. There is no stated browser or mobile client; it is a local daemon.

### How does the incremental update actually work?

A file-watcher daemon monitors the working tree. On change, it re-parses only the affected files and updates the affected subgraph. On a ~3,000-file repo (django) a two-file edit takes about 2.5 seconds on the hook path, of which ~1.4 seconds is process start-up. A health check every 30 seconds restarts dead watchers. The announcement does not state what happens on a large multi-file refactor or a branch switch.

### What is the precision-recall tradeoff in practice?

Recall is 1.0 across all 13 evaluation commits — the tool never omits a ground-truth file. Average precision is 0.546, meaning that for every two files it flags, roughly one is a false positive. Per-repo precision ranges from 0.785 (httpx) to 0.439 (gin). If you are using this to drive an automated "will this PR break anything?" gate, the false positive rate is high enough to expect manual review of the flagged list.

### What languages does it cover?

The six benchmark repos cover Python (fastapi, flask, httpx, code-review-graph), Go (gin), and JavaScript/TypeScript (express). The tool uses Tree-sitter, which supports many grammars, so in principle coverage extends to any language with a Tree-sitter parser. The announcement does not state a supported-language list or note which grammars are configured by default.
