Skip to content

Architecture

Every language plugs in behind one interface: parse() produces the structural graph; concepts() produces idiom annotations for the lens. Python uses the stdlib ast module; JavaScript/TypeScript use the official tree-sitter grammar wheels. Nothing above the seam hardcodes a language.

One project parser selects adapters by extension, merges their graphs, resolves Home globally, and rejects node-ID or file-hash conflicts. Adapters walk files in stable order, keep syntax-error files visible as partial modules, and record project, external, and unresolved relationships without guessing. Exact path and unique-name resolution can be certain; extension substitution, extensionless resolution, and ambiguous candidates remain labeled possible.

The graph layer computes everything the renderer needs — language, size, centrality, entrypoint rank, region, understood-state — and the 3D frontend is a pure consumer. No layout or game logic lives in the renderer. This is what keeps a future read-only share link (and any alternative renderer) cheap.

Graph JSON is schema-versioned and byte-deterministic. Schema 4 includes stable node IDs, source spans, regions, entrypoint ranks, call in-degree, file hashes, parser-owned concept annotations, and explicit certainty/external flags on edges. It also separates parser-ranked entrypoint candidates from the explicit Home selection, so ambiguous rank-zero candidates remain unselected until the learner chooses. Concept annotations contain the exact node, line span, and source snippet that the Lens is allowed to teach. Each annotation also carries its language so identically named Python and JS/TS concepts remain separate in the star chart. The file hashes are the cache and progress invalidation key.

The mixed-project language focus is a pure frontend projection over that graph. It retains the original node and region records, coordinates, metadata, uncertainty, progress, and outward external/unresolved edges. No filtered view is written back to the parser or persistence layer.

Structure comes from parsers. Check answers come from the graph. The model’s job is prose: explaining code it is shown, teaching idioms the parser found. Every explanation links to real file:line so you can verify it yourself.

The server exposes one deep study interface. It loads the selected source span, collects parser-proven neighbors, builds the correctness-contract prompt, calls the configured provider, validates every returned line and relationship, and only then writes a local cache entry keyed by provider, model, node, and file hash. Invalid provider output is withheld rather than softened into a guess.

Lens notes take a separate, model-free path: the language adapter emits a concept ID only for a proven syntax node, and a deterministic language module maps that ID to a teachable note. The star chart aggregates those same graph annotations. Studied state is ephemeral; understood state remains check-owned.

Checks use another deep interface with no provider dependency. CheckService derives stable question suites from certain calls, project imports, direct callers, and entrypoint ranks, then validates exact option IDs against those generated answers. Only a completed suite asks ProgressStore to persist the region. The store projects valid file signatures onto immutable nodes and regions when graph JSON is requested; stale signatures simply remain dim.

Python 3.11+ · FastAPI · tree-sitter · Vite + React · 3d-force-graph (three.js) · plain SVG for the 2D map · Anthropic / OpenAI (bring your own key) · local JSON persistence.

Both the 3D galaxy coordinates and the 2D map layouts are computed in codemble/graph/ and served as data — GET /api/graph and GET /api/map. The renderer places what the graph already decided. That is why “same code → same sky” holds, and why adding a second renderer needed no second source of truth.