Squidie Workflow Authoring Usage Rules

Copy Markdown View Source

Workflow Shape

Steps

  • Prefer use Squidie.Step for custom steps.
  • Use Squidie.start_child_run/4 or Squidie.start_child_run/5 only from native steps that receive Squidie.Step.Context.
  • Provide a stable, storage-safe :child_key for every child run; treat it as the idempotency key for the parent run and parent step.
  • Keep child workflow modules backend-neutral, the same as parent workflows.
  • Return {:ok, output} for success.
  • Return {:defer, reason, schedule_in: seconds} when a native step observes non-failed domain state that should continue from the same logical attempt later without consuming retry budget.
  • Return {:error, reason} for terminal failure governed by workflow routing.
  • Return {:retry, reason} or {:retry, reason, opts} for retryable failure.
  • Keep side-effect idempotency inside the step or host domain boundary.
  • Use context.idempotency_key and context.claim_id for external reconciliation and action idempotency. Never expose or persist claim tokens in step output, logs, or host-facing errors.
  • Use raw Jido.Action modules only for explicit interop.

Data Mapping

  • Use payload contracts for start input validation.
  • Use step input: to select only the data a step needs.
  • Use step output: to place returned data under stable keys.
  • Use conditional transitions for inspectable routing decisions.
  • Use equals for exact matches and greater_than or less_than for numeric threshold routing.
  • Keep condition values JSON-safe so selected routes can be persisted.

Manual And Long-Running Work

  • Use :pause or approval_step/2 for operator-controlled boundaries.
  • Resolve manual gates through resume/3, approve/3, and reject/3.
  • Use :wait for workflow-scale delays, not arbitrary timers.
  • Use deadline: [within: milliseconds] on normal steps, :pause, or approval_step/2 when operators need durable SLA evidence. Treat deadline state as read-model data; alert delivery and escalation execution stay in the host app.
  • Use deferred continuation for domain-owned polling decisions made by a native step; use retry only for failures and :wait for definition-owned delays.
  • Use a child workflow instead when the step discovers separate work with its own lifecycle rather than rechecking the same declared step.
  • Use a normal handoff step plus a later signal or run when an external domain system owns polling, backoff, cancellation, and alert delivery.
  • Prefer cron or host scheduling when the whole workflow should start later.

Recovery

  • Mark irreversible external side effects with irreversible: true or compensatable: false.
  • Use recovery: :compensation or recovery: :undo on error transitions when the route has operational meaning.
  • Treat child runs as separate replay, retry, cancellation, and inspection boundaries. Do not mutate already-run parent steps to simulate dynamic expansion.
  • Use Squidie.record_dynamic_work/3 for bounded dynamic work that should be visible to operators but should not execute.
  • Use Squidie.schedule_dynamic_work/3 for bounded dynamic work that should be persisted and executed through the journal dispatch path.
  • Schedule dynamic work only after the origin runnable has applied; do not use dynamic scheduling to speculate ahead of the producer step.
  • Use Squidie.preview_dynamic_work/3 before recording when tooling needs to validate and render the candidate graph overlay without appending. Use the preview's added id lists and warnings instead of client-side graph diffing.
  • Pass :action_registry to dynamic-work preview and record calls when the overlay represents future executable work; pass it to every schedule call. Each executable dynamic node must use a host-approved action key.
  • Use dynamic node retry: [max_attempts: n] only when the host action is safe for repeated delivery. Treat dynamic edges as graph metadata, not dependency ordering.
  • Do not rely on "this step should only run once" as the side-effect safety model.