> For the complete documentation index, see [llms.txt](https://docs.brain.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.brain.fi/protocol/the-pre-execution-gate.md).

# 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.

| Property       | Value                                                        |
| -------------- | ------------------------------------------------------------ |
| **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.

{% hint style="success" %}
Think of Policy as the **standing rule** and the gate as the **flight check**. Both must pass. Either one failing is a hard stop.
{% endhint %}

### 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**:

| Check                                       | What It Enforces                                                                                                                                         | Reads From                          |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| **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.

| Event                                | When                                          |
| ------------------------------------ | --------------------------------------------- |
| `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:

| Event                                                        | When                                         |
| ------------------------------------------------------------ | -------------------------------------------- |
| `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.

| Anti-pattern                                                     | Forbidden Because                              |
| ---------------------------------------------------------------- | ---------------------------------------------- |
| 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.

```json
{
  "payment_intent_id": "pi_a1b2c3",
  "status": "failed",
  "gate_failure": {
    "step": 8,
    "reason": "INSUFFICIENT_BALANCE",
    "expected_min": "61404.12 USD",
    "observed": "58901.04 USD",
    "ledger_row": "acct_ops"
  },
  "audit_event_id": "evt_..."
}
```

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

<table data-view="cards"><thead><tr><th></th><th></th><th data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Payment Intents</strong></td><td>The Ledger entity the gate evaluates.</td><td><a href="/pages/frJ0ygywHJq5WCmjwuXH">/pages/frJ0ygywHJq5WCmjwuXH</a></td><td></td></tr><tr><td><strong>Policy and Permissioning</strong></td><td>The standing rule that runs alongside the flight check.</td><td><a href="/pages/GSe2ntE9CLqxoQAiQuzG">/pages/GSe2ntE9CLqxoQAiQuzG</a></td><td></td></tr><tr><td><strong>Audit and Proof</strong></td><td>Where the per-step audit events land.</td><td><a href="/pages/PIgNXssgtEUZDLnC4b4d">/pages/PIgNXssgtEUZDLnC4b4d</a></td><td></td></tr></tbody></table>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.brain.fi/protocol/the-pre-execution-gate.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
