StatifierBlocks.Core.Assign (StatifierBlocks v0.3.0)

Copy Markdown View Source

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.

Functions

emit(block, context)

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).

io(config)

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.