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.
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
| Subsystem | Main modules | Responsibility |
|---|---|---|
| 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
- Discovery — collect tracked files (via
git ls-files+.gitignore) and classify file roles. - Parsing — tree-sitter parse per file with language routing across 28 supported languages.
- Extraction — symbols (classes, functions, methods, fields), signatures, docstrings, references.
- Resolution — convert references into graph edges (caller→callee, import chains, inheritance).
- Metrics — cognitive complexity, centrality (PageRank, betweenness), churn, co-change, cognitive load.
- 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.
| Package | What it does |
|---|---|
atomic_io | Shared 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.py | Cross-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
| Column | Purpose |
|---|---|
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.
| Tier | Definition | Example |
|---|---|---|
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.
| Detector | Wave | Tier | What it finds |
|---|---|---|---|
clones | W95 | structural | copy-paste / structural duplicates |
dead | W99 | structural | unreachable symbols |
complexity | W102 | static_analysis | cognitive complexity hotspots |
smells | W109 | heuristic | god class / long method / feature envy (24 kinds) |
n1 | W110 | structural | loop-with-dependent-query patterns |
missing-index | W111 | static_analysis | unindexed predicate columns |
over-fetch | W114 | static_analysis | SELECT * / wildcard column reads |
bus-factor | W115 | heuristic | single-owner critical components |
auth-gaps | W116 | structural | endpoints missing auth checks |
vulns | W117 | static_analysis | reachable vulnerable dependencies |
invariants / laws | W119 | structural | mined invariant violations |
hotspots | W120 | runtime | runtime-trace classified hotspots |
taint | W122 | static_analysis | source → sink dataflow leaks |
vibe-check | W125 | heuristic | AI-rot anomalies (8 pattern families) |
orphan-imports | W132 | structural | imported-but-unused modules |
conventions | W133 | heuristic | naming / layout convention drift |
pr-risk | W134 | structural | per-PR risk factors |
duplicates | W136 | heuristic | near-duplicate symbol families |
audit-trail-conformance | W145 | static_analysis | audit-trail integrity checks |
audit-trail-verify | W146 | static_analysis | separate SHA-256 audit-trail verification |
boundary | — | static_analysis / structural | public-by-accident exports, wrong-direction layer imports |
test-hermeticity | — | structural / static_analysis | non-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.
- Who acted? — human, agent id, MCP client id, tool id (
runs,replay, MCP receipt). - What authority existed? — mode, permits, leases, scopes, policy decision (
mode,permit,lease,constitution). - What context was read? — files, symbols, commands, handles, hashes (
pr-bundle,context,retrieve). - What changed? — diff hash, changed files, changed subjects (
diff,graph-diff,pr-analyze). - What could break? — blast radius, callers, tests, vulnerable paths (
impact,preflight,test-impact,vuln-reach). - What policy applied? — rules, laws, controls, exceptions (
rules,laws,constitution). - What verified it? — tests run / required, gates, attestations (
tests,critique,pr-bundle,runs verify). - Who accepted risk? — approval, accepted risk, reviewer, timestamp (
permit,pr-bundle, run ledger).
Data model
| Type | Purpose |
|---|---|
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
- Zero-dep: ships with Python; no client/server, no infrastructure.
- FTS5: full-text search built-in, used for symbol search and retrieval.
- Inspectable state: structured tables make indexed facts queryable. Git history, annotations, snapshots, and insertion history mean identical source need not produce byte-identical database files.
- Portable:
roam index-exportemits a tarball with manifest SHA-256 + optional cosign signature;index-importverifies before extracting. - Local: index lives at
.roam/index.dbin your repo. Ordinary analysis does not automatically upload source; selected online features and connected agents have separate network paths.
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.