Skip to content

Reading a policy

delta runs the compiler, so this is written for reviewing a policy rather than for building one locally. The authority for everything here is the language reference, which is synced from the compiler itself; this page is the orientation.

A policy is a header and two blocks:

name cart_guard
parameters {
max_total_usd_cents: int
max_item_usd_cents: int
}
requires {
sum(item.price_usd_cents * item.quantity for item in evidence.cart)
+ evidence.shipping_usd_cents <= parameters.max_total_usd_cents;
all(item.price_usd_cents <= parameters.max_item_usd_cents
for item in evidence.cart);
}
  • name <identifier> comes first.
  • At most one parameters block: the fields the user’s intent supplies values for. An empty parameters {} is the same as omitting it.
  • Exactly one, non-empty requires block: the constraints.

There is no evidence block. Evidence fields are referenced directly as evidence.<field> and are typed by the evidence schema supplied at compile time, not declared in the policy.

This is the distinction to hold on to when reviewing:

ReferenceWhere the value comes from
parameters.max_total_usd_centsthe user’s intent — the limits they authorised
evidence.shipping_usd_centsthe evidence extracted from the agent’s proposal

A policy is reusable precisely because the limits are parameters: one policy serves many intents at different values. When reviewing, read parameters.* as “whatever the user chose” and evidence.* as “whatever was true of the proposal”.

Scalars are bool, int, string, date. Collections are List<T> and Set<T> — a Set holds int, string or date; a List may also hold Object, but only for evidence produced at runtime, never as a declared field type. Element types cannot themselves be collections.

A parameter field may be prefixed optional:

parameters {
allowed_brands: optional Set<string>
}

Every constraint in requires must hold, and each ends with ;. A constraint prefixed optional: is skipped when the optional parameter it reads is absent — which is how “only check the colour if the user named colours” is expressed:

requires {
optional: evidence.color in parameters.color;
}

Things worth recognising when you read one:

  • Chained comparisondate(2026-01-01) <= evidence.delivery <= date(2026-12-31) reads as one range check, not two.
  • Reducers over collectionssum(...), all(...), count_distinct(...) with a for <var> in evidence.<collection> clause, and an optional trailing if filter: all(item.organic for item in evidence.cart if item.category under "Food > Vegetables").
  • Category pathsunder "Food > Vegetables" tests taxonomy membership rather than string equality.
  • Set membershipevidence.audience in {"men", "women", "unisex"} against a literal, or in parameters.acceptable_categories against a parameter.

A failed evaluation reports which constraint failed by index, with the constraint re-printed and its own reason — see constraint_failures in the Orchestrator API. The index is the constraint’s position in requires, counting from 0, which is why the order of constraints in a policy is worth keeping stable.

Next: worked policies for complete examples, and value encoding for how parameter values are written on the wire.