Documentation

Workflow releases

Treat workflow names as immutable production targets. Console edits write Draft; Live traffic runs the published revision.

Workflow releases

Treat a workflow name as an immutable production target. Console edits write Draft. Live traffic runs the published revision.

This feature is gated by workflow_releases_enabled (default off). With the flag off, API execution still uses the mutable workflow row. Create and import still write revision r1 so backfill stays idempotent. Do not enable the flag in production until the checklist below is complete.

Concepts

TermMeaning
DraftCurrent workflows / workflow_events rows
Liveworkflows.published_revision_id
snapshot_hashDedup hash (content_hash column); includes child Live pins; excludes structure id/name
content_fingerprintDirty hash; ignores pin revision fields and structure id/name
Execution snapshotShort-lived Draft pin for async playground jobs (not a revision)

Dirty reasons are independent:

  • draft_content_changed — Draft fingerprint differs from Live
  • dependency_live_moved — a callback target's Live pin moved

Runtime

  • Production keys always run Live. There is no @12 or header pin.
  • Live async jobs store workflow_revision_id.
  • Playground Draft async jobs persist an execution snapshot and store execution_snapshot_id. The row is kept until expires_at has passed and no pending channel and no incomplete Oban job references it.
  • Deactivating a child does not break existing pins. by_name 404s inactive workflows. New Publish cannot pin an inactive target.

Console

  • Name cannot be changed after create.
  • Delete deactivates the workflow; revision history and Live pins stay on that row. The name can be reused by a new active workflow in the same project. Production by_name lookup only matches the active row.
  • Publish and rollback: organization owner/admin.
  • Restore draft: editors; relinks the original structured output when its schema still matches the revision snapshot. Clones only if that structure was edited or deleted, so sibling workflows are untouched.

Enablement

  1. Run mix backfill_workflow_releases until zero workflows lack a Live pointer (including inactive workflows).
  2. Confirm callback graphs are acyclic.
  3. Soak staging with the flag on: queued jobs, channel TTL refresh, Publish during in-flight work.
  4. Confirm dashboards for [:modelriver, :workflow, :missing_live] and [:modelriver, :workflow, :execution_snapshot_expired].

Set WORKFLOW_RELEASES_ENABLED=true only after those gates pass.