A workflow in MAEL is a directed acyclic graph of steps — agent calls, human approvals, conditions, parallel fan-out, and sub-workflows — defined declaratively in YAML under workflows/.

Three core guarantees

Completeness

A workflow either completes every step, or compensates every side effect it produced before failing. There is no middle state invisible to operators.

Observability

Every step transition, decision, tool call, cost increment, and approval event is a typed event, persisted to the audit log. Any run can be replayed as a complete narrative.

Isolation

A tenant’s workflow accesses only that tenant’s agents, memory, data, and API quotas — even executing on the same shared worker pool as other tenants’ runs.

Step types

DAG execution

Steps form a directed acyclic graph — cycle detection runs at definition load time, not at execution time. Independent steps at the same topological depth execute in parallel (the “frontier”); a step becomes eligible the moment all its dependencies complete, not on a fixed schedule. (Simplified from full-content-pipeline.yaml’s real 17 steps.)

State machine

Runs and steps each have their own state machine, persisted to PostgreSQL with CAS-guarded transitions — a step can only move PENDING → RUNNING via a compare-and-swap that fails cleanly if another worker already claimed it (claim_step), which is the actual idempotency mechanism, not just a Redis lock.

Checkpointing and resume

Every step completion writes a checkpoint. A worker crash mid-run doesn’t lose progress — on resume, completed steps are never re-run; only the step that was in-flight (or never started) executes. Idempotency for resume depends on the same ADR-006 layers agents already use, so a resumed step that partially executed before the crash doesn’t double-apply its side effects. A heartbeat + stale-run reaper detects workers that died without a clean shutdown (no crash signal, just silence) and reclaims their in-flight steps after an idle threshold — this is the same mechanism that recovers Redis Streams’ pending-entries list after a rolling deploy (see Events).

Compensation (saga pattern)

On unrecoverable failure, the engine runs compensation steps in reverse order of the side effects that succeeded — a saga, not a distributed transaction. Compensation steps are isolated: a failure compensating one side effect doesn’t prevent compensating the others.

Retry, timeouts, and dead-letter

Each step has its own retry policy (exponential backoff) and timeout hierarchy (step timeout, workflow-level timeout, idle/stall detection). A step that exhausts retries routes to a dead-letter path rather than silently failing the whole run — the article gets a review_required status, not a vanished workflow.

Human approval gates

Approval steps are non-blocking — the workflow suspends (not a worker thread spinning idle) and resumes on an external event (Slack interactive action or a dashboard click). Approval requests carry an escalation timeout and can be delegated. For content specifically, approval is mandatory on every publish in the standard pipeline — the content-evaluation gate (see AI Agents) picks the queue an article lands in, but never approves or publishes itself.

Events

AI Agents