Execution
Preflight
How a proposed execution is normalized, evaluated against policy, and resolved into a decision before capital moves.
A proposed execution arrives as a call and its context. It is decomposed into something
policy can reason about, measured against the mandate that governs the actor, and leaves
as a decision: AUTHORIZED, or BLOCKED with a Reason Code. Nothing in the proposal is
assumed to be true on arrival. The evaluation first establishes what a call actually
does, and only then establishes whether the call is permitted.
The evaluation is deterministic. The same Execution Intent, the same mandate state and the same accounting state produce the same outcome and the same Reason Code. That property is not a convenience. It is what makes a decision reviewable months later by someone who was not present when it was made. An authorization system whose answer depends on unspecified ordering, or on state it did not record, can report what it did but cannot explain why.
Where the check belongs
Autonomous actors can increasingly form, price and submit financial instructions without a person in the loop. The layer that produces an instruction is the wrong place to enforce constraints on it. A model that consults its own limits before acting is producing a statement about its intentions. The statement may be entirely accurate. It is still not a control, because the component that generates the check and the component that generates the action are the same component, and they change together. Prompts are edited, tool definitions are extended, and one model is swapped for another between releases. A constraint that lives inside the thing being constrained inherits every change made to it.
Enforcement therefore belongs at the last point where the action can still be withheld: after the instruction is fully formed, and before it is submitted for settlement. Earlier than that, there is nothing definite to evaluate — asset, venue and value may still change as the actor revises its plan. Later, there is nothing left to decide. A settled transaction can sometimes be compensated, but it cannot be withdrawn. Preflight consumes a concrete call rather than a stated intention for exactly this reason.
A preview is not an enforcing check
Simulation answers a question about outcome: given current state, what would this call do, and would it revert. That question is worth answering, and simulation tooling answers it well. It is a different question from whether the actor was permitted to make the call at all. An intent can be entirely valid, fully funded and certain to succeed, and still be one the actor has no authority to submit.
The structural difference is where the answer sits relative to the execution path. A preview is a prediction. An enforcing check is a precondition. If execution can proceed while the preview is ignored, unavailable or stale, then the preview is advice, and the authority claim rests on the caller choosing to honour it.
Two properties separate the two in practice. First, the evaluation must be produced by the component that is able to withhold the execution, not by a component that reports to it. Second, it must be evaluated against the state under which the execution is admitted, not against state read at some earlier moment. Otherwise a gap opens between check and use: a credential is revoked in between, or a concurrent intent consumes the remaining daily budget, and the execution proceeds on an answer that was true when it was given and false when it was used.
PASS defines both and keeps them distinct in the interface itself. A non-committing evaluation exists as a rehearsal, so a front end can explain to a person why an action would be refused before an actor attempts it. It runs the same rules in the same order, and it carries no authority of its own: an execution admitted on the strength of a rehearsal has not been authorized.
interface ExecutionIntent {
actor: Address;
mandate: MandateId;
action: Action; // BUY, SELL, TRANSFER, BRIDGE, ...
asset: AssetId;
value: Uint256; // in the denomination of the mandate
venue: Address;
adapter: AdapterId;
}
interface Decision {
outcome: 'AUTHORIZED' | 'BLOCKED';
reason: ReasonCode; // AUTHORIZED when the outcome is AUTHORIZED
evidence?: Evidence; // failing rule, observed value, limit
}
interface Preflight {
// Rehearsal. Same rules, same order, no state change.
evaluate(intent: ExecutionIntent, at: Timestamp): Decision;
// Enforcing path. Commits periodic accounting when, and
// only when, the outcome is AUTHORIZED.
authorize(intent: ExecutionIntent): Decision;
}The four stages
STAGE 1 PROPOSED EXECUTION
call + context
|
v
STAGE 2 NORMALIZE / VALIDATE <- adapter derives
Execution Intent asset, action,
| value, venue
|
+--> no adapter validates:
| INVALID_EXECUTION_CONTEXT (stop)
v
STAGE 3 POLICY CHECK <- ordered rules,
against the mandate first failure wins
|
v
STAGE 4 DECISION
|
v
AUTHORIZED | BLOCKED + REASON CODEStage one: the proposed execution
The input is a call plus its context: the acting address, the mandate it claims to act under, the target contract, the calldata, any attached value, the chain, and the evaluation timestamp. At this stage none of it has meaning to policy. A target address is an address. Calldata is bytes. The proposal is a claim about what the actor wishes to do, recorded so the decision can later be tied to a specific input.
Stage two: normalize and validate
An adapter decodes the call into an Execution Intent: which asset, which action, what value, which venue. This is the step that turns bytes into facts policy can evaluate. The adapter model exists because calldata cannot be trusted to describe itself. A function selector is a convention, and a mandate that limits exposure to AAPL has to determine for itself that a given call moves AAPL rather than accept that assertion from the caller.
If no registered adapter can decode and validate the call, evaluation stops with
INVALID_EXECUTION_CONTEXT. No policy rules are consulted, because there is nothing
reliable to consult them about. Failing closed here is deliberate: an undecodable call is
not a call known to be harmless, it is a call whose effects are unknown, and unknown
effects cannot be measured against a limit.
Stage three: policy check
The derived intent is evaluated against the governing mandate in a fixed order. Ordering is part of the specification rather than an implementation detail, because the returned Reason Code is the code of the first failing rule, and an unspecified order would make that code arbitrary.
Every rule reads from one snapshot of state: mandate status, credential status and accounting figures as of the evaluation timestamp. Facts that were never simultaneously true must not be combined into a single decision, and no rule may depend on the outcome of a rule that follows it.
Stage four: decision
The result is AUTHORIZED, or BLOCKED with exactly one Reason Code. A decision names one
reason, not a list of them. When several rules would fail, the first in evaluation order
is reported, so the reason given is the most fundamental one: an actor who is not bound to
the mandate is told that, rather than being told about a limit that was never relevant.
Evaluation order
| Step | Check | Reason Code on failure |
|---|---|---|
| 0 | Intent derivable from the call | INVALID_EXECUTION_CONTEXT |
| 1 | Actor is bound to the mandate | ACTOR_NOT_AUTHORIZED |
| 2 | Mandate is live | MANDATE_EXPIRED, MANDATE_SUSPENDED |
| 3 | Required credentials present and current | MISSING_CREDENTIAL, CREDENTIAL_EXPIRED, CREDENTIAL_REVOKED |
| 4 | Action is permitted | ACTION_NOT_ALLOWED |
| 5 | Asset is in scope | ASSET_NOT_ALLOWED |
| 6 | Venue routes through a permitted adapter | ADAPTER_NOT_ALLOWED |
| 7 | Value is within the transaction limit | TX_LIMIT_EXCEEDED |
| 8 | Value fits the remaining periodic budget | DAILY_LIMIT_EXCEEDED |
Step zero belongs to stage two rather than to policy: it is the gate that produces an intent at all. The remainder moves from binding, to standing, to scope, to magnitude. Identity questions come first because they determine whether the mandate applies at all. Credential checks read the passport of the subject and precede scope checks, because a revoked credential removes the standing of the actor regardless of what was proposed. Limits come last because they are the only checks that read accounting state, and a call that fails for a structural reason should never touch that state. The policies page describes the rule categories themselves; this page describes only the order in which they are asked.
Periodic accounting
A transaction limit is stateless: the value of one intent is compared against one number. A periodic limit is not. It requires the enforcing layer to hold a running figure, and the definition of that figure has to be exact.
What counts is the value of intents that were authorized and admitted for execution, denominated in the unit of account of the mandate and measured at evaluation time. Blocked intents contribute nothing: a refused call must not consume budget that a permitted call would need. A mandate also has to state what happens when an authorized execution later fails downstream, because the two defensible answers differ in consequence. Releasing the reservation treats the budget as a measure of completed exposure. Retaining it treats the budget as a measure of authority spent. PASS requires that choice to be explicit in the mandate rather than implied by an implementation.
The window is equally explicit. A fixed calendar period resets on a boundary, so a mandate can be fully consumed one minute before that boundary and fully available one minute after. A rolling window has no such edge, but it requires per-intent history to compute. A daily exposure limit of $10,000 means something different under each, and the mandate states which one it means.
The accounting is maintained by the enforcing layer, never reported by the actor. A limit checked against a self-reported total is not a limit; it is a request that the actor keep its own score. The same reasoning covers concurrency. If two intents are evaluated against the same remaining budget before either commits, both can pass a check that only one of them should have passed. Reservation and commitment therefore happen inside the enforcement boundary, atomically with the decision.
Worked evaluation: authorized
Actor 0x81...29F acts for subject 0x3A...F02 under a mandate whose permitted assets are
AAPL and NVDA, whose maximum transaction value is $2,500, whose daily exposure limit is
$10,000, whose venues are approved adapters only, which requires the credential
STOCK_TOKEN_ELIGIBLE, and which blocks bridging. That subject's passport 0x7F...94A
carries IDENTITY_VERIFIED, NON_US_PERSON, EEA_ELIGIBLE, AML_CHECKED and
STOCK_TOKEN_ELIGIBLE. The actor has used $4,200 of the
current window and proposes to buy $1,800 of AAPL.
{
"actor": "0x81...29F",
"subject": "0x3A...F02",
"passport": "0x7F...94A",
"intent": {
"action": "BUY",
"asset": "AAPL",
"value": 1800,
"denomination": "USD",
"adapter": "STOCK_TOKEN_SPOT"
},
"accounting": { "window": "CALENDAR_DAY", "consumed": 4200, "limit": 10000 }
}The adapter decodes the call, so evaluation proceeds. The actor is bound to a live
mandate, and STOCK_TOKEN_ELIGIBLE is present on the passport, neither expired nor
revoked. BUY is permitted, AAPL is in scope, and the venue resolves to an approved
adapter. The value of $1,800 is below the $2,500 transaction limit, and $4,200 plus $1,800
is $6,000, below the $10,000 daily limit.
{
"outcome": "AUTHORIZED",
"reason": "AUTHORIZED",
"evidence": { "window": "CALENDAR_DAY", "consumedAfter": 6000 }
}Worked evaluation: blocked
The same actor, with $6,000 now consumed in the window, proposes to buy $4,200 of NVDA.
{
"outcome": "BLOCKED",
"reason": "TX_LIMIT_EXCEEDED",
"evidence": {
"rule": "maxTransactionValue",
"observed": 4200,
"limit": 2500,
"consumedAfter": 6000
}
}Steps one through six pass unchanged: the actor is bound, the mandate is live, the credential holds, BUY is permitted, NVDA is in scope, and the venue is approved. Step seven fails, because $4,200 exceeds the $2,500 maximum transaction value. Evaluation stops there. The daily limit is never consulted, and the consumed figure remains $6,000, because a refused intent does not spend budget.
Order also determines which reason is returned. Had the actor proposed $2,500 of NVDA with
$8,500 already consumed, step seven would pass — the value sits exactly at the transaction
limit — and step eight would fail, since $8,500 plus $2,500 is $11,000 against a $10,000
daily limit. The decision would then be BLOCKED with DAILY_LIMIT_EXCEEDED. Same asset,
same actor, same mandate, a different binding constraint, and a Reason Code that names
which one bound.
What a decision does not assert
AUTHORIZED means the actor was permitted to submit this intent under this mandate at this
moment. It is a statement about authority and nothing more. Market, smart-contract,
oracle and governance risk are real and sit outside the question preflight answers, and
the security model sets out that boundary in full.