StatifierBlocks.Migration (StatifierBlocks v0.36.0)

Copy Markdown View Source

The state mapping between two compiled revisions of one document (ADR-0004's amendment of 2026-09-23, clauses M1 to M6).

"Migration" here means moving a waiting execution from one chart to another, the act sp-ADR-0013 (statifier_persistence, docs/adr/0013-the-migration-plan.md) decides. It is not a block type's config migration: StatifierBlocks.BlockType.migrate_config/2 rewrites an old block's stored config as the block resolves, and has nothing to do with this module.

This package does not migrate anything. plan/2 reads two %StatifierBlocks.Compiled{} artifacts and answers plain data a host copies into the migration plan it writes; saving, compiling or publishing a document never produces one, and applying a plan is the persistence layer's. The answer names no timer, datamodel change, drop or chart hash: those are the plan author's (M5, M6).

Summary

Types

One moved invocation, [from_state_id, from_ordinal, to_state_id, to_ordinal]: sp-ADR-0013 decision 1's encoding of an invocation key. An ordinal is the zero-based, document-order position of an <invoke> among its state's own <invoke> children.

The mapping (M5), with string keys and plain data only, so it encodes as JSON as it stands.

One state of the from chart with no counterpart in the to chart (M3): the state id, and the block and role that owned it by the from side's provenance map. "role" is nil for a block's own state.

Functions

Maps the states of from's chart onto the states of to's chart, for two compiled revisions of one document.

Types

invocation()

@type invocation() :: [String.t() | non_neg_integer()]

One moved invocation, [from_state_id, from_ordinal, to_state_id, to_ordinal]: sp-ADR-0013 decision 1's encoding of an invocation key. An ordinal is the zero-based, document-order position of an <invoke> among its state's own <invoke> children.

mapping()

@type mapping() :: %{
  required(String.t()) =>
    %{required(String.t()) => String.t()} | [invocation()] | [unmapped()]
}

The mapping (M5), with string keys and plain data only, so it encodes as JSON as it stands.

  • "states" - from state id to to state id, for every mapped state that is not a history pseudo-state.
  • "history" - the same, for every mapped history pseudo-state.
  • "invocations" - invocation/0 entries, ordered by from state id and then ordinal.
  • "unmapped" - unmapped/0 entries, ordered by state id.

unmapped()

@type unmapped() :: %{required(String.t()) => String.t() | nil}

One state of the from chart with no counterpart in the to chart (M3): the state id, and the block and role that owned it by the from side's provenance map. "role" is nil for a block's own state.

Functions

plan(from, to)

@spec plan(StatifierBlocks.Compiled.t(), StatifierBlocks.Compiled.t()) ::
  {:ok, mapping()} | {:error, :different_documents}

Maps the states of from's chart onto the states of to's chart, for two compiled revisions of one document.

The clauses of ADR-0004's amendment of 2026-09-23, each as this function applies it:

  • M1. A from state corresponds to a to state when both carry the same state id and each side's by_state_id names the same block id and the same role for it; equal ids whose owners differ do not correspond. The states are the from chart's <state>, <parallel>, <final> and <history> elements, read from its SCXML: by_state_id also keys a delayed <send>'s id, which is not a state. Only the two artifacts are read, and equal inputs give an equal answer.
  • M2. Every from state with a counterpart maps to it, roles included, and each identity entry is written out. An <invoke> of a mapped state maps to the <invoke> at the same ordinal of the state it maps to, when that state has one; otherwise it gets no entry.
  • M3. Every other from state - each state of a deleted block, and each auxiliary state a surviving block no longer mints - is listed under "unmapped" with its owner and appears in no other field. A to state with no from counterpart appears nowhere.
  • M4. Two artifacts whose compilation records carry different document_ids are refused with {:error, :different_documents}, and nothing else is computed. Revisions are not compared: two artifacts of one document map in either order.
  • M5. "states", "history" and "invocations" carry sp-ADR-0013's field names and its map form (statifier_persistence, docs/adr/0013-the-migration-plan.md, decision 1), so a host copies them into its plan unchanged. "unmapped" is a report, not a plan field.

Raises ArgumentError for an artifact StatifierBlocks.Compiler.compile/3 never produces: one whose scxml is not well-formed XML, or whose from chart holds a state its own provenance map does not own.