defmodule ArcadiaCloud.Provisioning.Step do @moduledoc """ Contract every saga step module implements. Steps MUST be: 1. Idempotent — re-running produces the same effect; check context for prior outputs (`SagaState.get_output/2`) before doing work. 2. Compensable — has an undo or explicitly declares it doesn't need one (compensate is optional; default = noop). 3. Self-describing — writes its result into the saga context via `SagaState.put_output/3` so later steps + compensation can find what to act on. Failure modes: {:ok, %SagaState{}} — step succeeded, advance. {:error, reason} — step failed, runner triggers compensation of all completed steps in reverse order. """ alias ArcadiaCloud.Provisioning.SagaState @callback execute(SagaState.t()) :: {:ok, SagaState.t()} | {:error, term()} @callback compensate(SagaState.t()) :: :ok | {:error, term()} @callback name() :: String.t() @optional_callbacks [compensate: 1] end