# `StatifierBlocks.Migration`
[🔗](https://github.com/riddler/statifier_blocks/blob/v0.44.1/lib/statifier_blocks/migration.ex#L1)

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: `c: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).

# `invocation`

```elixir
@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`

```elixir
@type mapping() :: %{
  required(String.t()) =&gt;
    %{required(String.t()) =&gt; 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"` - `t:invocation/0` entries, ordered by from state id and
    then ordinal.
  * `"unmapped"` - `t:unmapped/0` entries, ordered by state id.

# `unmapped`

```elixir
@type unmapped() :: %{required(String.t()) =&gt; 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.

# `plan`

```elixir
@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_id`s 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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
