Skip to main contentPASS
Read the docs

Execution

Agents

Operating autonomous actors under a mandate: the division between decision-making and authority, and what that division buys.

An autonomous agent is a process that proposes executions. It reads state, forms a plan, and asks that capital move. In PASS terms it is an actor: the party exercising authority, distinct from the subject on whose behalf it acts. Everything it does up to the moment of submission is deliberation, and deliberation is not authority. The question that matters arrives after the agent has decided and before the chain has acted: was this actor permitted to do this specific thing, for this subject, under these constraints.

That question is answered outside the agent, by evaluation the agent does not perform on itself.

The division of labour

The agent decides what to attempt. The mandate defines what may proceed. Preflight evaluates one against the other. These are three jobs, and none of them substitutes for another.

AGENT
  |  proposes
  v
EXECUTION INTENT       <- adapter derives, validates
  |
  v
PREFLIGHT              <- mandate + passport evaluated
  |
  +--> AUTHORIZED -> submitted
  |
  +--> BLOCKED + REASON -> reported, not retried

An agent that reasons well produces better proposals than one that reasons poorly, and that difference is real. It is not, however, a permission. A mandate drawn tightly bounds outcomes but supplies no judgement: it refuses a harmful action and an excellent one with the same indifference if both fall outside its terms. Sound operation requires both to be right, and it requires them to be right independently, because the failure of one is exactly the moment the other has to hold.

The ordering in the diagram is deliberate. The proposed execution is decoded by an adapter into an Execution Intent before policy sees anything, because raw calldata cannot be trusted to describe itself. Policy is evaluated against what the call will do, not against what the agent believes it constructed.

An instruction is not a constraint

A limit expressed as an instruction to a model — never trade outside this list, never exceed $2,500 — is an input to a probabilistic process. It is frequently honoured. But its enforcement depends on the same machinery it is meant to bound: the model reads the instruction, and the model decides whether the present situation is an exception. Such a limit can be displaced by later context, diluted over a long session, correctly applied to a mis-read fact, or quietly outweighed by content arriving from outside the agent, such as a document, a price feed or a tool result that the model treats as authoritative.

A mandate clause is a different kind of object. It is data. It is evaluated by a component the agent does not control, at a point in the path the agent cannot skip, against the intent that will actually execute.

PropertyInstruction to a modelMandate clause
FormNatural language in contextMachine-readable policy
Evaluated byThe model itselfPreflight, outside the agent
Evaluated whenDuring reasoningBefore execution
Failure modeSilent and probabilisticDeterministic refusal with a Reason Code
Withdrawn byEditing a prompt and redeployingRevoking or suspending the mandate

Neither replaces the other. Instructions are how an agent is brought to behave well. A mandate is how the range of outcomes is bounded when it does not. A system with only instructions has no boundary that survives the reasoning it depends on; a system with only a mandate has a boundary and no competence inside it.

Failure without malice

The common assumption is that constraints exist because agents might be adversarial. Some might be. Most failures are more ordinary than that, and a mandate earns its cost even when every participant is acting in good faith.

Consider actor 0x81...29F, acting for subject 0x3A...F02 under a mandate whose allowed assets are AAPL and NVDA, whose maximum transaction 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.

FailureHow it appears at executionReason Code
Reasoning error: a ticker resolves to the wrong tokenized assetIntent targets an asset outside the allowed setASSET_NOT_ALLOWED
Stale assumption: a credential lapsed after the plan was formedPassport 0x7F...94A no longer satisfies the requirementCREDENTIAL_EXPIRED
Mis-parsed input: a size field denominated in cents is read as dollarsIntent value is 100 times the intended sizeTX_LIMIT_EXCEEDED
Unexpected market state: thin liquidity prompts a route through an unfamiliar venueIntent decodes to a venue outside approved adaptersADAPTER_NOT_ALLOWED
Unexpected market state: the agent tries to source liquidity on another chainIntent decodes to a bridging actionACTION_NOT_ALLOWED
Repetition: nine defensible $1,200 purchases within one sessionCumulative exposure crosses the daily ceilingDAILY_LIMIT_EXCEEDED

The last row is the one that is hardest to catch anywhere else. Each individual purchase is reasonable, and each clears the $2,500 per-transaction ceiling comfortably. A reviewer looking at any single instruction would approve it. The excess exists only in the aggregate, and only a boundary that holds state across executions can see it. This is why a mandate states a daily exposure limit as well as a per-transaction limit; the two constrain different errors, and neither implies the other.

None of these failures requires an adversary. They require a system operating on imperfect information, which is the normal condition.

The agent's key is an independent risk

An agent holds a signing key, and that key establishes control. It does not establish authority. The distinction becomes practical during an incident.

An agent process is often long-lived, deployed in an environment that also ingests untrusted input, and reachable by more people than the ones who originally configured it. A key in that position is exposed to ordinary operational hazards: it is copied into an image, shared between processes, or reachable by a component that has been induced to sign something it should not have. Under a mandate, the consequence of that exposure is bounded by the mandate's terms rather than by the account's balance. This narrows the blast radius. It does not eliminate it. Anything the mandate permits remains available to whoever holds the key, which is why mandate width is a security parameter and not only an operational convenience. See security for the broader treatment.

Withdrawal belongs at the authority layer. A mandate binds an actor identifier. Issuing the agent a new key does not disturb that binding: the mandate still names the old identifier, so the compromised key continues to satisfy it, while the new identifier has no authority at all until it is bound in turn. Rotation on its own therefore changes nothing at the boundary, and rotation followed by binding the new identifier without withdrawing the old widens the set of keys that may act. Revoking or suspending the mandate is the action that changes what may happen: the next preflight returns MANDATE_SUSPENDED or ACTOR_NOT_AUTHORIZED regardless of which key signs, and it does so everywhere the mandate reached rather than only in the system where a rotation was applied.

The sequence during an incident follows from that. Withdraw the authority first, because it takes effect at the boundary. Rotate the key second, as control hygiene. Bind the new identifier only once the failure is understood.

Designing a mandate for an agent

Begin from the narrowest authority that permits the task, not from the authority that would be convenient later.

The method is mechanical. Enumerate the executions the task actually requires. Derive the asset set from that enumeration rather than from the set the agent might one day want. Derive the action set the same way, and state which actions are blocked outright where an omission would otherwise be ambiguous. Restrict venues to the adapters the task uses. Size the per-transaction limit to the largest legitimate single execution and the cumulative limit to the largest legitimate day, rather than to the account balance. Give the mandate an expiry, so that authority ends by default and continuation is a decision somebody makes.

{
  "actor": "0x81...29F",
  "subject": "0x3A...F02",
  "allowed_assets": ["AAPL", "NVDA"],
  "allowed_actions": ["BUY", "SELL"],
  "blocked_actions": ["BRIDGE"],
  "max_transaction_value": 2500,
  "daily_exposure_limit": 10000,
  "venues": "APPROVED_ADAPTERS_ONLY",
  "required_credentials": ["STOCK_TOKEN_ELIGIBLE"],
  "expires_at": "2026-12-31T00:00:00Z"
}

Widening is then a deliberate act with an owner and a record, rather than an accumulation nobody chose. A mandate is an authorization boundary and not a risk model: it constrains who may act, on what, through where and how much, while market, smart-contract, oracle and governance risk remain to be managed by other means and are treated in the security model. The mechanics of the clauses themselves are covered in mandates and policies.

Multiple agents and delegation

Where several agents run, the natural unit is one mandate per agent per task rather than one shared mandate that all of them sign under. A shared mandate collapses attribution: afterwards the record shows that the authority was exercised but not which process exercised it, and a limit consumed by one agent silently constrains another. Separate mandates keep both the accounting and the boundary legible.

Delegation follows the same principle. An authority may be passed onward, and a derived authority can never exceed the authority it derives from. Where the two disagree, the narrower governs.

SUPERVISOR MANDATE
  assets: AAPL, NVDA    max tx: $2,500
        |
        |  delegates, narrowing only
        v
WORKER MANDATE
  assets: AAPL          max tx: $500

A supervising agent that dispatches work should therefore not lend its own authority to the processes it dispatches, either by sharing its key or by signing on their behalf. Each worker holds a mandate scoped to the work it was given, so a worker that misbehaves is bounded by its own terms and can be withdrawn without disturbing the rest.

Responding to a BLOCKED decision

A decision is information, and its Reason Code is deterministic. The code identifies which clause refused, which in turn identifies what would have to change and who would have to change it. An agent should treat that as the useful output it is.

Reason CodeWhat it indicatesCorrect response
ASSET_NOT_ALLOWED, ACTION_NOT_ALLOWED, ADAPTER_NOT_ALLOWEDThe attempt falls outside the mandate's termsAbandon the attempt and report the code
MISSING_CREDENTIAL, CREDENTIAL_EXPIRED, CREDENTIAL_REVOKEDThe subject's passport does not satisfy the requirementHalt and escalate to the party that manages the credential
TX_LIMIT_EXCEEDEDThe single execution exceeds the per-transaction ceilingReport the proposed value; propose a smaller execution only where a smaller one genuinely satisfies the task
DAILY_LIMIT_EXCEEDEDCumulative exposure is exhausted for the periodStop for the period, and do not resume by another route
MANDATE_EXPIRED, MANDATE_SUSPENDED, ACTOR_NOT_AUTHORIZEDThe authority itself is absentCease operating and escalate to the issuing party
INVALID_EXECUTION_CONTEXTThe intent could not be validatedTreat as a defect in the agent, report it, do not retry

Two behaviours are worth stating explicitly. The first concerns retries. An agent should not resubmit a refused intent unchanged: the same intent against the same state returns the same code, so repetition produces load and no information. Where a refusal is time-dependent — an exhausted daily window, a credential awaiting renewal — what has to change is the state rather than the intent, and it changes on a clock rather than in a retry loop.

The second concerns limits. Proposing a smaller execution because a smaller one does the job is ordinary work. Decomposing one order into pieces sized to clear the ceiling is not. Splitting a $4,000 order into two $2,000 orders to clear TX_LIMIT_EXCEEDED is an attempt to obtain an authority that was withheld: the per-transaction limit expresses a decision about single-execution size, the cumulative limit accounts for both halves in any case, and the record shows the pattern. A limit-shaped refusal is a stop, not a puzzle to be solved.

The execution boundary

The integration model places evaluation between the agent's decision and submission, on the same intent that will be submitted. A conceptual sketch of that boundary:

type ReasonCode =
  | 'AUTHORIZED'
  | 'MISSING_CREDENTIAL'
  | 'CREDENTIAL_EXPIRED'
  | 'CREDENTIAL_REVOKED'
  | 'ASSET_NOT_ALLOWED'
  | 'ACTION_NOT_ALLOWED'
  | 'ADAPTER_NOT_ALLOWED'
  | 'TX_LIMIT_EXCEEDED'
  | 'DAILY_LIMIT_EXCEEDED'
  | 'MANDATE_EXPIRED'
  | 'MANDATE_SUSPENDED'
  | 'ACTOR_NOT_AUTHORIZED'
  | 'INVALID_EXECUTION_CONTEXT';
 
interface Decision {
  outcome: 'AUTHORIZED' | 'BLOCKED';
  reason: ReasonCode;
}
 
interface ProposedExecution {
  actor: string;
  subject: string;
  target: string;
  calldata: string;
  value: string;
}
 
class AuthorityWithheld extends Error {
  constructor(
    readonly reason: ReasonCode,
    readonly proposed: ProposedExecution,
  ) {
    super(`execution refused: ${reason}`);
  }
}
 
interface Preflight {
  evaluate(proposed: ProposedExecution): Promise<Decision>;
}
 
async function actUnderMandate(
  proposed: ProposedExecution,
  preflight: Preflight,
  submit: (p: ProposedExecution) => Promise<string>,
): Promise<string> {
  const decision = await preflight.evaluate(proposed);
 
  if (decision.outcome !== 'AUTHORIZED') {
    // The reason code is the output. Record it, surface it,
    // and stop. Do not retry, and do not restructure the
    // execution to fit under the limit that refused it.
    throw new AuthorityWithheld(decision.reason, proposed);
  }
 
  return submit(proposed);
}

Three properties make the sketch meaningful. Evaluation runs on the identical object that is submitted, so nothing is introduced between the check and the call. The refusal path terminates rather than looping. And the Reason Code leaves the boundary intact, so an operator reading the log learns which clause held and why, instead of seeing an unexplained absence of activity. The same shape appears in more detail in the integration model.

An agent built this way remains free to be wrong. It will propose executions that should not proceed, because that is what imperfect information produces. The boundary is what makes being wrong survivable.