Predicates
Control Step execution and input selection with Lua or JSONPath conditions.
Step Conditions
A Step can include a Predicate, a condition script evaluated once per candidate Work Item. Without for_each, there is one candidate: a false result skips the Step and no service call is made:
{
"id": "send-premium-discount",
"name": "Send Premium Discount",
"type": "service",
"attributes": {
"customer_tier": {"role": "required", "type": "string"}
},
"predicate": {
"language": "lua",
"script": "return customer_tier == \"premium\""
},
"http": {
"invoke": {
"endpoint": "https://discounts.example.com/send"
}
}
}
A skipped Step produces no outputs. Downstream Steps that depend on those outputs will fail if they have no other way to get them.
Predicate Languages
Predicates read resolved required and optional Attributes plus consts, using their invocation names. JPath receives an object keyed by those names. Lua exposes them as local variables.
JSONPath (jpath)
jpath is the language identifier for JSONPath expressions. The Predicate is true when the query matches at least one value.
$.customer_tier == "premium"
Lua
Use Lua for conditions involving multiple Attributes or more complex logic:
return customer_tier == "premium"
Evaluation Results
A Predicate that evaluates to false excludes that candidate Work Item. If every candidate is rejected, the Step is skipped. A runtime error in any candidate fails the Step before any of its work starts and is recorded as its failure reason. Write predicates as complete boolean expressions over the Attributes declared by the Step.
Dependency Pruning
When a Step is skipped, Argyll prunes upstream work whose only purpose was to supply that Step. The remaining Execution Plan continues toward its other Goals.
Interaction with For Each
When a Step has both a Predicate and for_each, Argyll expands the collected candidates first, then evaluates the Predicate once on each combination of candidates before creating Work Items. Only combinations returning true become Work Items. Rejected combinations receive no receipt token and do not delay completion. The Predicate receives each candidate’s input values under their invocation names, rather than a separate check against the complete collected list.
For example, this Lua Predicate keeps only positive values collected for the numeric input items:
return items > 0
With items collecting [10, 0, -5] from its providers (for_each: true, collect: all), Argyll creates one Work Item for 10. If every candidate is rejected, the Step is skipped without Work Items or outputs. No collected candidates means there are none to evaluate and completes the Step without work. Accepted Work Items keep their inputs through dispatch, retries, and recovery. The Predicate is not evaluated again when they run. See Work Items for expansion and output aggregation.
Routing
Use Predicates to route work conditionally. Different Steps with different Predicates handle different paths:
payment_method: ?"] Check1{"payment_method
== credit_card?"} Check2{"payment_method
== bank_transfer?"} CC["charge-card"] Bank["initiate-transfer"] Confirm["confirm-payment"] Init --> Check1 Init --> Check2 Check1 -->|true| CC Check2 -->|true| Bank CC --> Confirm Bank --> Confirm style CC fill:#c8e6c9 style Bank fill:#c8e6c9
Match Predicates on Required and Optional Inputs
A required or optional input can declare a match script that filters values before collection. Each candidate value is evaluated individually, and only values that pass are collected. When the Flow’s initial state declares the Attribute, those initial values are the only candidates; providers cannot supplement them.
Required inputs also have a match gate. For first, last, and some, the gate closes once providers finish with no candidate passing. For all, any non-matching candidate makes the gate unmatched, since every candidate must pass. For none, any matching candidate makes it unmatched. Execution records a closed gate after providers are terminal and skips the Step without demanding its other required inputs. An empty initial set closes the required gate for first, last, some, and all, even though an empty set satisfies some and all without a required match predicate.
Optional inputs have no Step-level match gate. Their filtered values are judged against their Collection Policy, with the configured default used only when collection is unfulfilled. An optional some or all input declared in the initial state resolves to an empty array if every initial value is rejected; the frozen set is not replaced by provider values or the default.
{
"attributes": {
"payment_result": {
"role": "required",
"required": {
"match": {
"language": "jpath",
"script": "$.status == \"approved\""
}
}
}
}
}
The match script receives one candidate at a time. For Lua the candidate is available as value. For JSONPath (jpath) it is evaluated as the document. With for_each, the matched candidate list is exactly what gets expanded into Work Items. An empty result contains zero candidates; it is different from one candidate whose value is an empty array. For an optional some or all input declared in init, zero matching candidates is a resolved empty set, as described above.