Policy language reference
Specifies policy-engine v0.7.3, synced verbatim from
docs/LANGUAGE.md in Repyh-Labs/policy-engine — do not edit this page,
regenerate it. The policy engine overview
says which Orchestrator release runs this compiler.
This document specifies the policy language: source syntax, static semantics, runtime semantics, error model, bytecode, and hashing rules.
A policy is a named program pairing a parameters schema with a set of constraints. It is identified by the hash of its canonical serialization; evaluation binds the parameters to a values map and checks the constraints against the evidence.
The language is statically typed. Compilation requires an evidence schema. It embeds the minimal subshape of that schema the constraints reference into the bytecode, which evaluation uses to check the evidence. Compilation and evaluation are deterministic.
1. Non-Goals
Section titled “1. Non-Goals”The first version keeps the language small. These are non-goals:
- Range types.
- Division.
- Ordering between strings.
- Floating-point numbers.
- User-defined functions.
- User-defined types.
- General loops, recursion, or nested reductions.
- Implicit type coercions.
- Date arithmetic.
- Timestamps.
- Nested collection types such as
List<List<int>>orSet<List<int>>. - Nested input objects such as an
Objectwith anObjectfield.
2. Top-Level Structure
Section titled “2. Top-Level Structure”A policy starts with:
name <identifier>
After the header, top-level declarations may appear in any order:
- at most one
parametersschema block - exactly one non-empty
requiresblock
An empty parameters {} block is allowed and is equivalent to omitting it.
Evidence fields are referenced directly as evidence.<field> and object
attributes through reducer loop variables; both are typed by the evidence schema
supplied at compile time.
Example:
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);
count_distinct(item.brand for item in evidence.cart) <= 3;
all(item.organic for item in evidence.cart if item.category under "Food > Vegetables");}3. Types, Literals, and References
Section titled “3. Types, Literals, and References”Supported scalar types:
boolintstringdate
Supported collection types:
List<T>, whereTisbool,int,string,date, orObjectSet<T>, whereTisint,string, ordate
Collection element types cannot be collection types. Object is valid only as a
List element type, and only for evidence values produced at runtime; it is not
a declared field type.
Parameter field types may be prefixed with optional:
parameters { allowed_brands: optional Set<string>}Rules:
- If an optional parameter is present at runtime, it has the underlying non-optional type.
optionalmay prefix only parameters.- No declared field may have bare type
Object. - Parameter fields cannot contain
Object, includingList<Object>. intis a signed 64-bit integer.stringvalues are valid UTF-8.dateis a calendar date with no time of day and no timezone.- Lists and sets are homogeneous.
- Lists preserve order.
- Sets are canonicalized, so duplicate elements are ignored.
Literal forms:
TrueFalse018-5"CH"date(2026-01-01){1, 2, 3}{"CH", "DE", "FR"}{date(2026-01-01), date(2026-06-01)}Field references are namespace-qualified:
parameters.max_price_centsevidence.memory_gbObject attributes are available through a reducer loop variable:
item.price_usd_centsThe part after parameters., evidence., or an object loop variable is a single
field name, and like a loop variable it cannot be a reserved word.
Identifiers use ASCII and match [A-Za-z_][A-Za-z0-9_]*; the policy name and
loop variables are identifiers. Field names — parameter fields, evidence fields,
object attributes, and discriminants — may also start with a digit: they match
[A-Za-z0-9_]+.
Quoted literals are delimited by " and have type string. The recognized
escapes are \\, \", \n, \r, and \t. Any other backslash escape is a
syntax error. Raw newline characters are not allowed inside quoted literals.
The {} literal is allowed only when its element type can be inferred from the
other operand. Expressions such as {} == {} are rejected because the Set type
cannot be inferred.
There are no list literals.
4. Objects and Discriminated Objects
Section titled “4. Objects and Discriminated Objects”Objects are the evidence root and the elements of an evidence collection, the
latter reached through a reducer loop variable. The language does not declare
object fields in the source; an object attribute access such as
item.price_usd_cents is typed by the evidence schema.
An object always exposes its common fields. A discriminated object also names a discriminant string field whose value selects a case that contributes further typed fields. There are two kinds:
- an enum has a flat set of variants, each keyed by an opaque string tag;
- a tree has a prefix-closed set of nodes, each keyed by a category path; a node inherits its ancestors’ fields.
A case field resolves only after a category check has narrowed the object to a case that contributes it. Common fields and the discriminant are always available.
4.1 Category Paths
Section titled “4.1 Category Paths”A tree discriminant holds a category path: the empty path "" for the root, or
one or more category names joined by the exact separator >, where category
names are non-empty and cannot contain >.
under checks whether a tree discriminant is equal to or below a category path:
item.category under "Food > Vegetables"Rules:
- The left operand must be
<variable>.<d>for an object loop variable, orevidence.<d>, where<d>is the discriminant of a tree object. - The right operand must be a
stringliteral that is a declared node path. - At runtime,
actual under pathis true whenpathis"",actual == path, oractualstarts withpath + " > ".
Equality and membership on a discriminant are ordinary string operations that
also drive narrowing:
item.category == "Food > Vegetables > Carrots"item.category in {"Food > Vegetables", "Food > Fruit"}Every tag or path compared against a discriminant through ==, in, not in,
or under must be declared by the schema; an undeclared one is a compile-time
error. At runtime, an unknown discriminant value is not by itself an error: it
compares unequal to other values but may still satisfy under by path ancestry.
5. Expressions
Section titled “5. Expressions”5.1 Arithmetic
Section titled “5.1 Arithmetic”Arithmetic is supported only on signed 64-bit integers.
parameters.base_price_cents + parameters.shipping_budget_centsevidence.price_cents - evidence.discount_cents2 * item.quantityAll signed 64-bit integer arithmetic is checked. Overflow or underflow during expression evaluation or reducer accumulation is a runtime error.
5.2 Boolean Expressions
Section titled “5.2 Boolean Expressions”Supported boolean forms:
- Equality:
== - Ordering comparisons:
<,<=,>,>= - Membership:
in,not in - Boolean operators:
not,and,or - Parentheses
- Chained comparisons
- Category checks with
under
A boolean field or boolean-valued expression may appear directly in boolean position:
requires { evidence.in_stock; not evidence.refurbished;}and and or use left-to-right short-circuit evaluation.
5.3 Equality, Ordering, and Set Operations
Section titled “5.3 Equality, Ordering, and Set Operations”Equality is allowed for any two operands with the same non-Object type.
Scalar List equality is order-sensitive. Set equality is order-insensitive.
Equality is not defined for Object or for collections containing Object.
Ordering comparisons are allowed only for:
intdate
Set operations are allowed only for matching scalar-in-Set and Set-in-Set
checks:
int in Set<int>string in Set<string>date in Set<date>int not in Set<int>string not in Set<string>date not in Set<date>Set<int> subset of Set<int>Set<string> subset of Set<string>Set<date> subset of Set<date>Set<int> superset of Set<int>Set<string> superset of Set<string>Set<date> superset of Set<date>
These typing rules constrain the operands of each operator.
5.4 Reductions
Section titled “5.4 Reductions”Reductions operate over fields whose type is List<T> or Set<T>.
all(expr for value in evidence.values)all(expr for value in evidence.values if condition)all(expr for value in evidence.values if condition_a if condition_b)Supported reductions:
all: the body must bebool; returnsboolany: the body must bebool; returnsboolcount: the body may be any well-typed expression; returnsintsum: the body must beint; returnsintcount_distinct: the body must be a scalar primitive; returnsint
Scalar primitives are bool, int, string, and date.
count also accepts a single collection in place of a comprehension, giving its
length as an int:
count(evidence.cart)count(parameters.allowed_tags)Its argument is a parameter or evidence field reference of type List<T> or
Set<T>. At runtime an evidence source that is not a list or set is a runtime
error.
Rules:
- The loop variable may be any non-reserved identifier.
parameters,evidence, type names, booleans, reducers, and other built-ins are reserved.- A reducer source is a parameter or evidence field.
- A scalar loop variable may be used directly as an expression.
- An object loop variable may be used through
<variable>.<field>. - Reductions cannot be nested.
Listreductions visit elements in list order.Setreductions visit elements in canonical order: ascending for integers and dates, lexicographical for strings.- If a reducer has no filters, every collection element is produced.
- If a reducer has filters, they are evaluated left-to-right.
- The body is evaluated only for elements where every filter evaluates to
True. - If a filter evaluates to
False, later filters and the body are skipped for that element. - Any runtime error in an evaluated filter or produced body makes the reduction a runtime error.
countevaluates the body for every produced element, even though the body value is not used.- Bare object values are allowed only as the body of
count. allandanyevaluate every produced body result. They do not short-circuit over the collection.- Boolean expressions inside a reducer body or filter still short-circuit.
allover an empty result isTrue.anyover an empty result isFalse.countandcount_distinctover an empty result are0.sumover an empty result is0.
The loop variable type follows the source element type: a parameter Set<string>
yields a string loop variable, and a List<Object> evidence field yields an
Object loop variable. Attribute accesses on an object loop variable resolve
through the evidence schema. At runtime, an evidence source that is not a list or
set is a runtime error.
Examples:
all(item.price_usd_cents <= 1000 for item in evidence.cart)
any(item.category == "Food > Vegetables" for item in evidence.cart)
all(tag in parameters.allowed_tags for tag in evidence.tags)
any(country == evidence.country for country in parameters.allowed_countries)
count(item for item in evidence.cart if item.category under "Food") >= 1
count(item for item in evidence.cart if item.category under "Food > Vegetables" if item.organic) >= 1
sum(item.quantity for item in evidence.cart) <= 10
count_distinct(item.brand for item in evidence.cart) <= 35.5 Boolean Grouping Rule
Section titled “5.5 Boolean Grouping Rule”The language rejects mixed unparenthesized chains of and and or.
Allowed:
a and b and ca or b or ca and (b or c)(a and b) or cRejected:
a and b or ca or b and c5.6 Chained Comparisons
Section titled “5.6 Chained Comparisons”The language supports Python-style chained comparisons:
8 <= evidence.memory_gb <= 16date(2026-01-01) <= parameters.latest_delivery <= date(2026-12-31)a <= b == c < da <= b == c < d means (a <= b) and (b == c) and (c < d).
Rules:
- Chaining applies only to
==,<,<=,>,>=. in,not in, andunderare not part of chained-comparison syntax.- Direction reversals such as
a < b >= care rejected.
5.7 List Indexing
Section titled “5.7 List Indexing”A list element is read by a constant index after a dot:
evidence.readings.0parameters.thresholds.2Rules:
- The indexed operand is a parameter or evidence field reference of type
List<T>for a scalarT. - The index is a decimal integer in the unsigned 64-bit range. The result has
type
T. - An all-digit name directly after
parameters.orevidence.is a field name, not an index; every later all-digit segment is an index. - At runtime, an index outside the list bounds is a runtime error.
5.8 Precedence
Section titled “5.8 Precedence”Precedence and associativity for operators that can appear together unparenthesized, from tightest to loosest:
- Parentheses, literals, field references, list indexing, object references, loop variables, and reductions.
*(left-associative).+,-(left-associative).- Comparisons, equality, membership,
under, andSetoperators (non-associative, apart from chained comparisons). - Homogeneous chains of
andor homogeneous chains ofor.
Unary not does not participate in ordinary precedence. Its operand must be an
atom, a reduction, or a parenthesized expression. Use parentheses when negating
an expression that contains an operator: not a, not (a == b), and
not any(item.recalled for item in evidence.cart) are valid, while
not a == b and not a and b are rejected. A bare not expression must also
be parenthesized before it participates in a larger expression: use
(not a) == b or (not a) and b. For repeated negation, write
not (not a) rather than not not a.
6. Grammar Sketch
Section titled “6. Grammar Sketch”policy := header top_decl*header := "name" IDENT
top_decl := parameters_block | requires_block
parameters_block := "parameters" "{" "}" | "parameters" "{" opt_field_decl+ "}"
opt_field_decl := FIELD_NAME ":" "optional"? type
type := scalar_type | list_type | set_typescalar_type := "bool" | "int" | "string" | "date"list_type := "List" "<" scalar_type ">"set_type := "Set" "<" set_elem_type ">"set_elem_type := "int" | "string" | "date"
requires_block := "requires" "{" opt_expr (";" opt_expr)* ";"? "}"opt_expr := "optional:"? top_expr
top_expr := sub_expr | "not" (atom | reduction)sub_expr := atom | reduction | sub_expr (bool_op sub_expr)+ | sub_expr (cmp_op sub_expr)+ | sub_expr (arith_op sub_expr)+ | sub_expr set_op sub_expr | discriminant_ref "under" STRING
reduction := reducer "(" top_expr "for" IDENT "in" field_ref filter* ")" | "count" "(" field_ref ")"filter := "if" top_exprreducer := "all" | "any" | "count" | "sum" | "count_distinct"
atom := "(" top_expr ")" | indexed_ref | field_ref | object_ref | loop_var | literalindexed_ref := field_ref ("." INDEX)+field_ref := parameters_ref | evidence_refparameters_ref := "parameters" "." FIELD_NAMEevidence_ref := "evidence" "." FIELD_NAMEobject_ref := IDENT "." FIELD_NAMEdiscriminant_ref := object_ref | evidence_refloop_var := IDENT
cmp_op := "==" | "<" | "<=" | ">" | ">="bool_op := "and" | "or"set_op := "in" | "not" "in" | "subset" "of" | "superset" "of"arith_op := "+" | "-" | "*"
literal := BOOL | INT | STRING | DATE | SETBOOL := "True" | "False"DATE := "date(" YYYY "-" MM "-" DD ")"SET := "{}" | "{" set_elem ("," set_elem)* "}"set_elem := INT | STRING | DATEThis grammar sketch omits comments, whitespace, and line-termination details.
- Newlines separate top-level declarations and schema field declarations.
- Constraints inside
requires { ... }are separated by semicolon;. - The leading
IDENTofobject_refandloop_varis a reducer loop variable and cannot beparameters,evidence, or any other reserved word. This is what distinguishesobject_reffromparameters_refandevidence_ref. IDENTis an identifier,FIELD_NAMEa field name, andINDEXa list index.#starts a line comment and runs to the end of the line.- Comments may appear on their own line or after code.
#inside a quoted literal is not a comment.STRINGis the quoted literal token and has typestring.
7. Compilation and Static Semantics
Section titled “7. Compilation and Static Semantics”Compilation parses the policy, type-checks it against the evidence schema, and lowers the result to bytecode. The evidence schema is required.
Every expression has a concrete static type:
parameters.<field>has the declared parameter type. An optional parameter has its underlying non-optional type.evidence.<field>has the type the evidence schema gives it.- A loop variable has the source element type.
<variable>.<field>has the type the evidence schema gives it, using the object’s narrowed case for a case field.- A list index has the list’s scalar element type.
- Literals have their literal type.
- An operator’s result type follows from its type-checked operands.
A constraint expression must be bool. Every type requirement is checked at
compile time; evaluation only verifies that the runtime data matches.
Syntax and structure errors:
- The header is missing or malformed.
- A top-level declaration is repeated when at most one is allowed.
- The
requiresblock is missing or empty. - A parameter name is duplicated within the
parametersblock. - A date literal is not a valid calendar date.
- An integer literal is outside the signed 64-bit range.
- A list index is outside the unsigned 64-bit range.
- Mixed unparenthesized
andandorappear in the same chain. - A chained comparison reverses direction.
Schema and reference errors:
- A non-
optional:constraint references an optional parameter. - An
optional:constraint does not reference any optional parameter. - A parameter references a name not declared in the
parametersblock. - An
evidence.<field>names a field the evidence schema does not declare. - An object attribute is neither a common field, the discriminant, nor a case field of the object’s narrowed case.
- A case field is accessed without narrowing the object to a case that contributes it.
- A tag or path compared against a discriminant is not declared by the schema.
- A discriminated object’s discriminant, common, and case field names are not all distinct.
- A case field is declared as an object or
List<Object>. - A tree lists the root
""as a node, has a malformed node path, is not prefix-closed, or a node’s own field collides with an inherited one. - A collection type contains another collection type.
- A parameter is declared as bare
Objector asList<Object>. - A reduction is nested inside another reduction.
- A reducer loop variable uses a reserved name.
- A bare object loop variable appears anywhere except as the body of
count. - A field is accessed on a scalar loop variable.
underis used on anything other than a tree discriminant, as<variable>.<d>for an object loop variable orevidence.<d>.
Type errors:
- A constraint expression is not
bool. - Arithmetic operands are not
int. - Comparison operands do not have the same type.
- Equality is used on
Objector a collection containingObject. inornot inoperands do not form a matching scalar-in-Setcheck.subset oforsuperset ofoperands do not form a matchingSet-in-Setcheck.and,or, ornotoperands are not boolean.alloranyhas a non-boolean body.sumhas a non-integer body.count_distincthas a non-scalar body.count,all,any,sum, orcount_distinctiterates over, or measures, a source that is notList<T>orSet<T>.- List indexing is applied to a value that is not a
List<T>for a scalarT. - A
Setliteral is heterogeneous.
8. Evidence Schema
Section titled “8. Evidence Schema”The static semantics above resolve every evidence and object access against the
evidence schema. This section defines how those types are resolved and what the
compiler records: the minimal subshape of the evidence schema the constraints
reference. The inputs and outputs are defined in the Rust API specification of the policy engine (docs/API.md in Repyh-Labs/policy-engine).
8.1 Types and Resolution
Section titled “8.1 Types and Resolution”The evidence schema describes the runtime shape of the evidence map. Each evidence type is one of:
- a scalar type
bool,int,string, ordate; List<T>orSet<T>, whereTis another evidence type (sets hold scalars);- an object.
An object declares common fields shared by every value, and may be discriminated as an enum or a tree. The evidence root is an object of any of these kinds.
A reference resolves its static type from the evidence schema:
evidence.<field>resolves against the root.- A loop variable over a
List/Setevidence field uses the element type. - On an object, a common field has its declared type, the discriminant has type
string, and a case field has the type its narrowed case gives it.
Resolved types feed the same operator and reduction type rules that compilation applies.
The API representation can express schemas outside the source language subset. Such fields are valid schema data even when current policy source syntax cannot reference them; policies that try unsupported source syntax are rejected.
8.2 Discriminated Object Contract
Section titled “8.2 Discriminated Object Contract”A case field’s type may be scalar, scalar List, or Set, but not Object or
List<Object>. Common fields may use any evidence type. The discriminant, the
common fields, and each case’s fields have distinct names matching the field-name
syntax. Names that are source keywords may appear in schemas, but policy
source cannot reference them.
A tree’s nodes additionally satisfy:
- The root node is the empty category path
""; its fields are the object’s common fields, so it is not listed among the nodes. - A non-root node path is one or more category names joined by
>; category names are non-empty and cannot contain>. - Node paths uniquely identify a node.
- The set is prefix-closed: every non-root node’s parent path is also a node, the root being the parent of every depth-one node.
- A node lists only the fields it adds and inherits its ancestors’ fields; its own field may not collide with an inherited field.
Compilation validates this contract for every discriminated object in the evidence schema, whether or not the constraints reference it.
8.3 Narrowing
Section titled “8.3 Narrowing”A category check on an object’s discriminant narrows the object so its case
fields resolve. Checks read the discriminant field: <item>.<d> for an object
loop variable, or evidence.<d> when the evidence root is discriminated.
For an enum:
d == "tag"and"tag" == dnarrow to that variant.d in {tags}narrows to the fields common to the listed variants: a field present, with the same type, in every listed variant.
For a tree:
d == "path"and"path" == dnarrow to exactly that node.d under "path"narrows to that node, not the union of its descendants.d in {paths}narrows to exactly the path of a singleton set, and to the deepest common ancestor of the listed paths otherwise.
Common rules:
- Every tag or path compared against a discriminant must be declared by the schema; an undeclared one is a compile-time error. Tree paths must also be well-formed.
- Narrowing applies top-down through the constraints in a
requiresblock, left-to-right throughand, through parentheses, and from a filter to later filters and the reducer body. Only the evidence root discriminant carries across constraints; a loop object exists within a single reduction. - Narrowing of the evidence root also applies inside a reduction: it resolves a root case-field collection used as the source, and evidence-root reads in the filters and body.
- An
optional:constraint receives the narrowing before it but does not narrow the constraints after it. - Narrowing established inside an
orornotdoes not apply outside it. - Successive checks on the same object combine to the most specific case.
Checks that cannot all hold narrow to no case at all: equalities to two
different cases, an equality combined with a check strictly below its path,
unrelated tree paths, or an empty
in {}set. Such checks still compile and evaluate, they just cannot all hold. - Checks against runtime
stringorSet<string>values do not narrow.
Accessing a case field without narrowing to a case that contributes it, or after narrowing to no case, is a compile-time error; the discriminant itself is always accessible.
Allowed:
all(item.organic for item in evidence.cart if item.category under "Food > Vegetables")
all(item.organic for item in evidence.cart if item.category under "Food" and item.category == "Food > Vegetables")
all(item.perishable for item in evidence.cart if item.category in {"Food > Vegetables", "Food > Fruit"})Rejected by compilation:
all(item.organic for item in evidence.cart)
all(item.organic and item.category under "Food > Vegetables" for item in evidence.cart)
all(item.organic for item in evidence.cart if item.category under "Food" and item.category == "Apparel > Shoes")
all(item.organic for item in evidence.cart if item.category in {})When the evidence root is a tree, a leading guard narrows it for the constraints after it:
requires { evidence.category == "Food > Vegetables"; evidence.organic;}Reordering these two constraints is rejected, since evidence.organic would be
read without narrowing to a case that exposes it.
8.4 Minimal Evidence Subshape
Section titled “8.4 Minimal Evidence Subshape”Compilation records, in the compiled policy, the minimal subshape of the evidence schema: only the parts the constraints reference. It is derived from the policy and the evidence schema, applying the same narrowing used elsewhere in this document. It contains only:
- root fields the constraints reference;
- for each referenced object, the common fields and discriminant it reads;
- each case a case-field read narrows to, keyed by its tag or path, holding the fields read there.
A case field read under a narrowed enum variant or tree node is recorded at that case. Enum membership narrows to each listed variant, so a field read there is recorded under every one of them; tree membership narrows to the deepest common ancestor node and records there. Along a tree path, a field read under several nodes is recorded only at the shallowest, which its descendants inherit, so no node re-records an inherited field. Unreferenced ancestor nodes may be omitted from the embedded subshape. Unreferenced root fields, common fields, variants, nodes, and case fields are omitted, so schemas differing only in parts no constraint reads produce the same subshape.
An object satisfies the subshape when, for its runtime discriminant value, it exposes every field recorded at the matching case and, for a tree, at every ancestor node of it. The root’s recorded common fields therefore apply to every value.
For the policy
requires { sum(item.quantity for item in evidence.cart) <= 10;
all(item.organic for item in evidence.cart if item.category under "Food");
all(p.color == "red" for p in evidence.cart if p.category == "Food > Carrots");}where cart items are a tree with common field quantity: int and discriminant
category, the subshape for evidence.cart items records
common -> { quantity: int }"Food" -> { organic: bool }"Food > Carrots" -> { color: string }9. Runtime Semantics
Section titled “9. Runtime Semantics”At runtime, the engine first checks the parameters input. If the check succeeds, it evaluates constraints, checking each evidence and object access it reads against the evidence subshape embedded in the policy. Evaluation can produce runtime errors, such as missing fields, wrong field types, an out-of-bounds list index, or integer overflow.
Evaluation is deterministic.
9.1 Input Handling
Section titled “9.1 Input Handling”Only the parameters input is checked before constraint evaluation:
- every non-optional parameter exists in the parameters map
- each provided parameter has the declared type
- the parameters map has no undeclared fields
Declared type checks recurse into collection values: for a List<T> or Set<T>
parameter, every element must have type T.
The evidence map is not checked upfront. Each evidence field and object attribute is checked when a constraint reads it: it must be present, and if the embedded subshape records a type for the field at that runtime case, including tree ancestor nodes, the value must match it. A missing field or mismatched recorded type is a runtime error in the reading constraint. Because the check is lazy, an evidence error on a part of a constraint that short-circuit evaluation never reaches is not raised.
If the parameters input check fails, evaluation stops and returns an error.
9.2 Constraint Evaluation
Section titled “9.2 Constraint Evaluation”Constraints are evaluated independently in source order. Every constraint is
evaluated even if earlier constraints evaluate to False or hit a runtime error.
Each constraint yields exactly one of:
- the constraint evaluated to
True - the constraint evaluated to
False - runtime error
A non-optional constraint is satisfied if and only if its expression evaluates
to True.
An optional: constraint is satisfied without evaluating its expression if any
referenced optional parameter is absent from the parameters map. This applies
wherever the field is referenced, including a reducer source, filter, or body.
Otherwise, an optional: constraint evaluates like any other constraint. To
gate checks independently, write a separate optional: constraint for each
optional parameter.
A policy is satisfied if and only if every constraint is satisfied.
9.3 Runtime Errors
Section titled “9.3 Runtime Errors”Runtime errors are fail-closed: they make the current constraint non-passing. They do not stop evaluation of other constraints.
Runtime errors distinguish at least:
- a missing evidence field or object attribute, including the discriminant
- a wrong-typed evidence field or object attribute, including a value used in an operation that requires a different type
- a list index outside the list bounds
- integer overflow or underflow
10. Failure Reporting
Section titled “10. Failure Reporting”The evaluator returns a structured report containing all non-passing constraints in source order.
A good failure payload includes at least:
constraint_index- the pretty-printed constraint expression
- whether the constraint evaluated to
Falseor hit a runtime error - a human-readable reason
- optional evaluated subvalues when helpful
For runtime errors, the payload still identifies the constraint and includes the runtime error reason.
11. Bytecode and Hashing
Section titled “11. Bytecode and Hashing”Bytecode generation is deterministic: compiling the same source with the same evidence schema always produces the same bytecode.
- The bytecode is the same regardless of source formatting and comments.
x not in scompiles to the same bytecode asnot (x in s).- A chained comparison compiles to the same bytecode as its expansion into
pairwise comparisons joined by
and. - The compiled policy can be pretty-printed back into an equivalent source form.
- The compiled policy holds the policy name, the parameters schema, the constraints, and the minimal evidence subshape the constraints reference.
- Parts of the evidence schema no constraint reads are not included.
The policy ID is the base58-encoded SHA-256 digest of the bytecode, so it covers the embedded evidence subshape: two evidence schemas that differ only in parts no constraint reads yield the same ID, while a change to a referenced part yields a different one.