Sheet 03 · Methods & measurements

Why it works.

Every number here is reproducible. The relationships Synaptic extracts are validated against hand-labeled ground truth, and where the data is unflattering, it stays in.

Note 01 · how it is built

Structure, computed for you.

Principle 01

Parsed, not guessed

tree-sitter parses 30+ languages into concrete syntax, and Synaptic resolves call, import, and reference edges from it, then clusters the graph into communities so god nodes, surprising connections, and import cycles surface on their own.

Principle 02

Confidence you can audit

Every inferred relationship is tagged EXTRACTED, INFERRED, or AMBIGUOUS. A "0 dependents" answer carries a caveat when a symbol is reachable only through dynamic dispatch, so absence of evidence is never dressed up as proof.

Principle 03

Offline by default

A code-only corpus never makes a network call. The only feature that needs an API key is the optional semantic pass over docs and papers. Everything structural runs locally.

FIG. EXTRACTION source tree-sitter parse graph + communities
Fig. 2 · token economy

What one query costs.

Querying a compact graph costs a fraction of feeding raw files to an LLM. Measured on Synaptic's own source: 199 Rust files, 56,408 lines, 510,966 cl100k tokens.

Read the source
60,900
One Synaptic query
1,950
31×

fewer tokens overall, and a query response stays small no matter how big the repo gets, so the ratio grows with the codebase.

QuestionQuery responseRead the filesFewer tokens
http request handling1,80448,80327×
session create / reap1,97465,57833×
query_graph subgraph2,01153,75927×
extraction walker1,97770,44336×
PR fetch / rank1,92673,23138×
incremental merge2,01053,44027×

Exact cl100k_base counts via cargo run -p synaptic-server --example tokcount. The baseline is the unique source files the result's nodes live in, the conservative grep-then-read case. The graph.json index itself is large because it encodes every symbol and edge; you never load it into context, you query it and get back only the slice.

Test report 03 · accuracy

Validated against hand-labeled ground truth.

A corpus of mini-repos whose true call edges, test linkages, blast radii (including distractors that must not be flagged), and cross-language couplings are written out by hand. A preflight fails the run if any labeled symbol doesn't resolve, so a dropped node becomes a loud failure, not a quietly smaller denominator.

FixtureFamilyCall P/R/F1Aff-test recBlast rec/excl/sizeCross P/R/F1
systems-rustsystems-rust100/50/66100% / 100% / 1.0
scripting-pythonscripting-python100/100/100100%100% / 100% / 2.0
web-tsweb-ts100/100/100100% / 100% / 1.0
oo-javaoo-java100/100/100100% / 100% / 1.0
systems-gosystems-go100/100/100100% / 100% / 1.0
deep-pythonscripting-python100/100/100100%100% / 100% / 3.0
cross-lang-ts-rustcross-lang100/100/100

Across 7 fixtures / 6 language families / 26 labeled symbols (all resolved): pooled call edges precision 100% / recall 93% / F1 96%; blast-radius recall 100% with 0 distractors leaked; affected-test recall 100% with the one labeled unrelated test correctly not selected; cross-language precision 100% / recall 100%.

Reading it honestly: the 50% recall on Rust is real and expected. Rust call resolution is intra-file, so a module-qualified cross-file call is a true miss. Cross-file reachability is still preserved through imports edges, which is why blast-radius recall stays 100%. The corpus is intentionally small and hand-verified: it validates extraction correctness on representative shapes, not internet-scale coverage.

Test report 04 · throughput

It scales with content, not against it.

Extraction throughput across real OSS repos, each cloned at a pinned SHA, median of 3 reps. Cold clears the AST cache; warm is cache-hot; incr re-extracts a single file. Measured on Windows / x86_64 / 16 logical CPUs.

RepoTierFilesNodesEdgesCold (s)Warm (s)Incr (s)
memchrsmall753,84913,59212.57.54.3
clickmedium1122,1893,4752.41.70.8
p-mapsmall1085830.070.040.04
cobramedium558462,3621.10.70.4
axumlarge3483,6569,5104.73.63.5

Absolute times are machine-dependent; the reproducible signals are the cold→warm ratio (about 1.4 to 2×, the Rust AST cache removing re-parsing) and that throughput tracks repo content rather than collapsing on the large tier. memchr is slow per-file on purpose to show it: macro-heavy and edge-dense (13.6k edges over 75 files), which the benchmark surfaces rather than hides.

Test report 05 · calibration

When the metric says we're wrong, we publish it.

The change-forecast layer attaches a confidence to each predicted co-change. synaptic eval calibrate measures whether that confidence is meaningful: a reliability table, a Brier score, the Brier skill score against an always-guess-the-base-rate baseline, and expected calibration error.

On this repo's own squash-heavy, synthetic history the skill score is negative: co-change prediction there is worse than guessing the base rate, because squashed commits touch many files at once and inflate apparent co-change. That is the metric working: it refuses to dress up a predictor that is miscalibrated on this history. Confidence is a per-repo property, so run it on yours.

Synaptic · sheet 03 of 04 · rev 0.3.12 Reproduce it

Run it on your own repo.