Kevinkv/#
Codebase knowledge graph

The diagrams are derived, not drawn.

Kevin indexes its own source tree into a queryable graph of files, symbols, calls and bus topics. No model is called: it is AST extraction, so a refresh is deterministic and costs nothing. Every architecture diagram on this site is then tied by manifest to the code paths that determine it, and when those paths change the diagram is marked stale and a render job is queued. That is why the catalogue at /architecture/ tracks the system instead of describing a version of it that stopped existing in March.

does work stateful data third party initiates this page is drawn in the same grammar the generated SVGs use

One refresh, end to end

Every node below is a real file. One script composes them: scripts/graphify-subsystem.ps1

A delivery

The refresh runs once per delivery, before the commit that gets pushed. A nightly full pass catches drift.

Scope compile

Compiles the allow/deny list into a marked generated block in .graphifyignore, so a scope edit takes effect on this run.

daemon/src/graphify_index.py

AST extraction

Third-party engine: the graphifyy package. We did not write the parser and do not claim to.

Contract merge

Folds the declared bus domains and topics in as first-class nodes, so what the system says it publishes sits in the same graph as what the code does.

daemon/src/data_dictionary_graph_export.py

graph.json

The working graph. Local and gitignored: large, and regenerable from source at any time.

graphify-out/graph.json

graph.kvg

The same graph packed into a stamped container that names its own build, checksums its payload, and tombstones the artifact it replaces.

graphify-out/graph.kvg

The refresh is advisory infrastructure, never a delivery gate. Every internal step degrades to a warning and the script exits zero unless you ask it not to. A knowledge artifact that can block a release is a knowledge artifact people learn to route around.

Scope is a config file. The artifact is a format.

Two things decide whether a codebase graph is still usable a month after you build it: whether it indexes your code rather than your dependencies, and whether a consumer can tell how old it is. Both are explicit here.

What gets indexed

graphify-index.yaml: an allow-list, a deny-list, and nothing else

# only: non-empty = index ONLY these (allow-list)
only: []

# exclude: vendored + generated trees
exclude:
  - tools/frontend-dev   # vendored
  - tools/kevin-mail     # vendored
  - tools/mqttclient     # vendored
  - daemon/.test_runs

Built-in skips (node_modules, dist, venv, __pycache__) always apply and are not listed. The refresh compiles this file into .graphifyignore, and you never hand-edit the generated block. It is also editable from the operations console, because the person who notices a vendored tree landed is not always the person holding a shell.

On this repository, scoping is the difference between a scan of roughly 3,700 first-party files and one of 8,700, the figures recorded in the repo's own CLAUDE.md when the scope control was added. An unscoped graph is not more complete. It is slower, and most of it is someone else's code.

The .kvg container

daemon/src/kvgraph_format.py: magic header, JSON metadata, compressed payload

Meta fieldWhy it is there
generatedWhen this artifact was built. A consumer can decide it is too old without opening the payload.
supersedesThe stamp of the artifact this one replaces. Latest always wins, so two copies are never ambiguous.
commitThe git short SHA the graph was extracted from.
nodes
edges
communities
Readable from the header alone, without decompressing.
raw_sha256Integrity of the uncompressed graph. A mismatch raises on decode rather than returning a plausible-looking graph.
codecDefaults to lzma because it is stdlib. Any consumer decodes a .kvg with zero third-party dependencies.

It unpacks with one command

# header only, no decompression
python daemon/src/kvgraph_format.py info graphify-out/graph.kvg

# back to plain JSON
python daemon/src/kvgraph_format.py unpack \
  graphify-out/graph.kvg out.json

Packing this repository's graph on one recorded build took it from about 45 MB of JSON to about 2.2 MB. The compression is lossless, so that ratio is a property of that particular graph and yours will differ. The pack step prints its own. Treat the figure above as an example of the output, not as a benchmark.

What actually reads it

A graph nothing queries is a diagram with extra steps. Four things read this one, and each is a code path you can open.

Agent retrieval

query, path and explain traverse the graph under a token budget: breadth-first for broad context, depth-first to trace one route, shortest path between two symbols. An agent answering "what calls this" walks edges instead of grepping a tree.

Retrieval prompt context

The retrieval router pulls structured graph context alongside vector search and prepends it to the domain prompt, but only for domains on an explicit list. Injecting code-graph context into unrelated domains adds noise and blows the token budget, so it is opt-in per domain rather than global.

plugins/rag-router/graph_bridge.py

Ticket to code impact

Decodes the .kvg and answers "which modules does this ticket touch". One implementation, served over the API, with the graph's generated stamp and a staleness flag on every result. A stale answer is visible, never silent.

daemon/src/kanban_index.py

The navigable views

Two HTML views are tracked in git and served by the daemon at /api/graphify/tree.html and /api/graphify/callflow.html. The force-directed viewer is deliberately not generated: this repository's graph runs to roughly 95,000 nodes, past the point where an interactive force layout is useful, so the tree and the call-flow are what ship.

The call-flow view groups graph communities under our own section names, compiled from a repo-local config on every run. Left alone, the upstream package scores communities against a table describing its own repository, which is how a delivered view ends up narrating your system using someone else's headings.

Where it meets cost

The ticket id is the correlation key across the platform. The graph turns a ticket into the modules it touches; the fabric ledger writes that same ticket onto every request row it records. One key, two artifacts: what a change touched, and what the work on it cost.

Stated precisely, because the distinction matters. The ledger records routed cost, tokens, provider, latency and the routed-versus-baseline counterfactual per request, so a savings rollup is a query. What it will not do is backfill: rows written before those columns existed carry an unknown baseline, are stored as unknown rather than zero, and are excluded from the figure rather than counted as having saved nothing.

How a diagram stays true

This is the claim worth being exact about, because the loose version of it is a lie. The SVGs are not plotted automatically out of graph.json. They are derived: each one declares the source paths that determine it, staleness is detected mechanically against git, and the render follows a fixed contract that a validator enforces.

The manifest

One catalogue entry per diagram: slug, category, summary, status, last built, and the watch paths it is derived from. Everything else in the pipeline reads this file.

architecture/MANIFEST.yaml

Staleness detection

The refresh asks git which watch paths changed in the window, then queues a render job for each diagram those paths belong to. A diagram whose source moved cannot quietly stay current.

scripts/update-diagrams.ps1

The render contract

Five shape classes, four edge classes, fixed meanings, a required region order, and acceptance bounds: minimum and maximum file size, well-formed XML, ISO-dated subtitle, version in the title. An SVG using any class outside the canonical set is rejected.

architecture/STYLE_GUIDE.md

The grammar is the one this page is built in, which is the point. Cyan means code that does work. Pink means stateful data. Dashed green means a third-party system. Two people reading the same diagram get the same answer about what a box is, because the colour is a type and not a decoration. Filenames carry the version and subtitles carry the build date, both from a resolver rather than from whoever last edited the file.

What this is not

A knowledge graph is an easy thing to oversell, so here is the shape of it that survives a code review.

Worth having when…

  • Your architecture documentation is already wrong and nobody can tell you which parts.
  • You want agents answering questions about your code from a structured index rather than a grep and a guess.
  • You need "which modules does this ticket touch" to be one answer with a timestamp, not five opinions.
  • Your repository has vendored trees that swamp any naive index.
  • You want extraction to run on your own machine, offline, at no per-run cost.

Where it falls short…

  • The graph is a local, regenerable artifact. It is gitignored and not delivered over git. A fresh checkout has no graph until one is built. The tooling rebuilds it on entry, but nothing about a clone is guaranteed current.
  • Static analysis has blind spots. A topic published behind a variable, a dynamic import, a call through reflection: none of it lands in an AST-derived graph. These are logged rather than hidden, but they are not resolved.
  • The diagram render is not unattended. The pipeline detects staleness and queues the job; an agent produces the SVG and a person merges it. A diagram can sit stale in that queue, and the process is advisory by design, so it will never block your release.
  • Deep semantic extraction is not the cheap path. The per-delivery refresh is AST-only. Richer inferred edges mean a heavier run with models in the loop, which is a deliberate and separate operation.
  • This is not a compliance artifact. It is not an SBOM and not a provenance attestation, and it carries no SOC 2, HIPAA or FedRAMP standing, because Kevin has none. If your requirement is an audited supply-chain record, this does not satisfy it and no architecture argument makes it.
  • If you want a hosted product, buy one. Code-intelligence vendors ship a UI, a search index, support and an SLA. This is infrastructure inside a platform you run, and it assumes you are comfortable at that level.