core.assign: a leaf step that writes one literal to one datamodel path.
ADR-0002 decision 10 carries the row, through its 2026-08-29 amendment
(section G): slots(config) is [], the config is path and value, and
the type has the single default outcome.
Two config fields: path, the datamodel location to write; value, the
literal to write there.
value is SOURCE TEXT
value stores the literal exactly as an author would type it - false,
42350, "manual_review", quotes included for a string - which is why
it lands in the compiled expr attribute unchanged. This is not a
shortcut around ADR-0002 decision 7's closed field-type set; it is the
same string-of-source-text rule this package already applies wherever a
literal has to survive verbatim.
Expressions are deliberately not supported in V1. The obvious next
value is one computed from datamodel state, and this module does not
build it: which expression language, how it would be stored, and whether
a block document may carry an expression that must be evaluated to
compile are jointly predicator-ex's and statifier-ex's calls, not this
package's. Literals only, said out loud here rather than discovered by a
reader.
The declaration check is not here, and cannot be
validate_config/1 is handed a config, not a document, so "is path
declared in the datamodel?" is a question this callback has no document
to answer against. That check, if a host wants one, is a document-level
pass over the whole tree - not this type's business.
It exists now, and this type reaches it by declaration rather than by
checking anything: path is declared datamodel_path?: true (ADR-0002
decision 7, amended 2026-08-29, which names this field as the key's
first consumer). StatifierBlocks.Datamodel is the document-level pass,
and it produces an :info advisory anchored on path when a host
supplies a datamodel that does not declare the value - and nothing at
all when the host supplies no datamodel. The verdict is unchanged
either way: an assign to an undeclared location still compiles.
What an assign to an undeclared location means, and how the write is
ordered against a state's other onentry content, are both statifier-ex's
and both open.
Summary
Functions
A compound state whose entry writes expr to location and immediately
goes final.
core.raise's io/1 exactly, for core.raise's reason: one outcome, so
there is no join to refuse and produces is absent rather than
:unknown. An assign reads nothing through the type flow either - its
input is a literal it carries - so there is no consumes.
This block as one line of prose (ADR-0002's 2026-09-07 amendment).
Functions
A compound state whose entry writes expr to location and immediately
goes final.
<state id="s_blk_ASN" initial="s_blk_ASN__done">
<onentry><assign expr="false" location="review.parked"/></onentry>
<final id="s_blk_ASN__done"/>
</state>Emission.element/3 normalizes attribute order alphabetically, so the
written attribute order is expr then location regardless of the
order they are passed here.
Both attributes are annotated: location from the "path" config
field, expr from the "value" field, verbatim - the <assign>
element is this block's own, but each attribute value is the author's
(ADR-0004 decision 9).
core.raise's io/1 exactly, for core.raise's reason: one outcome, so
there is no join to refuse and produces is absent rather than
:unknown. An assign reads nothing through the type flow either - its
input is a literal it carries - so there is no consumes.
This block as one line of prose (ADR-0002's 2026-09-07 amendment).
The datamodel path written to, which is the whole of what this block does that a reader scanning a list cares about. The literal is left out: it is one field away in the inspector, and a value long enough to be interesting is long enough to bury the path.
iex> StatifierBlocks.Core.Assign.sentence(%{"path" => "cards.settlement.status"})
"Set cards.settlement.status"
iex> StatifierBlocks.Core.Assign.sentence(%{})
"Set a value"