# Coupling evidence

Doberman's architecture claims (a decoupled core, one decision path, no proxy reaching into policy) are measurable in a knowledge graph built over the repository. This page shows what that graph says.

## What the graph is

The graph is built by running a code-graphing tool (`graphify`) over the Doberman codebase: every file, class, function, and symbol becomes a node, and every reference between them (a call, an import, a type use) becomes an edge. Each edge carries a provenance tag: `EXTRACTED` means it was parsed directly out of the code (an import statement, a function call); `INFERRED` means a model estimated the relationship and attached a confidence score. Treat a surprising `INFERRED` edge as a hypothesis to check, not a fact.

The current build measures 13,873 nodes and 33,207 edges, about two-thirds `EXTRACTED` and one-third `INFERRED`. The numbers on this page come from the 2026-08-25 build; the graph is regenerated from the repository, so rebuilding it is the way to check them against the current code.

## Subsystem coupling

The table below aggregates cross-subsystem edges: how many references point from one subsystem's code into another's, and what kind of reference dominates.

| Subsystem pair | Edges | Dominant relation | Reading |
|---|---|---|---|
| models ↔ turngate | 206 | uses | the turn gate speaks the shared vocabulary |
| engine.rules ↔ models | 202 | uses | rules consume, never redefine, the shared types |
| auth ↔ tests | 146 | uses, calls | the challenge chain is directly exercised |
| storage ↔ tests | 110 | calls (80) | the heaviest call-level test coverage in the repo |
| models ↔ proxy | 96 | uses | the proxy depends on models, one-way |
| auth ↔ turngate | 63 | uses | step-ups from the turn gate reuse the auth tier |
| models ↔ subjective | 51 | uses | the subjective layer consumes the action vocabulary |
| cli ↔ policy | 48 | calls (47) | the CLI is the drift gate's biggest caller |
| auth ↔ hosthooks | 44 | uses | the host-hook deny path resolves AUTH through the shared chain |
| auth ↔ proxy | 29 | uses | the proxy's `_handle_auth` uses the same chain |
| proxy ↔ policy core | 0 imports | none | the boundary the import-linter contract enforces |

## Two facts a reader can check

**`models` is the hub.** The shared vocabulary module (`SecurityObject`, `Verdict`, `ReasonCode`, `Risk`, `ActionType`, `GuardrailResult`, `EvalContext`, `Decision`) is the most-connected code in the graph. Every subsystem above imports and reads these types; almost none of them get redefined downstream. If you're reading the codebase for the first time, this is the file to start with: understanding these types tells you what every other module is passing around.

**There is no proxy ↔ policy-core edge.** The table's last row is zero because of the boundary described in [the system atlas](https://docs.trydoberman.dev/architecture/atlas/): the decision engine, roles, policy, storage, auth, the subjective layer, and the egress broker never import the proxy, the host hooks, or the dashboard. An import-linter contract enforces it and fails CI if anyone adds that edge.

Both facts can be re-checked by rebuilding the graph against the current commit and reading the same two rows.
