AgentBlueprintProtocol.Predicate (Agent Blueprint Protocol v0.1.1)

Copy Markdown View Source

The closed, portable boolean predicate algebra carried by deterministic_predicate evaluation assertions (base §6.6).

Grammar (closed world — every node is an object with op plus exactly the operator's operand members):

{"op": "and" | "or",  "args": [predicate, ]}      # n-ary, ≥ 1
{"op": "not",         "args": [predicate]}
{"op": "eq" | "ne" | "lt" | "lte" | "gt" | "gte",
 "path": [segment, ], "value": json}
{"op": "in",          "path": [segment, ], "values": [json, ]}
{"op": "present" | "absent", "path": [segment, ]}

validate/2 checks shape against the declared port names: path[0] must name a declared port (:predicate_path_unresolved), the node count is ceilinged at 256 (:predicate_nodes_exceeded), unknown operators deny :predicate_op_unknown, and operand-shape failures use the package's class vocabulary (:unknown_member, :missing_required_field, :invalid_type, :invalid_cardinality). Check order inside one node is pinned so two implementations pick the same reason: op presence → op value/known → unknown member → missing operand → operand type/cardinality → path[0] → node ceiling (nodes are counted depth-first, on entry).

evaluate/2 validates first, then applies the predicate to a map of port name → tagged value. Path addressing: segment 0 is the port-name map key; later segments address object members by name and array elements by CANONICAL decimal index string ("0", "17" — no leading zeros). For present/absent an unresolvable path is a defined outcome (present → false, absent → true); for every value operator it is {:error, :predicate_path_unresolved}. eq/ne/in use Schema.equal?/2 (mathematical-value numbers, order-blind objects); lt/lte/gt/gte require both operands numbers (mathematical value) or both strings (byte order) — any other combination is :predicate_path_unresolved (the path did not resolve to a value the operator can order). The n-ary and/or folds are ERROR-DOMINANT: any errored operand makes the fold an error regardless of the other operands' booleans, which is what makes the verdict independent of operand order (a value short-circuit would not). Predicates are declarative data — evaluation never authorizes anything.

Summary

Functions

Apply predicate to ports (port name → tagged value). Validates first, so a shape-invalid predicate denies instead of raising. Returns {:ok, boolean()} or the first error encountered under the pinned orders.

Validate a predicate's shape against port_names. Total and never-raising on any input: malformed tagged shapes deny :invalid_type.

Types

reason()

@type reason() ::
  :predicate_op_unknown
  | :predicate_path_unresolved
  | :predicate_nodes_exceeded
  | :unknown_member
  | :missing_required_field
  | :invalid_type
  | :invalid_cardinality

Functions

evaluate(predicate, ports)

@spec evaluate(AgentBlueprintProtocol.Json.value(), %{
  optional(binary()) => AgentBlueprintProtocol.Json.value()
}) :: {:ok, boolean()} | {:error, reason()}

Apply predicate to ports (port name → tagged value). Validates first, so a shape-invalid predicate denies instead of raising. Returns {:ok, boolean()} or the first error encountered under the pinned orders.

validate(predicate, port_names)

@spec validate(AgentBlueprintProtocol.Json.value(), [binary()] | :any_root) ::
  :ok | {:error, reason()}

Validate a predicate's shape against port_names. Total and never-raising on any input: malformed tagged shapes deny :invalid_type.