Discipline

The Agent Contract

Five rules an AI coding agent should follow when Roam is available.

After indexing a repo, agents can query local symbols, calls, dependencies, and Git history through the CLI or available MCP tools. Runtime hot-path analysis additionally needs ingested traces. The questions below help choose useful checks for a change.

These are investigation routines, not a guarantee against defects. Read the reported source, refresh the index as code changes, and run the project's required tests. Select the checks that answer the current question instead of treating every change as the same task.

Surface at a glance

Authoritative counts from roam surface --json (v14.1.0). The MCP server exposes a tunable preset surface so the agent sees what it needs, not the entire registry.

SurfaceCountNotes
CLI commands287280 canonical + 7 aliases.
MCP tools registered246Full registry — pick a subset via preset.
MCP tools in core preset17Default for new agent integrations.
Languages28Tier-1 extractors plus tree-sitter fallback.
Result contractsCommand-specificShared analysis envelope conventions, plus MCP errors, metadata tools, and paginated handles. Inspect the relevant schema.

1. Before editing — ask for context

$ roam context <symbol-or-file>

Returns ranked source locations and related code to inspect. Use callers, callees, and test candidates to guide reading; the ranking does not guarantee that every relevant file is present. Use full --json output when saving evidence or making a gate decision.

2. Before deleting — ask for impact

$ roam impact <symbol>

Inspect callers and transitive relationships visible in the index. A newly discovered caller is a reason to inspect the deletion's effect, not proof that it breaks. Pair with roam safe-delete <symbol> for a SAFE, REVIEW, or UNSAFE verdict. Unresolved use in an imported file requires review; an absent graph edge does not prove that dynamic or external consumers do not exist.

3. Before merging — ask for critique

$ roam --json critique --working-tree --intent "Describe the intended change"

Patch-level structural review. Catches clones-not-edited (the agent updated one of three near-identical implementations), caller-impact spikes, and intent mismatch. Exits 5 when high-severity findings are present. Inspect check_status and skipped-check reasons: a partial critique is not merge approval. Clone comparison requires a current roam --json clones --persist scan. The working-tree form selects tracked staged and unstaged changes against HEAD, not untracked files. For a different range, capture the intended Git patch successfully and supply it with --input. Inspect summary.review_source.

4. Before refactoring — simulate first

$ roam simulate move <symbol> <new-file>
$ roam simulate extract <symbol> <new-file>
$ roam simulate delete <symbol>

Clone the graph, apply the transform, and inspect predicted metric changes before editing source. A simulation does not establish semantic correctness. roam mutate is a separate source-change workflow; roam plan-refactor plans a symbol refactor, while roam split <file> proposes file-level decomposition.

5. Before optimising — ask for algorithmic risk

$ roam math
$ roam algo --confidence high

Inspect candidate algorithmic risks that ordinary syntax/style checks can miss: nested-loop lookups becoming O(n²), N+1 queries, regex compiled inside hot loops, repeated JSON parsing, quadratic string concatenation, branching recursion without memoisation. Check the matched source, varying loop inputs, receiver identity, call ordering, and measured cost before applying advice; a matching pattern is not a performance proof.

Pairs with roam n1, roam missing-index, and roam hotspots for ingested runtime evidence. hotspots requires runtime traces; static churn/complexity rankings are a different metric. See the evidence limits.

The whole contract, in one block

# Before any edit
$ roam context <target>

# Before any deletion
$ roam impact <target>
$ roam safe-delete <target>

# Before any merge
$ roam --json critique --working-tree --intent "Describe the intended change"

# Before any refactor
$ roam --json simulate move <symbol> <new-file>
$ roam plan-refactor <target>

# Before any optimisation pass
$ roam math
$ roam hotspots --danger

The canonical JSON envelope

Analysis commands share envelope conventions, but command data, MCP error results, metadata tools, and response handles have different contracts. The following is an illustrative error-result sketch, not a universal success schema. Check the installed tool's schema and the actual result state before reading its summary.

{
  "command": "<tool_name>",
  "status": "<index_not_built | advisory_warnings | partial_failure | hard_failure | usage_error | rate_limited | stale_index>",
  "isError": true,
  "summary": {
    "verdict": "<one-line, imperative, concrete-noun terminal>",
    "level": "<blocker | warning | info>",
    "partial_success": false,
    "state": "<machine-readable state>"
  },
  "error_code": "<closed enum>",
  "error": "<human-readable error text, never raw JSON dump>",
  "hint": "<imperative: what to do next>",
  "next_command": "<copy-pasteable roam command, when applicable>",
  "retry_after_seconds": 60,
  "agent_contract": {
    "facts": ["<concrete-noun anchored fact>", "..."],
    "next_commands": ["<copy-pasteable>", "# explanation..."]
  },
  "_meta": { "timestamp": "...", "index_age_s": 42 }
}

Result-reading rules:

Worked example: the cold-start envelope an MCP tool returns when .roam/index.db does not exist yet — /docs/mcp-usage#the-cold-start-envelope.

Verdict vocabulary — concrete nouns only

Roam's maintained writing convention anchors agent_contract.facts in concrete objects or measured states. It is not a guarantee about how every model reasons. A fact-string is anchored when its terminal token (last word, punctuation stripped) is one of the canonical anchors. The live set lives in src/roam/output/formatter.py and is mirrored by the LAW 4 lint at tests/test_law4_lint.py.

FamilyRepresentative anchors
Code structure files, symbols, edges, nodes, cycles, clusters, layers, modules, commands, tools, capabilities, imports, endpoints, dependencies, packages, routes
Findings findings, hotspots, smells, violations, warnings, errors, alerts, issues, gaps, leaks, secrets, vulnerabilities
Quality metrics keys, values, chars, lines, tokens, bytes, items, entries, records, fields
State qualifiers passed, failed, scanned, checked, affected, scored, confirmed, analyzed, skipped, reached
Time units days, weeks, months, years, hours, minutes, seconds, milliseconds

Wrong: "7 of 10 capabilities are AI-safe" (ends on AI-safe, not anchored). Right: "7 of 10 AI-safe capabilities" (ends on capabilities, anchored). When an agent generates a fact for a consumer, also check that the claim is supported and its next action is usable. A grammatical anchor does not establish either.

The eight evidence questions

Roam can collect supported command evidence into a ChangeEvidence packet organized around these eight questions. Missing or incomplete evidence remains a gap; a packet does not establish that every AI-assisted change was observed. Each question maps to commands the agent already runs as part of the contract above — the packet just makes the answers portable for a reviewer.

QuestionPrimitives
Who acted?roam runs, roam replay, MCP receipt
What authority existed?roam mode, roam permit, roam lease, roam constitution
What context was read?roam context, roam retrieve, pr-bundle
What changed?roam diff, roam pr-analyze, roam graph-diff
What could break?roam impact, roam preflight, roam test-impact, roam vuln-reach
What policy applied?roam rules, roam laws, roam check-rules
What verified it?roam test-impact, roam critique, roam pr-bundle, roam runs verify
Who accepted risk?roam permit, roam pr-bundle, audit trail

Roam maps to and supports evidence for governance controls; it does not certify or make compliant. The evidence packet is portable input for the external GRC tool of your choice.

MCP boundary security

A valid packet, source hash, or signed ledger establishes only its checked contract. It does not independently prove the requested behavior, current-tree test execution, or active enforcement. Proof bundles retain review obligations and verifier results when written and read back; stronger trust still needs an independent consumer with the appropriate authority. Read the verification evidence guide for regression controls, serialization checks, and benchmark limits.

Roam owns the inside-server half of the agent's trust boundary: it runs locally as the same user as the editor, gates writes through a four-mode policy, and emits a tamper-evident decision receipt on every sensitive call. It is explicitly not a network gateway and does not proxy model traffic — gateway-class defences (semantic prompt-injection scanning, response interception, cross-server aggregation) are a complementary layer owned by your MCP host. See the Discussion #37 reply for the full inside-server-vs-gateway framing, and /docs/mcp-usage#security-stance for the public stance.

Egress secret redaction

Every MCP tool response passes through a structural secret-pattern scan on the egress path (redact_secrets_in_string / redact_secrets_in_value) before the bytes leave the server. Hits replace the secret with a stable placeholder and stamp the receipt's redactions field with the closed-enum reason secret. Structural injection-marker observations use prompt_injection_marker; they are signals, not secret substitutions. Consult the exported receipt schema for the current vocabulary. Secret patterns cover GitHub PAT (classic and fine-grained), OpenAI / Anthropic sk- keys, AWS AKIA, Bearer tokens, PEM private-key markers, and JWT. Per-pattern hit counts ride in extra["redaction_details"] as {pattern_id: hit_count}. These checks are bounded pattern matching, not a guarantee that every secret or malicious instruction is detected.

Four-mode policy enforcement

Every MCP wrapper resolves the caller's mode (read_only / safe_edit / migration / autonomous_pr) and checks the tool's required_mode before dispatch. The receipt's policy_decision is a closed enum — allow / deny / escalate / redact / not_evaluated / would_deny_dry_run — reflecting an actual enforcement decision at the MCP boundary, not a hard-coded allow. Resolution is invocation-sensitive for mixed query/write wrappers: the query form remains read-only, while an explicit persist or write control raises both required_mode and the receipt's declared effects. Configure the server's mode through its supported policy interface and inspect the receipt's resolved mode and decision. A CLI flag is not automatically an MCP argument; do not invent a per-call mode parameter when the tool schema lacks one.

A generated .roam/constitution.yml remains fail-safe as the command surface grows. New constitutions record a semantic SHA-256 of the exact generated modes snapshot in additive generator metadata (the constitution document schema remains version: 1). Roam follows newer generated defaults only while the on-disk modes still match that recorded digest. Editing one permission breaks generator ownership and keeps the customized lists authoritative. In a partial customized policy, an omitted higher mode inherits only the permissions already declared below it; absence never imports a baked-in allow-list. Legacy files without a digest are also authoritative: roam constitution check reports their migration state, roam constitution upgrade previews every per-mode addition and removal, and --apply refuses a policy-changing legacy or customized upgrade unless the caller also passes --accept-mode-replacement and the preview's --expect-modes-digest. That digest binds replacement to the exact policy reviewed. The emitted recovery command includes the audited --override-mode bootstrap escape so a stale policy cannot block its own repair. The accepted write preserves unknown YAML keys, uses an atomic compare-and-swap, and aborts if the file changes during application.

HMAC-linked decision receipts

When an active run is available and linking succeeds, a receipt's sha256 content hash is linked into the HMAC-chained ledger at .roam/runs/<run_id>/events.jsonl. Receipts can also exist without a run link. Inspect the actual linkage and verification result before relying on offline tamper detection. verify_chain_with_receipts() in src/roam/runs/signing.py extends the standard four-state run-verify envelope (ok / tampered / unsigned / key_missing) with a receipt_integrity closed enum: ok (every linked receipt's on-disk sha256 matches), missing (a ledger event names a receipt file no longer on disk), tampered (a receipt file no longer hashes to the value the chain anchors), and not_linked (no receipt-bearing events were found, or chain tampering prevented receipt inspection). This verifier walks ledger-linked receipts; it does not search for orphan receipt files. Read the chain state as well as receipt integrity. Pre-link chains remain compatible; no migration is needed.

Receipt schema export

The McpDecisionReceipt dataclass exports to a Draft 2020-12 JSON Schema via python -m roam.evidence.mcp_receipt_schema in the installed Python environment. Gateway and policy-enforcement-point developers can validate emitted receipts against the schema before tailing them into SIEM. The schema is schema-stable; field additions are additive.

What every McpDecisionReceipt carries

One JSON file per sensitive tool call, stored at .roam/mcp_receipts/<run_id>/<tool_call>.json when a valid run is selected, or under .roam/mcp_receipts/_no_run/ otherwise. Inspect storage and linkage failures in the actual result. Frozen dataclass; canonical-JSON serialisation; stable sha256 content hash. Input/output hash fields contain digests, not raw payloads. Other local artifacts, including response handles, can contain analysis data. Recorded actor labels are not authenticated identity.

FieldPurpose
tool_callOpaque per-invocation id (<tool>_<12-hex>).
client_idMCP client process id from ROAM_MCP_CLIENT_ID.
tool_nameCanonical tool name (e.g. roam_preflight).
actor_ref_idAgent id from ROAM_AGENT_ID; ties to the ActorRef vocabulary.
declared_side_effectsTuple of read_only / write_filesystem / etc. from the tool registry.
required_moderead_only / safe_edit / migration / autonomous_pr.
input_hashsha256 of canonical-JSON input args. Never the args themselves.
policy_decisionClosed enum: allow / deny / escalate / redact / not_evaluated / would_deny_dry_run.
output_ref / output_hashArtifact id for large output, or sha256 for small. Mutually exclusive.
run_event_idLink to .roam/runs/<id>/events.jsonl row.
redactionsClosed-enum tuple of redaction reasons; stable across versions.
extraFree-form structured detail (e.g. redaction_details per-pattern counts).

Reviewers and gateways consume receipts alongside the eight evidence questions on /docs/architecture#eight-evidence-questions and the worked PR-Replay packet under /audit#evidence.

Wire it into your agent

Roam ships a Model Context Protocol server and an auto-generated Claude Code skill. Preview the client configuration, connect it, and test the actual tool inventory — see /docs/integration-tutorials for Claude Code, Cursor, Codex, Gemini, and Amp.

Or run roam skill-generate --target claude to emit a SKILL.md from the capability registry. Review the generated guidance against the installed version, available tools, and your workflow; generation alone does not prove the agent uses it correctly.

The principle behind the contract

Every command in Roam should answer a real engineering question that an agent or reviewer would ask. If a command doesn't map to a clear question, it's hidden, marked experimental, or improved. The goal isn't fewer commands — it's a clearer mental model.

Start with the uncertainty in the change. Choose a useful check, inspect its evidence, and keep the remaining uncertainty visible.

Where to next

Ready to try Roam? Install the free CLI · or read the command reference for the full surface.

Procurement and audit teams: the Agent Governance Evidence Pack turns the contract into a tamper-evident deliverable with control mapping to SOC 2 CC8.1, ISO/IEC 42001, NIST AI RMF, and EU AI Act Article 12.

See it run: The worked change-review demo — local queries, patch review, a saved bundle, and run-ledger verification.

Agent onboarding companions: