Replays a recorded workflow history against current workflow code.
The pre-deploy compatibility check: fetch a real execution's history, run it through the deterministic executor with the code you are about to ship, and learn whether in-flight workflows would survive the deploy — before the deploy.
{:ok, history} = Temporalex.Client.fetch_workflow_history(handle)
:ok = Temporalex.Replay.replay(history, workflows: [Checkout])A divergence — the code scheduling a different activity, a different
timer, or completing differently than the record — returns
{:error, {:nondeterminism, detail}}. That is exactly the error those
workflows would hit in production, surfaced in CI instead.
Coverage
Replays histories built from: workflow start, activity schedule /
completion / failure, timers, signals, cancellation, and terminal
completion / failure / cancellation. Any other event kind returns
{:error, {:unsupported_event, type, event_id}} rather than being
silently skipped — a replay that ignores part of the record proves
nothing. Notable not-yet-supported: patch markers, child workflows,
updates, continue-as-new, local activities.
Activity inputs are compared as decoded terms (ETF payloads compare exactly; a lossy codec could round-trip differently). Only the first workflow-input payload is used — Temporalex workflows take one input term; multi-payload starts from non-Elixir clients are truncated to it.
Activation-grouping drift is caught even though the model is flat: the runner refuses to deliver an outcome while commands sit unconsumed, so refactoring sequential work into parallel (or back) diverges loudly even when the global command order is unchanged.
Fixtures must be the protobuf form (fetch_workflow_history(handle, raw: true)). temporal workflow show --output json files are NOT
replayable: .temporal.api.history is absent from temporalio-common's
pbjson list, so proto-JSON does not round-trip (the same reason the NIF
returns bytes).
Typical CI shape: check fixture files in with
fetch_workflow_history(handle, raw: true), and replay them in a test:
for fixture <- Path.wildcard("test/fixtures/histories/*.binpb") do
{:ok, history} = Temporalex.Replay.decode(File.read!(fixture))
assert :ok = Temporalex.Replay.replay(history, workflows: [Checkout])
end
Summary
Functions
Decodes a raw history fixture (the raw: true fetch shape) into a
Temporalex.History.
Replays history against the current code of one of workflows:.
Functions
@spec decode(binary()) :: {:ok, Temporalex.History.t()} | {:error, term()}
Decodes a raw history fixture (the raw: true fetch shape) into a
Temporalex.History.
@spec replay( Temporalex.History.t(), keyword() ) :: :ok | {:error, term()}
Replays history against the current code of one of workflows:.
The module is resolved by the recorded workflow type (respecting name:
overrides). Returns :ok when every recorded decision matches what the
current code decides, {:error, {:nondeterminism, detail}} on divergence,
and {:error, {:unsupported_event, type, event_id}} for history the
replayer cannot yet drive.