The Pre-Execution Gate
Before any PaymentIntent can execute, it must pass a deterministic pre-execution gate: 13 numbered checks plus 10 hardening additions (checks 1.5, 3.5, 5.5, 6.5, 6.6, 6.7, 7.5, 8.5, 9.5, 11.5) = 23 entries total; the canonical happy path is the 13 numbered checks (several additions record not_applicable for non-M2M flows). The gate is the only path to financial execution. The gate is non-overridable.
Runs at
The boundary before approved -> dispatching
Reads from
The live Ledger (current balance, counterparty status, etc.)
Emits
An audit event before each step and after each pass/fail
Why a Gate
Policy returns allow based on the rules a tenant signed. But "the rules say yes" is not the same as "it is safe to execute right now." Between Policy allow and rail dispatch, dozens of conditions can change: a balance drops below the threshold, a counterparty flips to sanctioned, the policy version supersedes, an idempotency-key replay arrives.
The gate is the deterministic check that runs immediately before dispatch and reads from the current Ledger state, not the snapshot Policy evaluated against.
Think of Policy as the standing rule and the gate as the flight check. Both must pass. Either one failing is a hard stop.
The Core Steps
The gate runs the following classes of check, every payment, every time. Steps are deterministic and versioned with the protocol. These are the 13 numbered checks of the canonical happy path; 10 hardening additions are inserted at their correct positions (checks 1.5, 3.5, 5.5, 6.5, 6.6, 6.7, 7.5, 8.5, 9.5, 11.5. See Hardening Additions below) for 23 entries total. The M2M / x402 / escrow additions (3.5, 5.5, 6.5, 6.6, 8.5) record not_applicable for non-M2M flows so the canonical happy path is unchanged. Check 6.7 (obligation direction) is dormant when the intent carries no obligation_id.
#
Step (check name)
Reads From
1
Agent identity verified (agent_identity_verified): the principal is an agent that owns this intent, and the agent record is active
agent record (resolveAgent)
2
Agent authorized (agent_authorized): the principal carries payment_intent:execute scope, or the agent may execute payments
principal scopes, agent scope
3
Action allowed (action_allowed): policy matched a rule for this action type and returned allow or confirm, never reject
policy_decisions
4
Source account allowed (source_account_allowed): the source account exists and is active
ledger_accounts
5
Counterparty allowed (counterparty_allowed): the destination counterparty exists and is not sanctioned
ledger_counterparties.risk_level
6
Counterparty verified (counterparty_verified): when the policy threshold applies, the counterparty is document_verified or sanctions_cleared
ledger_counterparties.verified_status
7
Amount within limit (amount_within_limit): the amount is at or below the policy amount_upper_bound, with currency match
policy_decisions (amount_upper_bound)
8
Available balance sufficient (available_balance_sufficient): available_balance - Σ(active reservations) ≥ amount, with currency match
ledger_accounts.available_balance, ledger_reservations
9
Required evidence present (required_evidence_present): when policy requires evidence kinds, the intent carries evidence references
policy_decisions, intent evidence_ids
10
Approval requirement determined (approval_requirement_determined): the policy decision outcome (allow vs confirm) is recorded
policy_decisions
11
Approval granted when required (approval_granted_when_required): required approver signatures are present, and the hard human-approval floor is satisfied
approvals
12
Policy decision recorded (policy_decision_recorded): the PolicyDecision row is persisted and its id surfaced
policy_decisions
13
Audit-before emitted (audit_before_emitted): the payment_intent.execute.before event is written before any rail dispatch
audit_events
If any step fails, the PaymentIntent transitions to failed with a structured reason. No rail call is made.
Hardening Additions
Ten further deterministic checks are inserted at their source-defined positions, bringing the complete trace to 23 entries:
Agent behavior pinned (1.5)
The runtime agent behaviorHash equals the registered behavior hash; a silent model, prompt, or tool swap is a hard reject when required.
BrainMCPAgentRegistry
On-chain settlement permitted (3.5)
The matched policy rule explicitly permits on-chain settlement for this tenant and payment class.
policy dimension
Agent counterparty attested (5.5)
When the payee is an agent, it is registered and active in BrainMCPAgentRegistry.
BrainMCPAgentRegistry
x402 payment context valid (6.5)
The x402 settlement context is USDC on Base and matches the resolved counterparty payee.
intent settlement context
Escrow state bound (6.6)
For an escrow release, the on-chain BrainEscrow lock matches: still Locked, enough remaining to cover this release, same payee, same jobTermsHash.
BrainEscrow.getEscrow (testnet)
Obligation direction matches flow (6.7)
When the intent cites an obligation_id, the linked obligation is not a receivable. An outflow targeting an obligation owed to us is a hard reject.
ledger_obligations.direction
Ledger-state snapshot binding (7.5)
The Ledger snapshot Policy decided against is captured and re-validated immediately before dispatch; drift is a hard reject.
computeLedgerSnapshot over Ledger
Micropayment cap within window (8.5)
Per-agent rolling-window spend stays within the signed policy cap.
executions
Evidence semantic validation (9.5)
The supporting evidence actually substantiates this action (amount, counterparty, obligation), not just that it exists.
raw_parsed, evidence_ids
Duplicate-payment protection (11.5)
No prior execution with the same counterparty and amount inside the configured duplicate window, and no reused paid evidence or settled obligation.
executions, Ledger evidence
These persist into the gate_checks snapshot on the audit-before event, so the full 23-entry trace is part of the verifiable Proof artifact.
Check 11 also enforces the hard human-approval floor for on-chain money movement. onchain_transfer and escrow_release require at least one recorded human approval even when policy returns allow. x402_settle can remain approval-free only when the matched signed policy rule sets onchain_settlement_permitted: true and x402_autonomous_max_amount with the same currency and a value greater than or equal to the intent amount. Otherwise the gate fails with hard_human_approval_floor_required until a human approval is recorded.
Audit Emission
The gate emits two audit events per step.
payment_intent.gate.step_started
Immediately before each step runs
payment_intent.gate.step_completed
After the step passes (or fails, with reason)
Plus two outer events:
payment_intent.gate.started
Before step 1
payment_intent.gate.passed or payment_intent.gate.failed
After the final step (or earlier on failure)
The full step-by-step audit means a counterparty or auditor can reconstruct exactly what was checked, in what order, against what state.
Why Deterministic
Every step is a pure function over Ledger state plus the PaymentIntent. Two independent runs against the same Ledger snapshot produce the same result. This is what lets the gate appear in the audit trail with high confidence: it is replayable.
LLM-driven decision in the gate
Non-deterministic; not replayable
Network call to an external service for a "yes/no"
Adds non-determinism and latency to a hot path
Step that mutates Ledger state
The gate must be observation-only
Step that depends on wall-clock except for stale-data thresholds
Wall-clock dependence is opt-in and bounded
What Happens on Failure
Failure is structured.
The agent that proposed the intent receives the failure code. It can re-propose with adjusted parameters (smaller amount, different source account); that's a new PaymentIntent, with a new id, new PolicyDecision, and a fresh gate run.
Why No Override
A bypass path defeats the purpose. If anyone (tenant, operator, agent) can override the gate, then the audit story collapses ("the gate passed, except when it didn't"). The gate is non-overridable by design. To execute a payment that the gate currently rejects, the tenant must change the underlying state (top up the account, verify the counterparty, sign a new policy). The gate then passes naturally.
This is the same logic as airline pre-flight checklists: not because the captain doesn't know what they're doing, but because removing the checklist removes the proof that it was done.
Dry-Run Mode (Agent Autonomy)
The gate accepts a dryRun flag. In dry-run it runs the same checks against the same Ledger state and returns the same envelope, but does not insert a policy_decisions row, write a reservation, or emit audit events. Agents call dry-run before building a full proposal. To short-circuit obvious rejects and to decide confirm vs execute. There is one evaluator: the same gate code runs live and dry-run, so the two can never drift. The live gate still runs at execute time.
Behavior Pinning Check
Check 1.5 sits between identity and authorization: the runtime agent behaviorHash must equal the value registered on-chain in BrainMCPAgentRegistry. A mismatch (a silent model/prompt/tool swap) is a hard reject regardless of every other signal. It is verified only when a runtime hash is supplied (or when a tenant opts into mandatory pinning), so the canonical happy path remains the 13 numbered checks.
Net of Reservations
Check #8 (balance) subtracts active balance reservations: available_balance - Σ(active reservations) ≥ amount. With several money-movers live, parallel proposers cannot double-spend the same balance.
The live execution path treats the gate as a preflight and then performs the authoritative reserve in the handoff transaction. It locks the source account, locks the latest balance snapshot, rechecks available_balance - active reservations >= amount, creates the reservation, moves a PaymentIntent from approved to dispatching, and enqueues the outbox row. The outbox row carries reservation_id across the async boundary. On a successful rail receipt, completeExecution() consumes the reservation inside the same transaction as dispatching -> executed; on a deterministic rail rejection, failExecution() releases it inside the same transaction as dispatching -> failed. x402_settle and escrow_release remain not_applicable for this check because their spend is enforced by on-chain wallet or escrow state, not by an off-chain ledger-account hold.
What's Next
Policy and Permissioning
The standing rule that runs alongside the flight check.
Last updated
