Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ Call resolution the fork adds:

### Measured results

Upstream `main` at `69af19ac` against the fork at `bc27fdb4`, each run on Node.js 24.22.1 and on Bun 1.4.4. Measured 2026-10-27 on a 16-vCPU WSL2 host over seven corpora, with the arms in mirrored order; index figures are the median of two runs and sync figures the median of four. A comparison counts as a change only when the gap exceeds that comparison's own run-to-run spread — the larger of the two arms' (max − min) / median — and cells that cannot show that are marked inconclusive or no clear change rather than a fixed band. Upstream refuses to start on Bun, which reports itself as Node 26, because upstream blocks Node 25 and newer; it ran here with `CODEGRAPH_ALLOW_UNSAFE_NODE=1`. The host shared other work during the run, so spread-based cells carry more noise than a quiet host would; the numbers, the method, the graph sizes and the disclosures are in [`docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md`](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md). The scripts that produced them are in [`scripts/benchmarks/runtime/`](scripts/benchmarks/runtime/).
Upstream `main` at `69af19ac` against the fork at `c29349a3`, each run on Node.js 24.22.1 and on Bun 1.4.4. Index and sync figures were re-measured 2026-10-29 on the same 16-vCPU WSL2 host after the ambient-`declare module` scan fix below; MCP, command-line and explore figures are carried from the 2026-10-27 run at `bc27fdb4` (unchanged by a resolution-path change). Seven corpora, arms in mirrored order; index figures are the median of two runs and sync figures the median of four. A comparison counts as a change only when the gap exceeds that comparison's own run-to-run spread — the larger of the two arms' (max − min) / median — and cells that cannot show that are marked inconclusive or no clear change rather than a fixed band. Upstream refuses to start on Bun, which reports itself as Node 26, because upstream blocks Node 25 and newer; it ran here with `CODEGRAPH_ALLOW_UNSAFE_NODE=1`. The host shared other work during the run, so spread-based cells carry more noise than a quiet host would; the numbers, the method, the graph sizes and the disclosures are in [`docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md`](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md). The scripts that produced them are in [`scripts/benchmarks/runtime/`](scripts/benchmarks/runtime/).

![Fork against upstream CodeGraph on n8n and the command line: the fork indexes n8n 2.4 times slower, syncs an edited file 2.8 times faster, answers a warm question 1.8 times faster and uses 49% less memory while indexing. The fork is better in 56 of 76 comparisons, shows no clear change in 5 and is worse in 15.](assets/fork-vs-upstream-performance.svg?v=5)
![Fork against upstream CodeGraph on n8n and the command line: the fork indexes n8n 1.1 times faster, syncs an edited file 1.3 times faster, answers a warm question 1.8 times faster and uses 50% less memory while indexing. The fork is better in 58 of 76 comparisons, shows no clear change in 8 and is worse in 10.](assets/fork-vs-upstream-performance.svg?v=6)

The fork's one-file sync is faster than upstream's on six of the seven corpora — 11% (gin) to 64% (n8n) on Node, 19% (supabase, where upstream's spread is wide) to 57% (discourse) on Bun — with supabase on Node the one no-clear-change. Full indexing is the fork's weak surface this run: it is slower on five of seven corpora (supabase 2.3× and n8n 2.4× on Node; the supabase-resolution section below explains why), ties nowhere outside spread, and wins only on CPython (1.6× on Node). It uses less memory while indexing on all seven (18–49%). The 15 comparisons where the fork is worse are listed in the chart and the benchmark doc: six index-time cells, the busy-MCP memory on n8n (+18% on Node), the gin concurrent-explore wave, and small index-time cells on gin and Alamofire. The benchmark doc has [every result](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md#every-result-at-a-glance) in one heatmap.
The fork's one-file sync is faster than upstream's on six of the seven corpora on Node — 4% (pretix) to 56% (CPython), with supabase the one no-clear-change — and on Bun it wins every clear comparison but one, losing pretix by 11%. Full indexing is now a split surface: the fork wins on n8n (1.1×), CPython (1.6×) and discourse (1.4×) on Node, ties inside spread on gin and supabase-Node, and still loses on pretix (+33%) and Alamofire (+18%) and on the Bun arm of supabase, n8n and pretix. It uses less memory while indexing on all seven (18–50%). The 10 comparisons where the fork is worse are listed in the chart and the benchmark doc: five index-time cells (pretix on both runtimes, Alamofire on Node, supabase and n8n on Bun), the busy-MCP memory on n8n (+18% on Node), the gin concurrent-explore wave, a pretix-Bun sync cell and a gin file-watcher cell. The benchmark doc has [every result](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md#every-result-at-a-glance) in one heatmap.

The fork starts its resolver pool at or above 150,000 pending references. Below that, it can start the pool part-way through if at least 20,000 unresolved references remain and the estimated main-thread work is at least 0.4 s. An explicit `CODEGRAPH_PARALLEL_RESOLVE_MIN` replaces both gates. Against a pre-change baseline build, five tested corpora indexed 11-48% faster at 56-132% more peak memory with identical graphs ([measurements](docs/benchmarks/adaptive-pool-engage-2026-10-15.md)).

Expand All @@ -212,29 +212,29 @@ A [2026-10-15 mid-size comparison](docs/benchmarks/midsize-sweep-2026-10-15.md)

| Corpus | Upstream, Node | Fork, Node | Fork, Bun |
|---|---|---|---|
| gin (Go, 119 files) | 0.96 s, 559 MiB | 1.09 s, 392 MiB | 1.06 s, 296 MiB |
| Alamofire (Swift, 129 files) | 1.35 s, 879 MiB | 1.62 s, 632 MiB | 0.99 s, 664 MiB |
| pretix (Python + JS, 1,472 files) | 9.04 s, 3.29 GiB | 12.5 s, 2.71 GiB | 12.4 s, 2.42 GiB |
| CPython (C + Python, 3,708 files) | 55.0 s, 7.15 GiB | 33.8 s, 4.01 GiB | 33.5 s, 3.48 GiB |
| discourse (Ruby + JS, 20,277 files) | 19.6 s, 4.07 GiB | 22.2 s, 2.82 GiB | 20.8 s, 2.40 GiB |
| supabase (React + Next.js + TS, 10,716 files) | 18.2 s, 4.86 GiB | 41.5 s, 3.59 GiB | 40.6 s, 3.76 GiB |
| n8n (Vue + TS, 24,434 files) | 74.9 s, 12.2 GiB | 3:00, 6.27 GiB | 2:57, 6.33 GiB |
| gin (Go, 119 files) | 0.96 s, 559 MiB | 1.03 s, 396 MiB | 0.88 s, 296 MiB |
| Alamofire (Swift, 129 files) | 1.35 s, 879 MiB | 1.60 s, 648 MiB | 0.94 s, 662 MiB |
| pretix (Python + JS, 1,472 files) | 9.04 s, 3.29 GiB | 12.0 s, 2.71 GiB | 11.8 s, 2.44 GiB |
| CPython (C + Python, 3,708 files) | 55.0 s, 7.15 GiB | 33.4 s, 4.03 GiB | 33.3 s, 3.47 GiB |
| discourse (Ruby + JS, 20,277 files) | 19.6 s, 4.07 GiB | 14.4 s, 2.91 GiB | 13.2 s, 2.40 GiB |
| supabase (React + Next.js + TS, 10,716 files) | 18.2 s, 4.86 GiB | 19.6 s, 3.75 GiB | 18.7 s, 3.61 GiB |
| n8n (Vue + TS, 24,434 files) | 74.9 s, 12.2 GiB | 68.4 s, 6.18 GiB | 65.2 s, 6.61 GiB |

The fork's index is slower than upstream's on five of the seven corpora this run — clearly so on the two TypeScript-heavy monorepos (supabase 2.3×, n8n 2.4×) and pretix (+38%). The supabase and n8n regressions trace to upstream's ambient-`declare module` lookup (merged in fork #548): the Rust port scans every declaration file per out-of-repo import. A per-file memo ([#554](https://github.com/bompus/codegraph/pull/554)) ended the reparse-every-reference quadratic — without it the supabase index never finished — but the remaining scan is still per-reference × per-file work upstream does once per candidate name. The fix for the residual gap is tracked as follow-up work; the benchmark doc has the [full accounting](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md#the-supabase-resolution-regression). In return the fork's index uses 18–49% less memory on every corpus, and its one-file sync is faster on six of seven (below).
The fork's index was slower than upstream's on five of the seven corpora in the first pass of this run — the supabase and n8n regressions traced to upstream's ambient-`declare module` lookup (merged in fork #548): the Rust port scanned every declaration file per out-of-repo import. A per-file memo ([#554](https://github.com/bompus/codegraph/pull/554)) ended the reparse-every-reference quadratic, and the follow-up above lands the rest: a port of upstream's text tokenizer plus a `specifier → files` index, so each import checks only candidate declaration files instead of every `.d.ts`. The re-measured numbers in this table are the result: supabase went from 41.5 s to 19.6 s on Node and n8n from 178 s to 68.4 s — supabase now ties upstream inside spread and n8n beats it. The remaining losses are pretix (+33% Node) and Alamofire (+18% Node), whose index time is not ambient-scan work, and the Bun arm of supabase (+14%) and n8n (+10%). The benchmark doc has the [full accounting](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-27.md#the-supabase-resolution-regression). In return the fork's index uses 18–50% less memory on every corpus, and its one-file sync is faster on six of seven (below).

Upstream at `6560052` took 2.0 to 2.7 times as long to index Python as at `290e03f` (pretix 9.2 s to 18.8 s, CPython 35.6 s to 95.3 s; [a same-host check](docs/benchmarks/fork-vs-upstream-node-bun-2026-10-03.md#since-2026-09-28) put the cause in upstream's code). At `69af19ac` upstream's CPython index is slower than at `986de95c` (55.0 s against 40.2 s on this host) while the fork's is unchanged, so the fork's CPython lead is 1.6× this run.

**One-file sync** (edit one file, `codegraph sync`):

| Corpus | Upstream, Node | Fork, Node | Fork, Bun |
|---|---|---|---|
| gin | 0.47 s | 0.42 s | 0.32 s |
| Alamofire | 0.48 s | 0.32 s | 0.21 s |
| pretix | 1.62 s | 1.07 s | 0.81 s |
| CPython | 5.35 s | 2.42 s | 1.70 s |
| discourse | 4.78 s | 2.31 s | 1.73 s |
| supabase | 4.58 s | 3.37 s | 2.34 s |
| n8n | 11.6 s | 4.18 s | 3.41 s |
| gin | 0.47 s | 0.40 s | 0.29 s |
| Alamofire | 0.48 s | 0.29 s | 0.18 s |
| pretix | 1.62 s | 1.55 s | 1.27 s |
| CPython | 5.35 s | 2.37 s | 1.62 s |
| discourse | 4.78 s | 2.28 s | 1.66 s |
| supabase | 4.58 s | 2.83 s | 1.90 s |
| n8n | 11.6 s | 8.90 s | 8.02 s |

Upstream's supabase sync works in this run — the `FOREIGN KEY constraint failed` seen in every prior run ([colbymchenry/codegraph#2565](https://github.com/colbymchenry/codegraph/issues/2565)) was fixed upstream on 2026-10-19. Its two passes differed widely on Node (1.05 s and 8.1 s); the 4.58 s figure is the median of four, and the spread means that comparison counts as no clear change rather than a fork win.

Expand Down Expand Up @@ -263,7 +263,7 @@ The fork-Node server's memory *while busy* is 18% above upstream's on n8n this r
| `codegraph status` | 166 ms | 114 ms | 63 ms |
| `codegraph explore "<query>"` | 220 ms | 117 ms | 71 ms |

**Node or Bun.** The fork produces the same node and edge counts on both, and the full test suite passed on Node.js 24.21.0, Node.js 26.10.0 and Bun 1.4.2 at `48903f5` (`npm run test:bun`; one test is skipped under Bun for [oven-sh/bun#42891](https://github.com/oven-sh/bun/issues/42891)). Since then the suite has grown, and the Bun run skips 14 more tests that need what Bun does not provide: patching a builtin so the library sees it (`syncBuiltinESMExports` is a no-op, [oven-sh/bun#42616](https://github.com/oven-sh/bun/pull/42616)) and an emptied `PATH` blocking a spawn ([oven-sh/bun#44948](https://github.com/oven-sh/bun/issues/44948)). Each skip is measured at run time, so the test returns when Bun supports it. On Bun 1.4.4, fork index times match Node's within each corpus's own spread except Alamofire (−39%), sync times run 19–36% faster, and commands start faster (`--version` 21 ms against 35 ms). The idle MCP server holds 29% (gin) to 62% (n8n) less memory on Bun. Bun has one cost this run: eight concurrent explores on gin took 20% longer (161 ms against 134 ms); on n8n the wave runs 15% faster (1.63 s against 1.91 s). Peak index memory on Bun is 11–25% lower than Node on four corpora and 2–6% higher on Alamofire, supabase and n8n — no uniform direction.
**Node or Bun.** The fork produces the same node and edge counts on both, and the full test suite passed on Node.js 24.21.0, Node.js 26.10.0 and Bun 1.4.2 at `48903f5` (`npm run test:bun`; one test is skipped under Bun for [oven-sh/bun#42891](https://github.com/oven-sh/bun/issues/42891)). Since then the suite has grown, and the Bun run skips 14 more tests that need what Bun does not provide: patching a builtin so the library sees it (`syncBuiltinESMExports` is a no-op, [oven-sh/bun#42616](https://github.com/oven-sh/bun/pull/42616)) and an emptied `PATH` blocking a spawn ([oven-sh/bun#44948](https://github.com/oven-sh/bun/issues/44948)). Each skip is measured at run time, so the test returns when Bun supports it. On Bun 1.4.4, fork index times match Node's within each corpus's own spread except Alamofire (−41%), sync times run 10–38% faster, and commands start faster (`--version` 21 ms against 35 ms). The idle MCP server holds 29% (gin) to 62% (n8n) less memory on Bun. Bun has one cost this run: eight concurrent explores on gin took 20% longer (161 ms against 134 ms); on n8n the wave runs 15% faster (1.63 s against 1.91 s). Peak index memory on Bun is 4–25% lower than Node on five corpora and 2–7% higher on Alamofire and n8n — no uniform direction.

Measured separately:

Expand All @@ -280,8 +280,8 @@ The [benchmark](#benchmark-results) and [speed](#built-for-speed--the-rust-kerne

- **Larger database:** 11–20% bigger on six of the seven corpora above, and 37% on supabase, which has 1,978 Markdown files. It holds Markdown, binding rows and more nodes.
- **Fewer edges on some projects:** 8% fewer on pretix and 2% fewer on CPython, because the fork declines links it cannot confirm. Some of those were correct links.
- **Slower to index most projects this run:** in the 2026-10-27 comparison the fork's full index is slower on five of seven corpora — supabase 2.3× and n8n 2.4× on Node, pretix +38% (+72% on Bun), Alamofire +20%, discourse +13% (+15% on Bun) and gin +14% on Node; only CPython is faster (1.6×). The TypeScript-monorepo regressions trace to the ambient-`declare module` port described above; the residual gap is tracked follow-up work. One-file syncs still peak higher on some corpora (2026-10-18 run: CPython 1.23 GiB against 909 MiB on Node, +39%; Alamofire +14%; gin +10%; discourse +4%).
- **On Bun:** slower concurrent explores on small projects (gin wave +20% this run), and a mixed peak-memory picture — 11–25% lower on four corpora, 2–6% higher on three.
- **Slower to index some projects this run:** in the 2026-10-27/29 comparison the fork's full index loses on two Node cells — pretix +33% and Alamofire +18% — and on the Bun arm of supabase (+14%), n8n (+10%) and pretix (+64%); it wins n8n (1.1×), CPython (1.6×) and discourse (1.4×) on Node and ties gin (both runtimes) and supabase-Node inside spread. The earlier TypeScript-monorepo index regressions traced to the ambient-`declare module` port described above and are closed by this change's text-scan + specifier index. One-file syncs still peak higher on some corpora (2026-10-18 run: CPython 1.23 GiB against 909 MiB on Node, +39%; Alamofire +14%; gin +10%; discourse +4%).
- **On Bun:** slower concurrent explores on small projects (gin wave +20% this run), and a mixed peak-memory picture — 4–25% lower on five corpora, 2–7% higher on two.

---

Expand Down
118 changes: 118 additions & 0 deletions __tests__/evaluation/results/precision-exposed-2155404-21141301.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"timestamp": "2026-10-27T16:16:50.659Z",
"corpus": "exposed",
"repo": "https://github.com/JetBrains/Exposed.git",
"commit": "2155404",
"codegraphSha": "21141301",
"summary": {
"absentCases": 2,
"absentHeld": 2,
"presentCases": 6,
"presentHeld": 6,
"unjudgeable": 0
},
"resolvedBy": {
"exact-match": 33266,
"(none)": 26630,
"import": 7391,
"instance-method": 3903,
"framework": 2463,
"function-ref": 1097,
"file-path": 221
},
"edges": 74971,
"results": [
{
"caseId": "exposed-literal-trim-jdbc",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:b50c90376f77d8d0aca5d309ac09f69c",
"to": "method:9d77982d7d95880e2a54c4893b86a838",
"resolvedBy": "exact-match"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-literal-trim-r2dbc",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:05217ebf2e2d2da1556291c5c369839a",
"to": "method:9d77982d7d95880e2a54c4893b86a838",
"resolvedBy": "exact-match"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-column-count-extension",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:941657a44efa96b0b33ffbd2175bb09b",
"to": "method:495855c626bbbf0a54e3d191aa2fae92",
"resolvedBy": "exact-match"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-column-substring-extension",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:33263165fe66809c5f378e014e92784d",
"to": "method:8d9c8fca9a37a0223dc1a4ea8cdaccaa",
"resolvedBy": "exact-match"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-column-contains-extension",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:212c066478bc17c1ac27f6f14b109aee",
"to": "method:0d2884b38da4f463426a16b42399df20",
"resolvedBy": "exact-match"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-column-not-query-count",
"expect": "absent",
"pass": true,
"found": [],
"missingEndpoints": []
},
{
"caseId": "exposed-wildcard-package-receiver",
"expect": "present",
"pass": true,
"found": [
{
"from": "method:f40372af7132a6726971d7163deef334",
"to": "method:71e425ce8fd2d25d06c04cf318012e1a",
"resolvedBy": "instance-method"
}
],
"missingEndpoints": []
},
{
"caseId": "exposed-wildcard-not-other-package",
"expect": "absent",
"pass": true,
"found": [],
"missingEndpoints": []
}
]
}
60 changes: 60 additions & 0 deletions __tests__/evaluation/results/precision-flask-d73fa1c-21141301.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"timestamp": "2026-10-27T16:16:15.933Z",
"corpus": "flask",
"repo": "https://github.com/pallets/flask.git",
"commit": "d73fa1c",
"codegraphSha": "21141301",
"summary": {
"absentCases": 0,
"absentHeld": 0,
"presentCases": 2,
"presentHeld": 2,
"unjudgeable": 0
},
"resolvedBy": {
"(none)": 2474,
"import": 1180,
"instance-method": 736,
"exact-match": 633,
"function-ref": 191
},
"edges": 5214,
"results": [
{
"caseId": "flask-package-reexport-receiver",
"expect": "present",
"pass": true,
"found": [
{
"from": "file:examples/tutorial/flaskr/auth.py",
"to": "method:31833b8026746e7977b4c2736d71c63e",
"resolvedBy": "instance-method"
},
{
"from": "file:examples/tutorial/flaskr/auth.py",
"to": "method:31833b8026746e7977b4c2736d71c63e",
"resolvedBy": "instance-method"
},
{
"from": "file:examples/tutorial/flaskr/auth.py",
"to": "method:31833b8026746e7977b4c2736d71c63e",
"resolvedBy": "instance-method"
}
],
"missingEndpoints": []
},
{
"caseId": "flask-control-relative-from-import",
"expect": "present",
"pass": true,
"found": [
{
"from": "file:src/flask/app.py",
"to": "function:00d6010e49ed947bc5858174234b23e4",
"resolvedBy": "import"
}
],
"missingEndpoints": []
}
]
}
Loading
Loading