Reference Workflows

Copy Markdown View Source

The minimal host app contains executable reference workflows for the product lane described in Positioning. They use Jizoku workflow and step APIs in the happy path, keep host scheduling and delivery outside the workflow definition, and run through the same smoke and resilience harnesses as the rest of the example app.

Use these examples when you want to see how the runtime features fit together inside a host application without adding a dashboard or a separate workflow service.

Where They Live

The reference host app is in examples/minimal_host_app.

The app exposes workflow operations through MinimalHostApp.WorkflowRuns, which is the host-facing boundary a Phoenix context or OTP service would normally wrap. The workflow modules live under examples/minimal_host_app/lib/minimal_host_app/workflows, and their step modules live under examples/minimal_host_app/lib/minimal_host_app/steps.

Workflow Map

WorkflowTrigger shapeWhat it demonstrates
PaymentRecoveryManualRetry policy, explicit recovery routing, non-compensatable side effects, and replay boundaries.
ManualApprovalManualDurable operator approval, rejection, pause state, resume, and audit history.
DependencyRecoveryManualIndependent roots, dependency joins, named path input mapping, and inspectable joined work.
SagaCheckoutManualReversible side-effect metadata and retry exhaustion on a later step.
DailyDigestManual and cronOne workflow graph shared by manual and scheduled starts, including cron idempotency metadata.
RecurringCursorManualNative continue-as-new, fresh successor history, explicit lineage, and bounded chain inspection.

Payment Recovery

MinimalHostApp.Workflows.PaymentRecovery models a customer-facing recovery flow:

step :check_gateway_status, MinimalHostApp.Steps.CheckGatewayStatus,
  retry: [max_attempts: 5, backoff: [type: :exponential, min: 1_000, max: 1_000]]

transition :check_gateway_status,
  on: :error,
  to: :issue_gateway_credit,
  recovery: :compensation

step :notify_customer, MinimalHostApp.Steps.NotifyCustomer, compensatable: false

This example shows three boundaries:

  • Retry policy belongs to workflow progression, not to the host job runner.
  • Recovery transitions are durable route choices that show up in inspection.
  • Non-compensatable side effects make replay require explicit operator intent after the notification step has completed.

The smoke path starts this workflow through MinimalHostApp.WorkflowRuns.start_payment_recovery/1, waits for worker execution, and inspects the completed run.

Manual Approval

MinimalHostApp.Workflows.ManualApproval uses an approval step:

approval_step :wait_for_approval, output: :approval

transition :wait_for_approval, on: :ok, to: :record_approval
transition :wait_for_approval, on: :error, to: :record_rejection

The approval step is durable state in the journal. It is not a process waiting in memory. Host code resolves the boundary through public APIs such as Jizoku.approve/2 or Jizoku.reject/2, and inspection history keeps the pause and resolution facts visible for operator tools.

Dependency Recovery

MinimalHostApp.Workflows.DependencyRecovery starts two independent roots and joins them before notification preparation:

step :load_account, MinimalHostApp.Steps.LoadAccount
step :load_invoice, MinimalHostApp.Steps.LoadInvoice

step :prepare_notification, MinimalHostApp.Steps.PrepareNotification,
  after: [:load_account, :load_invoice],
  input: [
    account_id: [:account, :id],
    invoice_id: [:invoice, :id],
    account_tier: [:account, :tier]
  ]

The join step consumes named values from durable run context instead of relying on transient process state. inspect_run(..., include_history: true) shows the mapped input that reached :prepare_notification.

Saga Checkout

MinimalHostApp.Workflows.SagaCheckout demonstrates explicit compensation metadata:

step :reserve_inventory, MinimalHostApp.Steps.ReserveInventory,
  compensate: MinimalHostApp.Steps.ReleaseInventory

step :authorize_payment, MinimalHostApp.Steps.AuthorizePayment,
  compensate: MinimalHostApp.Steps.VoidPaymentAuthorization

step :capture_payment, MinimalHostApp.Steps.CapturePayment, retry: [max_attempts: 2]

When capture exhausts its retry policy, Jizoku keeps the completed side-effecting steps and their declared rollback callbacks visible through run inspection, graph inspection, and explanation. The example keeps each external effect behind a step module, while the workflow definition remains the place where retry and compensation semantics are visible.

Daily Digest

MinimalHostApp.Workflows.DailyDigest has a manual trigger and a cron trigger sharing one graph:

trigger :manual_digest do
  manual()
end

trigger :daily_digest do
  cron "@reboot", timezone: "Etc/UTC", idempotency: :return_existing_run
end

The workflow declares schedule intent. The host app owns recurring delivery and sends cron payloads into the runtime boundary. The smoke path verifies that a manual digest and a cron-activated digest complete through the same workflow graph.

Recurring Cursor

MinimalHostApp.Workflows.RecurringCursor returns native continue-as-new from its first cursor step:

{:continue_as_new, %{cursor: 1}, key: "cursor-1", definition: :current}

The smoke path executes both runs, verifies the predecessor is :continued, verifies the successor completes with only %{cursor: 1}, and traverses the two-run continuation chain through an explicit five-hop bound. This is the reference for recurring or paginated work that must periodically start with a fresh run history.

How To Run Them

Run the full smoke path from the example app:

cd examples/minimal_host_app
MIX_ENV=test mix example.smoke

That command exercises the reference workflows through the host app boundary, then checks inspection, replay, cancellation, cron activation, local transactions, and compensation behavior.

For restart-specific behavior, run:

MIX_ENV=test mix example.resilience

For a bounded mixed workload of success, retry, replay, and cancellation, run:

MIX_ENV=test mix example.soak