Payload for {:datamodel_change, %__MODULE__{}} - one successful datamodel
write, carrying enough to reconstruct the datamodel from the effect stream
alone, without ever calling Session.snapshot/1 (plan decision 1).
Emitted for every successful write write_location/4 performs (decision
2); a failed write emits nothing, since the datamodel did not change and
the failure is already observable on the error channel (decision 9). One
exception: a <send>'s idlocation write that ADR-0047 goes on to reject
(an invalid target or unsupported type, classified after the write lands)
emits no effect either, because the composite error return that carries
the rejection has no effects slot. The write is in the datamodel
regardless - a consumer reading it back through _event.sendid sees it -
and live and replay agree because both derive from the core.
location_path is Predicator.ContextLocation.location_path() - the
resolved [binary() | integer()] path, the only shape a consumer can
apply to reconstruct items[i] without the pre-assignment datamodel it
does not have. location_source is the raw author string that produced
it, kept alongside under a distinct name so neither is mistaken for the
other (decision 7).
new_value and prior_value are ordinary predicator values, or the atom
:undefined when nothing stood at the path before the write - ADR-0037's
single spelling for an unbound value. Neither field commits to a wire
format: docs/observability.md:175 names "no wire format" as an explicit
non-goal for this repo, and per ADR-0025 serialization is statifier-ui's
own half of the mirror (decision 8).
c_index identifies the content node that performed the write
(constraint 3), nil for the two runner-side writes - the empty-<finalize>
auto-assign and <invoke idlocation> - that write outside any content
block; owner names which construct performed it (decision 3).
macrostep/microstep/round are the counters as they stood when the
write ran.
d_index and c_index are mutually exclusive identities on this payload
(decision 3): a non-nil d_index means the write was a <data>
binding performed by Statifier.Interpreter.Datamodel.bind_value/4, which
belongs to no content block and therefore always carries owner: nil. A
<data> element is not executable content, so Machine.content/2 could
never resolve it - Statifier.Machine.data/2 is its resolver instead, the
same relationship c_index has to Machine.content/2.
Summary
Types
@type owner() :: Statifier.Machine.Content.owner() | {:invoke, non_neg_integer(), non_neg_integer()}
Which construct performed the write - Machine.Content.owner/0 widened with the <invoke idlocation> case, which belongs to no content block (see Trace.ContentExecuted's owner typedoc for the same widening-at-the-payload precedent).
@type t() :: %Statifier.Effect.DatamodelChange{ c_index: non_neg_integer() | nil, d_index: non_neg_integer() | nil, location_path: Predicator.ContextLocation.location_path(), location_source: String.t(), macrostep: non_neg_integer(), microstep: non_neg_integer(), new_value: term(), owner: owner() | nil, prior_value: term(), round: non_neg_integer() }