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.
assign_to names a datamodel path
The field is declared {:path, %{}} - ADR-0002 decision 7's eighth field
type - so the editor offers the host's declared datamodel paths as
candidates on it, a value the datamodel does not declare draws ADR-0005
clause 11e's :info advisory rather than a refusal, and the write this
block makes is visible to StatifierBlocks.Environment as :unknown at
that path (ADR-0011 decision 2) instead of being emitted where nothing
declared can see it. Its rule is core.assign's and core.subchart's -
any non-empty path with no whitespace - for ADR-0011 decision 13's
reason: one <assign> element writing one datamodel cannot have two
rules about what a location may be. A blank assign_to is a call whose
answer is thrown away, which is an answer rather than a gap.
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.
error is failure-classed: a call that came back on the error path did
not succeed (ADR-0002's amendment of 2026-09-06, section 3).
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.
done and error, in that order - the pair this type has always
emitted (ADR-0002's amendment of 2026-09-06, section 1).
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 still ends the block on error
ADR-0002's amendment of 2026-09-06, section 2: the failure final is
emitted whether or not the slot is occupied, because a class is read off
a final - the only thing that raises
done.outcome.<state id>.error is that final's own onentry, and an
outcome whose final is sometimes absent cannot be classed. With the slot
empty the failure transition targets the final directly; with it
occupied every byte is what it was, the transition targeting the child
and the child's own done_event carrying it into the final.
An author who wants the old silence for a particular call has the slot:
an occupied on_error is the amendment's section 4 definition of
handling, and a handled failure reaches nothing above the block.
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.
error is failure-classed: a call that came back on the error path did
not succeed (ADR-0002's amendment of 2026-09-06, section 3).
That is what the outcome has always meant and what the :failure
slot_style on on_error has always drawn. The on_error slot is
unchanged by the class - amendment section A2's refusal to marry an
outcome to a slot stands, and the slot and the outcome share a word here
the way core.subchart and core.map have had both for longer.
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.
done and error, in that order - the pair this type has always
emitted (ADR-0002's amendment of 2026-09-06, section 1).
Fixed rather than config-derived, and the same pair
StatifierBlocks.InvokeStep.outcomes/0 returns for every host type
built on it. Until that amendment this type exported no outcomes/1 at
all, so StatifierBlocks.BlockType.outcomes/2 answered the default
single done while emit/2 minted an error outcome final beside it -
a raised event in no summary, which ADR-0004's outcome amendment forbids
in its clause 2e.
Declaration order is read by ADR-0004 decision 6's byte determinism, so this list is never sorted.
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.