Execution order
guard runs its on_fail action, and an
incomplete accumulate records the arrival and waits (outcome waiting). For the precedence
rules (what overrides what, when projection runs, chain-depth and atomicity), see the
handler execution model.
Core vs extension fields
Core (most handlers):guard, advances_to, sets_gate, data_accumulation, emit,
rules, on_complete.
Extension (specialized): accumulate, compute, fan_out, filter, reduce, count,
query, clear, clear_gates, action.
Entity acquisition
A stateful input-pin handler declares exactly one acquisition mode (more than one is a boot error):boolean
Mint a fresh entity at
initial_state before any other step. The new entity gets an
auto-generated id and the inbound event’s entity_id is ignored for state operations;
every declared field is materialized (its initial: value, else the type default) so guards
and conditions can read it immediately. Prohibited with accumulate (boot error). Allowed
with fan_out: one entity is created and every fanned-out event shares it.object
Resolve exactly one existing flow-owned entity by a declared business key (
by maps entity
fields to payload.* references). Zero or multiple matches stop the handler with an error
rather than guessing (fail closed).object
Resolve one active match, or deterministically mint one from the declared key.
Guard
object
A check (or list of checks) evaluated before the handler proceeds. Fields:
id, check
(a CEL expression), checks (a list of {id, check} for multiple named conditions; all must
pass, and it supersedes the singular check), and on_fail. on_fail is one of reject
(default), kill, discard, or escalate:{event}.Branching
list
An ordered list of
{condition, advances_to, emit}. The first matching condition wins.
Used after accumulation or computation.list
A list of named branches matched against the payload. Each has
id, condition, and
optional emit, advances_to, and data_accumulation. The matched rule owns the emit.on_complete and rules are mutually exclusive.
State and data
string
Set
entity.current_state to a target state.string
Set
entity.gates.{name} to true.boolean
Reset gates to false (runs before guard). All-or-nothing, and entity-wide: it clears every
gate on the entity, not just the gates this node declares. Selective clearing is not
supported.
object
Write fields to entity state.
writes is a list; source_event defaults to the trigger
event. Each write item is one of four forms (see below).data_accumulation write forms
value and expression are mutually exclusive on one item. The constant key is value;
{target_field, literal} is not valid.
Writes persist to entity state, so a value written by one handler is readable as
entity.<field> in later handlers, not only the one that wrote it. Within a single handler,
guards and expression writes see entity.* as it stood before this handler’s writes, while
the emit step runs after the writes and sees the new values.
Emit
string or object
Publish the follow-up event. The object form
{event, fields} populates it; the bare
string form emits with an empty payload. A handler top-level emit is valid only when the
handler has a single emit site.fields must set every field the event declares — nothing is copied from the triggering
event (the payload is producer-complete). Each field is a CEL expression (typically
entity.* or payload.*), so the bare string form is correct only for events with no
payload fields. The analyzer accepts a bare emit of a field-carrying event, so the empty
payload surfaces only at runtime; populate fields whenever the event carries data.
An emit carries no routing fields at all: intra-flow delivery comes from subscriptions, and a
pin-declared output routes through the parent’s connect edges with instance selection owned
by the receiving pin’s resolution. (The retired target:/broadcast: emit fields fail at
load with a RETIRED-EMIT-ROUTING error naming the replacement.)
List processing and computation
object
Wait for multiple arrivals. Fields:
into, expected_from, completion
(all, threshold, or timeout), dedup_by (default: sender session id), on_complete,
on_timeout. The dedup_by default collapses multiple items from one sender; see
Accumulation and projection for that and for materialize_from.object
Run a computation primitive.
operation is one of weighted_average, pick_or_average,
sum, min, max, count; plus tiers/keys/params and store_as.object
Keep items matching a condition. Fields:
items_from, condition, store_as.object
Aggregate items to a single value. Same
operation set as compute.object
Count items matching a condition. Fields:
items_from, condition, store_as.object
A finite fan-in barrier owned by a stage. Shape:
stage,
members: {by: payload.<field>, from: entity.<list-field>},
window: {by: payload.<text-field>, from: entity.<field>}, output: payload.<field>
(required — the value captured per arrival), required timeout: {after: <duration>, …},
and on_complete (both outcomes accept data_accumulation/emit/advances_to and may
read the typed join.* context: expected, completed, missing, results,
timed_out). Optional complete_when (CEL over join.*) with remaining: ignore. A
join owns its handler exclusively — no sibling handler fields. See
Parallel work.object
Emit one event per item. Fields:
items_from, emit. Writes fan_out.count to
handler context.object or list
Pre-fetch cross-entity data. Fields:
entities, filter, group_by, count, select,
store_as. Runs before clear_gates.object
Reset accumulator or state buckets.
targets is a list. Runs last.Actions
string or object
Invoke a platform action. One of
create_flow_instance, record_evidence,
mailbox_write, or artifact_repo_commit.create_flow_instancerequirestemplate,instance_id_from, andconfig_from.record_evidenceappends the payload to an accumulator; requiresevidence_target(defaults to the node id).mailbox_writematerializes a mailbox row; declared underaction.mailboxwithitem_type,severity(normal/urgent/critical),summary, and an explicitpayload.artifact_repo_commitcommits allowlisted files to a local git repo and exposes aswarm-artifact://URL; declared underaction.artifact_repo.
Value bindings across blocks
Several blocks take field values, but they do not share one grammar. The thing that bites people is what a bare scalar means, which is decided per block:
In the
{ ref / literal / cel } object form, give exactly one key; expression: is an accepted
alias for cel:. The practical rule: in emit.fields and config_from a
bare path resolves, but in the mailbox and artifact_repo blocks a bare path is a constant
string, so write { ref: ... } when you mean a reference. See
Actions for worked examples.

