Serializes datamodel_change as a wire type - #27
Merged
Conversation
Adds effect.datamodel_change, the tenth effect.* type: one message per successful datamodel write, carrying the resolved location_path (a heterogeneous JSON array - string segments are object keys, integer segments are array indexes), the raw location_source, and the written values. new_value and prior_value follow the _event.data three-way absence rule, since the engine distinguishes unbound (:undefined) from a stored null here; put_event_data/2 generalizes to put_defined/3 for all three fields. The owner vocabulary gains the widened "invoke" kind for <invoke idlocation> writes. Until now a wire consumer could observe datamodel variable names (session.datamodel snapshots before the binding fold) but never a value; folding these messages over that snapshot reconstructs the datamodel at any point in the run. The format version stays 1, recorded in the new schema section: adding a type is additive under the must-ignore rule. Type index grows to 24; the @coverage table to 19 pairs. Refs: sui-h92
This was referenced Aug 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Since sui-bpb landed
session.datamodel, a wire consumer can see thedatamodel's variable names but never a value: the snapshot is taken before
the binding fold, so every
<data>element arrives as{"$undefined": true},and the
DatamodelChangeeffects that carry the actual writes were droppedwith an
{:unknown_effect, :datamodel_change}warning per assignment. Thisserializes them, closing the gap and unblocking the datamodel explorer pane
(sui-t36.7).
What
effect.datamodel_change(the tentheffect.*type), onemessage per successful datamodel write, consuming statifier's
Statifier.Effect.DatamodelChange(st-oef3) as-is - no upstream change.location_pathserializes as a heterogeneous JSON array: string segmentsare object keys, integer segments are 0-based array indexes. JSON's own
typing carries the distinction, so no tagging is needed;
location_sourcekeeps the raw author string alongside.
new_value/prior_valuefollow the_event.datathree-way absence rule(
:undefinedomits the key,nilis JSONnull), because ADR-0037'sunbound sentinel makes
nila genuinely stored null here. The normalizer'sput_event_data/2generalizes toput_defined/3for all three fields.ownervocabulary gains the widened"invoke"kind for<invoke idlocation>writes (Owners table grows to six variants).docs/wire-format.md: full payload schema, thelocation_pathandmutual-exclusivity (
d_index/c_index) rules, type index row 24, and theversioning decision recorded explicitly - the format version stays 1,
since adding a type is additive under the must-ignore rule.
@coveragegrows to 19 pairs; a new describe covers the assign,<data>-binding, and<invoke idlocation>shapes plus the value codec;the type-index drift test now checks 24 types.
Notes
mix qualitygreen (243 tests, 95.4% coverage, dialyzer clean; thetwo permanent skips, Gettext and Sobelow, are declared not-applicable).
mix gate.verifyalso run directly and green.@coveragemaximal literal populatesd_indexandc_indextogether,a shape the engine never emits - deliberate, so the produced key set covers
the full documented schema; the real mutually-exclusive shapes have their
own tests. Called out in a comment at the table.
Closes sui-h92