* perf(resolution): resolve imports to definitions, not sibling import nodes (#915) "Resolving refs" crawled (tens of minutes) on large projects — most painfully ones mixing a big front-end and back-end. An external package or module imported across hundreds/thousands of files (react, a shared UI package, Python logging/typing) is re-declared as an `import` node in every importing file, so its unresolved import ref fell through to the exact-name matcher, which scored all K same-named import nodes via findBestMatch — K refs x K candidates = O(K^2) per package, producing only meaningless import->import edges. Fix: exclude `import`-kind nodes as name-match targets (they're statements, not definitions; real import->definition resolution is the import resolver's job). Plus two safe constant-factor wins in findBestMatch: hoist the per-candidate ref.filePath split, and skip cross-language candidates when a same-language one exists (provably the same winner — same-language scores >=50, cross-language maxes at 35). Measured: superset (Py+TS) candidates scored 7.5M -> 833K (9x), non-import edges preserved (+1618 now resolve to real defs), ~22K useless import->import edges removed; kubernetes (Go) computePathProximity 37.2s -> 5.0s; synthetic 8k-file mixed repo (K=4000) resolution 16.0s -> 1.7s. Full suite green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct stale better-sqlite3/wasm references to node:sqlite The SQLite backend has been Node's built-in node:sqlite (real SQLite, WAL + FTS5, from the bundled runtime) for a while — there is no native build step and no node-sqlite3-wasm fallback. README and the docs site were already updated; this catches the stragglers: - CLAUDE.md: the src/db/ backend description and the sqlite-backend test note. - src/db/index.ts, src/mcp/tools.ts: two code comments that still blamed "the wasm backend" for non-WAL behavior (reworded to "when WAL isn't in effect"). Leaves tree-sitter grammar wasm (web-tree-sitter / --liftoff-only) untouched — that's a different, still-current use of wasm. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(telemetry): drop the dead sqlite_backend field (schema v2) node:sqlite is now the only backend, so the `index` event's `sqlite_backend` field was a constant ("native") carrying no signal — and the `install` event never actually sent it. Remove the field and the backendKind() helper, bump the telemetry SCHEMA_VERSION 1 -> 2, and update TELEMETRY.md + docs/design/telemetry.md. The ingest worker is deliberately left tolerant: `index` doesn't require the field and schema_version validates as nonNegInt(99), so v2 events ingest fine and old clients still sending v1 + sqlite_backend keep validating too. Added a legacy comment there explaining it's safe to drop once old-client share is negligible. telemetry.test.ts: the assertion pinning schema_version and a stale-claim fixture line updated 1 -> 2. All telemetry tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codegraph telemetry ingest worker
The first-party endpoint behind telemetry.getcodegraph.com. This directory is in the
public repo on purpose: it is the exact code that receives codegraph's anonymous usage
telemetry, so anyone can audit what is stored. The schema contract (every event, every
field, and everything that is never collected) is in
docs/design/telemetry.md.
What it does, in one breath: validates incoming batches against a strict allowlist (unknown
events dropped, unknown properties stripped), never reads or forwards the client IP,
rate-limits per machine ID, and forwards to PostHog off the response path. It ships nowhere
with the npm package — the engine's files allowlist excludes it.
Endpoint contract
POST /v1/events— JSON body: envelope (machine_idUUID,codegraph_version,os,arch,node_major,ci,schema_version) +events: [{event, ts?, props?}]. Responds204when accepted (including events dropped by the allowlist), honest4xxfor malformed/oversized/rate-limited requests. Clients treat every response as final — no retries.GET /— plain-text pointer to the docs and the off-switches.
Deploy
Prereqs: the getcodegraph.com zone on the deploying Cloudflare account (the custom
domain route auto-provisions DNS + cert), wrangler ≥ 4.36 (the ratelimits binding).
cd telemetry-worker
npm install
npx wrangler login # once
npx wrangler secret put POSTHOG_KEY # the phc_… project write key — never committed
npm run deploy
The PostHog project itself must have "Discard client IP data" enabled — defense in
depth on top of this worker never forwarding IPs ($geoip_disable is also set per event).
Local dev & checks
cp .dev.vars.example .dev.vars # placeholder key; also feeds `wrangler types`
npm run check # wrangler types + tsc --noEmit + deploy --dry-run
npm run dev # http://localhost:8787
curl -i localhost:8787/v1/events -H 'content-type: application/json' -d '{
"machine_id": "00000000-0000-4000-8000-000000000000",
"codegraph_version": "0.9.9", "os": "darwin", "arch": "arm64",
"node_major": 22, "ci": false, "schema_version": 1,
"events": [{ "event": "usage_rollup",
"props": { "kind": "mcp_tool", "name": "codegraph_explore",
"count": 12, "error_count": 0, "client_name": "Claude Code" } }]
}'
Changing the schema
The allowlist in src/index.ts mirrors docs/design/telemetry.md (and the user-facing
TELEMETRY.md). A field is added by one PR touching all of them together — that is the
whole point of the design.