Wires dirties_on and augmented_by onto ReactiveDag.Node.Changes.MarkDirty,
so ordinary Ash writes trigger the cascade with no host boilerplate.
Two wirings, because they answer two different questions
dirties_on names action TYPES and becomes ONE GLOBAL change. Global (rather
than one per action) means actions declared later are covered too — the
point of the feature is that no write site can be forgotten, and a per-action
wiring would reintroduce exactly that risk. That is right for a source-fed
LEAF, where every write is an observation.
augmented_by names specific ACTIONS and becomes one change added to EACH of
those actions' own changes list. Per-action is not a weaker dirties_on
here — it is the only wiring that works. On a COMPUTED node the library writes
the rows itself through payload_action, which is an ordinary Ash write: a
global change would make every recompute re-dirty the cell it just computed,
and the drain would spin forever. Naming the human-facing actions excludes the
payload upsert BY CONSTRUCTION rather than by a filter someone must maintain.
Marking exactly once
Ash concatenates an action's own changes with the resource's global changes
and dedupes NEITHER (Ash.Changeset.run_action_changes/6). So an action named
in augmented_by whose type is also in dirties_on would run MarkDirty
twice. The frontier insert is ON CONFLICT DO NOTHING and would survive that,
but schedule_drain would enqueue twice per write — so such actions are
wired ONCE, by the global change, and skipped here.
The key derivation is resolved HERE, at compile time, and passed as options: the change then does no introspection per write.
Summary
Functions
Callback implementation for Spark.Dsl.Transformer.after_compile?/0.
Callback implementation for Spark.Dsl.Transformer.before?/1.
Functions
Callback implementation for Spark.Dsl.Transformer.after_compile?/0.
Callback implementation for Spark.Dsl.Transformer.before?/1.