core.invoke: a step that calls the host and waits for it to answer,
with an optional subtree for the failure case (ADR-0002 decision 10, as
amended 2026-08-29 section D).
This type names an invoke type; it never runs one. Which handler a
name resolves to is deployment state supplied per session (st-ADR-0051),
and the palette is authoring state supplied per operation - the
two-registry seam ADR-0002 decision 2 draws, which this is the first
core.* type to stand on. StatifierBlocks.Compiler.InvokeTypes is what
a host compares the two against, at the one moment it holds both.
The failure path is a slot, not a port
A failing call is one of the two ways this block can finish, and the way
an author says what happens then is by putting blocks in the on_error
slot. It is not a second outlet with an author-drawn edge: every edge in
a document is a parent/slot/child relationship, which is the invariant
the editor's rendered connectors rest on (D13, and ADR-0002's amendment
section A2). The slot is a rail beside the step, declared with
zero_or_one arity and the :failure slot style ADR-0005 decision 10's
2026-08-29 amendment (10g) names for exactly this slot: an in-band
continuation path taken on a bad outcome, rendered on the rail
core.group's interrupts already uses but in the error family rather
than the interrupt one. The rendering half of 10h is the renderer's, not
this type's: all a block type does is declare the style.
Two outcomes
done and error, in that order. Under ADR-0004's outcome amendment
each one the block reaches compiles to its own <final>, minted through
StatifierBlocks.Compiler.Context.outcome_id/2, whose entry raises
done.outcome.<state id>.<outcome>. A parent that does not care which
way the call went wires the prefix and never learns an outcome name; one
that does names the full event. The block decides nothing about what
comes next: its emission ends at the final it enters.
The params field is knowingly provisional
ADR-0002 decision 7's field types are a closed set and none of them is "a
list of name/path pairs", so params is a :string holding one
name=path per line. That is the amendment's own compromise, recorded in
its section D1 as a deferred question: whichever way it is resolved - a
new field type, a dedicated control, or the flattening as shipped - it is
a decision 7 change rather than a change to this type's row.
Summary
Functions
A compound state that runs the call in an inner state and finishes at
the <final> of whichever outcome it reached.
A step with two outcomes, so produces is :unknown for the reason
core.branch declares it: joining the type this block's own call
produces with the type its on_error subtree produces is the lattice
ADR-0003 decision 4 refuses to build.
The params field's rows, as {name, path} pairs in the order the
author wrote them.
One optional slot, on_error, holding what runs when the call fails.
Functions
A compound state that runs the call in an inner state and finishes at
the <final> of whichever outcome it reached.
<state id="s_INV" initial="s_INV__running">
<state id="s_INV__running">
<invoke type="myapp:authorize">
<param expr="order.amount" name="amount"/>
</invoke>
<transition event="done.invoke" target="s_INV__o_done">
<assign expr="_event.data" location="authorization"/>
</transition>
<transition event="error.communication.invoke" target="s_blk_PARK"/>
</state>
{the on_error child's own subtree}
<transition event="done.state.s_blk_PARK" target="s_INV__o_error" type="internal"/>
<final id="s_INV__o_done">
<onentry><raise event="done.outcome.s_INV.done"/></onentry>
</final>
<final id="s_INV__o_error">
<onentry><raise event="done.outcome.s_INV.error"/></onentry>
</final>
</state>Both transitions match by prefix, and neither names an id
The <invoke> carries no id, so the engine mints one, and the two
transitions match done.invoke and error.communication.invoke by
SCXML's descriptor prefix rule rather than naming the invocation. That
is safe for exactly the reason ADR-0004's worked example gives: both
transitions sit on the inner state, which is active only while this
block's own call is outstanding, so no other invocation's completion can
be selected by them.
error.communication.invoke.<invoke_id> is statifier-ex ADR-0068's
name - a blessed suffix extension of the error.communication that
st-ADR-0051 decision 1 already assigns to this failure - which is what
makes the prefix match here and a host chart's existing
error.communication handler both correct at once.
An absent on_error emits no failure transition at all
With the slot empty there is nothing to transition to, so neither the
failure transition nor the error outcome's <final> is emitted and the
error propagates as it does today. That costs a parent nothing: outcome
wiring is an event rather than a target, so a parent may transition on an
outcome whose final was never emitted and the transition simply never
fires (ADR-0004's amendment, 2c).
Who owns what
Everything here is this block's except one transition: the one leaving
the on_error child for the error final is attributed to that child,
because "what happens after the parking step finishes" is a fact about
the child (decision 5, the same rule Emit.chain/2 follows). The
type attribute's value is stamped as coming from invoke_type and the
location's from assign_to, so an upstream finding inside either is
the author's typo rather than a bug in this type.
A step with two outcomes, so produces is :unknown for the reason
core.branch declares it: joining the type this block's own call
produces with the type its on_error subtree produces is the lattice
ADR-0003 decision 4 refuses to build.
consumes is absent - an invoke reads its inputs through params, out
of the datamodel, rather than through the type flow.
The params field's rows, as {name, path} pairs in the order the
author wrote them.
One name=path per line, blank lines ignored. Public because the editor
and the tests both need the same reading of the flattened field, and two
spellings of it would be two chances for them to disagree - the field is
provisional (ADR-0002's amendment, D1) and this function is where the
provisionality is contained.
One optional slot, on_error, holding what runs when the call fails.
Its arity is zero_or_one because a failure path is one continuation,
not a list of them: an author who wants several steps there puts a
core.sequence in it, exactly as anywhere else a single child is asked
for.