Write Paths
RobotMoney's six layers form a one-way upward data flow. Information flows up; control flows down. Within that flow, only two write paths are allowed to write upward into the authoritative state without coming from below: agent contributions to Raw, and agent-proposed PaymentIntents into Ledger. Decision-only authorizations are governed Policy and Audit writes; they do not write Ledger state or create PaymentIntents.
This page documents all write paths, where they originate, and what guarantees each one provides.
The Six-Layer Write Surface
┌────────────────────────────────────────────────────┐
│ 6. AUDIT append-only │ ← every layer writes here
├────────────────────────────────────────────────────┤
│ 5. AGENT proposals, executions │ ← agents propose
├────────────────────────────────────────────────────┤
│ 4. POLICY decisions │ ← policy evaluation writes
├────────────────────────────────────────────────────┤
│ 3. WIKI pages, snapshots, annotations* │
├────────────────────────────────────────────────────┤
│ 2. LEDGER 11 entities │
├────────────────────────────────────────────────────┤
│ 1. RAW artifacts, parsed │
└────────────────────────────────────────────────────┘(*) Wiki annotations write through Raw, not directly into Ledger.
The Default Rule: Information Flows Upward
Each layer is derived from the layer below it. The Ledger is derived from Raw via deterministic extraction. The Wiki is regenerated from Ledger and Raw on demand. Policy reads from Ledger. Agents read from all of the above and write proposals.
This rule has one purpose: everything authoritative is replayable. If an extractor changes, the Ledger can be re-derived from Raw. If a Wiki generator changes, pages can be re-rendered from Ledger and Raw. Source immutability at Raw is what makes the whole protocol auditable.
The Two Controlled Exceptions
Two write paths break the strict bottom-up rule. They are explicit, scoped, and audited.
Exception 1: Agent Contributions to Raw
External agents with raw:write scope can push artifacts into the Raw layer. Stored, content-addressed, attributed to the agent's on-chain registration.
Originates from
External agent (off-protocol)
Writes to
Raw
Required scope
raw:write (on-chain in BrainMCPAgentRegistry)
Signature requirement
EIP-712 over content + tenant_id + timestamp
Quarantine?
Yes, for the first N contributions from a new agent
Confidence cap on derived rows
0.5
Exception 2: Agent-Proposed PaymentIntents into Ledger
Agents create PaymentIntent rows in the Ledger as proposals for financial actions. PaymentIntents are the only Ledger-write path that does not originate from a Raw extraction.
Originates from
Internal or external agents
Writes to
Ledger (ledger_payment_intents)
Required scope
payment_intent:propose (for external agents)
Service method
PaymentIntentService.create() (shared by HTTP and MCP)
Lifecycle gates
Policy → Approval → pre-execution gate (13 numbered + 10 hardening = 23 entries)
Decision-Only Authorizations
Headless agents can also request a decision with POST /v1/authorizations and execution: "external". This records a decision_authorizations row, a policy_decisions row, and audit evidence. It does not create a PaymentIntent and does not move money, so it is not a Ledger-write exception.
All Write Paths, by Layer
Raw
Source adapters
Webhook + ingestion endpoints
Plaid, NetSuite, Gmail, Alchemy, generic upload
Wiki annotations
POST /v1/wiki/annotate writes through Raw
Annotations never write directly into Ledger
External agents
raw.contribute MCP tool with raw:write scope
Quarantine, then standard extraction
Tombstoning
DELETE /v1/raw/{id}
Writes a tombstone, never mutates the original
Ledger
Extraction pipeline
Derived from Raw via parsers
The default and dominant write path
Reconciliation engine
Writes ledger_reconciliation_matches
Triggered by reconciliation-agent
Agent-proposed PaymentIntents
PaymentIntentService.create()
The exception
Re-normalization
POST /v1/ledger/normalize
Idempotent re-extraction of an artifact
Wiki
Page renderer
wiki_pages regenerated from Ledger + Raw
On schedule and on demand
Annotations
wiki_annotations plus a corresponding Raw artifact
Annotations are human-authored memory
Snapshots
wiki_snapshots updated when Ledger rows change
Bitemporal pointers
Policy
Policy compose
POST /v1/policy/{tenant}/compose produces a draft
Stays in draft state
Policy sign
POST /v1/policy/{tenant}/sign activates a version
Plus on-chain registration for enterprise
Policy evaluate
Writes a policy_decisions row per evaluation
The audit-trail anchor
Authorization
POST /v1/authorizations records a decision
No PaymentIntent and no money movement
Agent
Proposal
proposals row from agent.action.propose or internal agent code
Non-financial actions
Execution attempt
executions row when a Proposal or PaymentIntent is dispatched
Idempotency-keyed
Approval
approvals row from human signing approval
EIP-712
Audit
Every layer
audit_events append-only writes
Every material state change
Anchor publisher
audit_anchors per published Merkle root
Up to 50 on-chain tenant roots per cycle; closes at the root threshold or one-hour maximum wait. Demo and sandbox tenants remain database-only.
What Is Not Allowed
Direct Ledger write that does not originate from Raw extraction or PaymentIntent
Breaks replayability
Wiki text used as a source of truth for balances, transactions, obligations
Wiki is human-readable memory; Ledger is machine-readable truth
Policy reading Wiki for evaluation
Policy reads Ledger only; Wiki is for narrative
Wiki code writing to ledger_*
Enforced physically: the Wiki connects as the read-only brain_wiki_reader role
Audit row UPDATE or DELETE
Audit is append-only; no exceptions
Anchor re-publication of the same Merkle root
Idempotency at the on-chain layer
Implications
Replayability
Drop the Ledger and Wiki; rebuild deterministically from Raw plus extraction logic
Auditability
Every row carries provenance back to raw_artifacts.id plus raw_parsed.id
Tenant trust
Agent contributions are quarantined and capped at 0.5 confidence until reviewed
Counterparty trust
Anyone with a Merkle proof can verify against the on-chain anchor without trusting RobotMoney
What's Next
Last updated
