System Design

Roam Architecture

Roam builds a local code index in SQLite and exposes analysis and checks through CLI and MCP tools. Configured workflows can also record receipts, run events and evidence packets for another tool or reviewer to inspect.

Available source capabilities. This page explains the local engine and its explicitly configured evidence workflows. Optional does not mean enabled by default. Hosted Review and Cloud are planned, not services provided by these modules; see current product availability. Research directions are separate from this implementation guide.

Inside vs gateway. Roam is the server in the MCP layering: it emits McpDecisionReceipt per sensitive tool call, the HMAC-chained run ledger, and the ChangeEvidence packet. Hosts (Claude Code, Cursor) own per-call approval; gateways own policy + correlation + audit aggregation; Roam ships the structured evidence stream those layers consume. See the MCP layering discussion for the full split.

Search, then follow the relationships

Roam combines lexical retrieval with indexed structure. Its retrieval pipeline finds candidates through FTS5/BM25, then ranks them with graph centrality, co-change, and available clone or runtime evidence. PageRank is one heuristic, not a definition of importance.

Follow callers, dependencies, or related tests back to source. Resolution can miss dynamic behavior, and ranking can omit relevant candidates. Neither graph search nor embedding similarity guarantees relevance.

Pipeline at a Glance

Repository ──> Index Pipeline ──> SQLite Storage
                                           │
                          ┌────────────────┼────────────────┐
                          ▼                ▼                ▼
                 Graph Analytics    Retrieval +      CLI / MCP
                 Rules Engine       Patch Verifier   Interfaces
                 Security           Code Graph       JSON / SARIF
                                    Attestation

Build the index with roam index, or use roam init to create project configuration too. Refreshes can reuse unchanged source. Many analysis commands query .roam/index.db; others inspect current files, Git changes or imported evidence directly. Check the command's scope before treating the index as its complete input.

Subsystem Responsibilities

SubsystemMain modulesResponsibility
Index Pipeline index/indexer.py, index/parser.py, index/symbols.py Build and refresh the structural index from source + git history.
Storage db/schema.py, db/connection.py SQLite schema, migrations, batched query helpers.
Graph Intelligence graph/builder.py, graph/layers.py, graph/clusters.py, graph/pagerank.py Centrality, layering, communities, cycle analysis, AST clone clustering.
Retrieval retrieve/pipeline.py, retrieve/rerank.py Graph-aware FTS5 + structural reranker (PageRank + co-change + clones + runtime hot).
Patch Verifier critique/checks.py, critique/aggregator.py Diff parsing + clones-not-edited + blast-radius + intent-alignment for roam critique.
Taint & Reachability security/taint_engine.py Source-to-sink analysis under selected rules, with sanitizer handling and explicit evidence levels. Dataflow findings and co-occurrence leads require different follow-up checks.
Code Graph Attestation attest/cga.py in-toto v1 statement builder. Merkle root over symbol fingerprints + edge bundle digest. Cosign-signable.
Fleet Planner fleet/manifest.py Multi-agent partitioner (Louvain + co-change + PageRank anchors); emits .roam-fleet.json.
Rule Engine rules/builtin.py, rules/engine.py Built-in rules + YAML rule packs (path / symbol / AST / dataflow patterns).
Interfaces commands/cmd_*.py, mcp_server.py, mcp_extras/ Deterministic queries for CLI and MCP clients. Sampling-driven compression, watcher-based invalidation, per-session memory.
Output Contracts output/formatter.py, output/sarif.py, output/schema_registry.py Structured CLI/MCP results and supported SARIF output. Inspect errors, scope and incomplete-result fields before treating a check as complete.

Index Pipeline Stages

  1. Discovery — collect tracked files (via git ls-files + .gitignore) and classify file roles.
  2. Parsing — tree-sitter parse per file with language routing across 28 supported languages.
  3. Extraction — symbols (classes, functions, methods, fields), signatures, docstrings, references.
  4. Resolution — convert references into graph edges (caller→callee, import chains, inheritance).
  5. Metrics — cognitive complexity, centrality (PageRank, betweenness), churn, co-change, cognitive load.
  6. Persistence — upsert into SQLite with incremental diffing; reprocess changed files and affected neighbors.
discover -> parse -> extract -> resolve -> metrics -> persist
                 (reuse unchanged data; refresh affected neighbors)

Source freshness and Git freshness

File hashes and Git history have separate freshness checks. A normal full index run checks Git metadata even if every source file is unchanged: a new commit may contain content already indexed from the working tree. Skipping Git-stat collection requires both an unchanged manifest HEAD and that exact commit in the indexed Git history.

roam index --force rebuilds derived source data and forces Git-stat collection within the configured history window. Internal light indexing can omit expensive analysis; use a full roam index before relying on history-sensitive metrics. Run manifests record invocation options separately from persistent configuration identity.

Index ownership and lifecycle markers prevent concurrent writers and distinguish a completed generation from an interrupted one. Recovery requires proving an abandoned owner before rebuilding. See writer-lock recovery for operational guidance.

Command-to-Data Flow

Example: roam preflight AuthService

CLI cmd_preflight
  -> ensure_index()
  -> query symbols/edges/metrics
  -> run health/rule checks
  -> aggregate verdict + risk factors
  -> render text or JSON envelope

Example: roam cga emit --include-taint

CLI cmd_cga
  -> ensure_index()
  -> attest.cga.build_statement()
       -> _symbol_fingerprints()       # Merkle root over (qname, kind, sig, path)
       -> _edge_bundle_digest()        # graph snapshot fingerprint
       -> security.taint_engine.run()  # graph-reach BFS, sanitizer stops
            -> _finding_to_vex_claim() # OpenVEX status + justification
  -> in-toto v1 Statement
       (predicateType: https://roam-code.com/spec/CodeGraph/v1)
  -> write the statement and report its output path

Signing is a separate, explicit option. Configure a key or keyless identity with roam cga emit --help, then inspect the returned signing result and artifact paths. Missing cosign, a skipped signer, or a failed signing attempt does not produce a signed statement. Keyless signing uses network services.

Reproducibility: compare source, Git, builder, and configuration identities before comparing structural fingerprints. Whole predicates include invocation and builder metadata; even unchanged source need not produce an identical predicate digest.

Tradeoff: static analysis can miss runtime connections. roam ingest-trace adds observed runtime evidence, not a complete model of every application behavior.

Agent OS Substrate

On top of the analysis core, Roam provides repo-local modes, coordination and evidence records under .roam/. Configured mode checks apply at Roam's tool boundary; they do not contain arbitrary shell actions or grant permission to ship. Signing, connected agents and selected operations have the documented network paths.

PackageWhat it does
atomic_ioShared helpers for atomic file replacement with os.replace on POSIX and Windows.
agents_md/Compositional AGENTS.md generator; consumes the rest of the substrate.
constitution/Capstone .roam/constitution.yml unifying laws, rules, memory, gates.
db/findings.pyCross-detector finding registry (roam findings list/show/count); schema version owned by db.connection.USER_VERSION.
laws/Invariant mining (roam laws mine/check) — self-installing.
leases/Multi-agent coordination (roam lease claim/release/list).
memory/Repo-local agent memory at .roam/memory.jsonl.
modes/Four cumulative action modes: read_only / safe_edit / migration / autonomous_pr.
policy/Graph-aware rule clauses (reachable_from, imports_from, ...).
quality/Canonical metric definitions (ai_rot, cycles, god_components, public_symbols).
runs/Per-run event ledger + HMAC tamper detection (roam runs verify).
world_model/Four detectors: side-effects, idempotency, causal-graph, tx-boundaries.

The workflow below shows how these pieces fit together, not a complete copy-and-paste recipe. Use the worked example for full JSON commands, run binding, clone-evidence refresh, missing-proof checks, and ledger verification.

1.  roam runs start             # open run, get ROAM_RUN_ID (HMAC-signed events)
2.  roam mode safe_edit         # declare action surface
3.  roam pr-bundle init         # start proof bundle
4.  roam preflight <sym>        # risk report before edit; not an edit blocker
5.  roam impact <sym>            # blast radius
6.  <edit>
7.  roam --json critique --working-tree   # review
7a. roam findings list          # cross-detector findings on the workspace
8.  roam pr-bundle emit --strict # inspect required and missing evidence
9.  roam runs end --run-id <id>  # choose a truthful status, then verify this ID
10. roam replay <id>             # narrate the run
11. roam agent-score            # composite 0..100 score

Findings Registry

A normalised cross-detector table for inspecting saved findings in one place. Reading the registry does not run its detectors or establish current coverage. Detector-specific evidence remains attached to each record for inspection and suppression management.

Schema

ColumnPurpose
finding_id_str Stable string identifier (UNIQUE). Deterministic — rerunning a detector refreshes the same row in place. Convention: "<detector>:<subject>:<hash>".
subject_kind What kind of thing the finding is about: symbol, file, edge, commit, package, etc.
subject_id Foreign key into the table named by subject_kind. Nullable — not every subject maps to a row id.
claim Human-readable summary of the finding.
evidence_json Detector-specific structured fields. Schema is owned by the detector, not by the registry.
confidence One of heuristic, structural, static_analysis, runtime. See the tier table below.
source_detector Which detector emitted the row: clones, dead, complexity, etc.
source_version Detector version stamp. Consumers can spot rows produced under a stale detector shape.

Confidence tiers

Every finding carries a confidence label drawn from a closed enumeration of four tiers. Detectors pick the tier that matches their evidence — never mint new strings.

TierDefinitionExample
heuristic Name-pattern matching, length thresholds, fuzzy NLP signals. vibe-check's comment_anomalies — comments don't match code semantics.
structural Graph-pattern matching over the symbol / edge / call graph. n1's loop-with-dependent-write — a loop body issues a DB call that depends on the loop variable.
static_analysis Deterministic AST / CFG / dataflow analysis. complexity scores; missing-index's unconditional-predicate finding.
runtime Requires ingested runtime traces (OpenTelemetry / Jaeger / Zipkin / coverage). hotspots's UPGRADE / CONFIRMED / DOWNGRADE classification.

CLI surface

roam findings list                       # all findings on this workspace
roam findings list --detector clones     # filter by detector
roam findings list --subject-kind symbol # filter by subject kind
roam findings show <finding_id>          # one record, full evidence
roam findings count                      # per-detector totals

Full reference and flags: command reference for roam findings.

Detectors that persist findings

30 detectors persist findings through configured command paths. This count is checked against command modules containing an emission call; it is not evidence that thirty checks ran. The registry stores last-run state per detector, not a cumulative tally. An absent result does not establish a clean scan. Run roam findings count for the live per-detector tally on your workspace. The table below gives examples, including boundary and test-hermeticity; consumer / aggregator detectors (critique, doctor, fan, fingerprint, health, llm-smells, dark-matter) re-emit derived findings from these upstream detectors.

DetectorWaveTierWhat it finds
clonesW95structuralcopy-paste / structural duplicates
deadW99structuralunreachable symbols
complexityW102static_analysiscognitive complexity hotspots
smellsW109heuristicgod class / long method / feature envy (24 kinds)
n1W110structuralloop-with-dependent-query patterns
missing-indexW111static_analysisunindexed predicate columns
over-fetchW114static_analysisSELECT * / wildcard column reads
bus-factorW115heuristicsingle-owner critical components
auth-gapsW116structuralendpoints missing auth checks
vulnsW117static_analysisreachable vulnerable dependencies
invariants / lawsW119structuralmined invariant violations
hotspotsW120runtimeruntime-trace classified hotspots
taintW122static_analysissource → sink dataflow leaks
vibe-checkW125heuristicAI-rot anomalies (8 pattern families)
orphan-importsW132structuralimported-but-unused modules
conventionsW133heuristicnaming / layout convention drift
pr-riskW134structuralper-PR risk factors
duplicatesW136heuristicnear-duplicate symbol families
audit-trail-conformanceW145static_analysisaudit-trail integrity checks
audit-trail-verifyW146static_analysisseparate SHA-256 audit-trail verification
boundarystatic_analysis / structuralpublic-by-accident exports, wrong-direction layer imports
test-hermeticitystructural / static_analysisnon-hermetic test calls (network, time, random, fs, env, subprocess)

Agent loop integration

The registry slots into the canonical agent loop as step 7a — between roam critique (review the diff) and roam pr-bundle emit (close the proof bundle):

...
7.  roam --json critique --working-tree             # review the change
7a. roam findings list                    # cross-detector findings on the workspace
8.  roam --json pr-bundle emit --strict   # inspect required and missing evidence
...

Review saved findings before closing a bundle, then check which required detectors completed on the intended scope and revision. A saved row is not current defect proof, and an absent row is not evidence that a scan ran.

Evidence Compiler

Roam is a local evidence compiler for AI-assisted software change. The findings registry above is one input layer; the evidence compiler aggregates findings, run events, policy decisions, tests, and approvals into typed ChangeEvidence packets. The shared model supports consumers such as PR Replay and OSCAL exports. Other paths, including CGA construction and command-level SARIF, retain their own collection or rendering logic; not every exporter consumes one packet.

The eight evidence questions

Use these questions to review a report's evidence. Packet fields record supplied observations and gaps, not a complete or authenticated account of the work. Inspect missing producers and evidence before drawing a conclusion; an absent approval record is not approval.

  1. Who acted? — human, agent id, MCP client id, tool id (runs, replay, MCP receipt).
  2. What authority existed? — mode, permits, leases, scopes, policy decision (mode, permit, lease, constitution).
  3. What context was read? — files, symbols, commands, handles, hashes (pr-bundle, context, retrieve).
  4. What changed? — diff hash, changed files, changed subjects (diff, graph-diff, pr-analyze).
  5. What could break? — blast radius, callers, tests, vulnerable paths (impact, preflight, test-impact, vuln-reach).
  6. What policy applied? — rules, laws, controls, exceptions (rules, laws, constitution).
  7. What verified it? — tests run / required, gates, attestations (tests, critique, pr-bundle, runs verify).
  8. Who accepted risk? — approval, accepted risk, reviewer, timestamp (permit, pr-bundle, run ledger).

Data model

TypePurpose
ChangeEvidence One evidence packet per code-change scope. Carries evidence_id, schema_version, repo_id, git_range, commit_sha, diff_hash, run_ids, mode, started_at, completed_at, verdict, risk_level, changed_subjects, findings, policy_decisions, tests, approvals, accepted_risks, artifacts, redactions, and a content_hash.
EvidenceSubject Portable identifier wrapper around things Roam already sees — symbol, file, endpoint, package, module, directory, commit, rule, control, run, bundle, finding, test, artifact. Survives reindexing so reports, SARIF rows, and attestations stay stable across rebuilds.
EvidenceLink Typed edges inside a packet (12-member closed enumeration): derived_from, touches, calls, tested_by, triggered, blocked_by, allowed_by, accepted_by, satisfies_control, maps_to_standard, supersedes, mitigates.
EvidenceArtifact File or data reference with a content hash and an optional path. Large artifacts are referenced by hash rather than embedded so the packet stays small and redaction metadata stays meaningful.

Implemented model and consumers

The dataclasses and closed vocabularies are implemented under src/roam/evidence/. They record supplied observations, relationships, artifacts, and gaps. Schema-valid records and canonical serialization do not authenticate inputs or prove complete verification.

Choose the exporter for the required format: PR Replay reports, command-specific SARIF, CodeGraph in-toto statements, or OSCAL control mappings. Inspect that command's contract and resulting artifact. A roadmap for consolidating exporters is not evidence that every path already shares one collector or supports every proposed service.

See the verification evidence guide for supplied versus executed checks, source binding, incomplete records, and external consumer verification. Control mappings support review; they do not certify compliance.

CodeGraph statements and verification summaries

roam cga emit builds an in-toto CodeGraph statement from indexed graph and Git evidence. --also-vsa requests a sibling Verification Summary Attestation. The proof-bundle path also exposes roam pr-bundle emit --slsa-l3. These are explicit operations, not artifacts emitted for every code change.

Check the intended subject, supplied evidence, and missing prerequisites. Canonical serialization of identical packet content is distinct from repeat-run byte identity: invocation time and builder metadata can change a statement.

Optional signing and CI

roam ci-setup --platform github --with-slsa-l3 previews the optional workflow. Review its triggers and permissions before writing it with roam ci-setup --platform github --with-slsa-l3 --write. Keyless cosign signing uses OIDC, Fulcio, and Rekor; inspect the actual signing result and verify the expected identity and subject with the chosen consumer.

Names such as --slsa-l3, a VSA-shaped document, or a valid signature do not establish that a system meets a SLSA level. Qualification requires the applicable requirements, trustworthy source and build controls, and the intended verifier. This page does not certify external-verifier interoperability or an attained assurance level.

Why SQLite

See it run

The worked change-review demo — local queries, patch review, a saved bundle, and run-ledger verification. Setup time varies with the project.

Want help interpreting results for your repository? PR Replay reports add founder interpretation of an agreed 30-PR or 90-PR scope. The free local sample instead uses HEAD~5..HEAD, not five identified PRs. Or jump straight to pricing / governance / trust.