mdxui

Workflows

Understanding state machines and pipelines - coordinating actors and functions

Workflows

A Workflow is a coordinated sequence of functions executed by actors to accomplish a business process. Workflows come in two main forms: State Machines and Pipelines.


Workflow Types

State Machine

A State Machine workflow manages state transitions through events and actions.

Characteristics:

  • Explicit states and transitions
  • Event-driven
  • Can loop and branch
  • Maintains state between steps
  • Best for: Approval flows, order processing, task management

Pipeline

A Pipeline workflow executes a linear or branching sequence of functions.

Characteristics:

  • Sequential or parallel execution
  • Data flows through stages
  • Typically one-way (no loops)
  • Transforms data at each stage
  • Best for: ETL, data processing, CI/CD

State Machine Workflows

State machines define explicit states, events that trigger transitions, and actions to execute during transitions.

Anatomy of a State Machine

interface StateMachine {
  id: string
  name: string
  states: Record<string, State>
  initialState: string
}

interface State {
  name: string
  on: Record<string, Transition>
  entry?: Action[]   // Execute when entering state
  exit?: Action[]    // Execute when leaving state
}

interface Transition {
  target: string     // Next state
  guard?: Function   // Condition to check
  actions?: Action[] // Execute during transition
}

interface Action {
  type: 'code' | 'generative' | 'agentic' | 'human'
  actor: Actor
  function: Function
}

Example: Invoice Approval Workflow

const invoiceApprovalWorkflow: StateMachine = {
  id: 'invoice-approval',
  name: 'Invoice Approval',
  initialState: 'draft',
  states: {
    draft: {
      name: 'Draft',
      on: {
        submit: {
          target: 'validating',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: validateInvoice
          }]
        }
      }
    },
    validating: {
      name: 'Validating',
      on: {
        validated: {
          target: 'pending_approval',
          actions: [{
            type: 'generative',
            actor: model,
            function: extractInvoiceData
          }]
        },
        invalid: {
          target: 'draft'
        }
      }
    },
    pending_approval: {
      name: 'Pending Approval',
      on: {
        approve: {
          target: 'approved',
          guard: (invoice) => invoice.approver !== null,
          actions: [{
            type: 'human',
            actor: approver,
            function: approveInvoice
          }]
        },
        reject: {
          target: 'rejected',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: notifyRejection
          }]
        },
        escalate: {
          target: 'pending_approval',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: escalateToManager
          }]
        }
      }
    },
    approved: {
      name: 'Approved',
      entry: [{
        type: 'code',
        actor: serviceAccount,
        function: notifyApproval
      }],
      on: {
        pay: {
          target: 'paying',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: processPayment
          }]
        }
      }
    },
    paying: {
      name: 'Processing Payment',
      on: {
        paid: {
          target: 'paid',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: recordPayment
          }]
        },
        failed: {
          target: 'payment_failed'
        }
      }
    },
    paid: {
      name: 'Paid',
      entry: [{
        type: 'code',
        actor: serviceAccount,
        function: notifyPaymentComplete
      }]
    },
    rejected: {
      name: 'Rejected',
      on: {
        resubmit: {
          target: 'draft'
        }
      }
    },
    payment_failed: {
      name: 'Payment Failed',
      on: {
        retry: {
          target: 'paying'
        },
        cancel: {
          target: 'cancelled'
        }
      }
    },
    cancelled: {
      name: 'Cancelled'
    }
  }
}

Visualizing State Machines

┌─────────┐   submit    ┌────────────┐   validated    ┌──────────────────┐
│  Draft  │────────────▶│ Validating │───────────────▶│ Pending Approval │
└─────────┘             └────────────┘                └──────────────────┘
     ▲                        │                              │         │
     │                    invalid                       approve    reject
     │                        │                              │         │
     │                        ▼                              ▼         ▼
     │                  ┌─────────┐                    ┌──────────┐ ┌──────────┐
     │                  │  Draft  │                    │ Approved │ │ Rejected │
     │                  └─────────┘                    └──────────┘ └──────────┘
     │                                                       │            │
     │                                                      pay      resubmit
     │                                                       │            │
     │                                                       ▼            │
     │                                                  ┌────────┐        │
     │                                                  │ Paying │        │
     │                                                  └────────┘        │
     │                                                   │      │         │
     │                                                paid    failed      │
     │                                                   │      │         │
     │                                                   ▼      ▼         │
     │                                              ┌──────┐ ┌─────────┐ │
     │                                              │ Paid │ │ Failed  │ │
     │                                              └──────┘ └─────────┘ │
     │                                                            │       │
     └────────────────────────────────────────────────────────────┴───────┘

State Machine MDXLD

Define state machines declaratively in MDXLD:

---
$type: Workflow
$id: https://example.com/workflows/invoice-approval
name: Invoice Approval
kind: StateMachine
initialState: draft
---

# Invoice Approval Workflow

This workflow manages the approval and payment of invoices.

## States

<States>
  <State name="draft" on={{ submit: 'validating' }} />
  <State name="validating" on={{ validated: 'pending_approval', invalid: 'draft' }} />
  <State name="pending_approval" on={{ approve: 'approved', reject: 'rejected' }} />
  <State name="approved" on={{ pay: 'paying' }} />
  <State name="paying" on={{ paid: 'paid', failed: 'payment_failed' }} />
  <State name="paid" />
  <State name="rejected" on={{ resubmit: 'draft' }} />
  <State name="payment_failed" on={{ retry: 'paying', cancel: 'cancelled' }} />
  <State name="cancelled" />
</States>

## Actions

<Actions>
  <Action event="submit" function={validateInvoice} actor="ServiceAccount" />
  <Action event="validated" function={extractInvoiceData} actor="Model" />
  <Action event="approve" function={approveInvoice} actor="Human" />
  <Action event="pay" function={processPayment} actor="ServiceAccount" />
</Actions>

<WorkflowDiagram />

Pipeline Workflows

Pipelines execute functions in sequence or parallel, transforming data at each stage.

Anatomy of a Pipeline

interface Pipeline {
  id: string
  name: string
  stages: Stage[]
}

interface Stage {
  name: string
  type: 'sequential' | 'parallel'
  steps: Step[]
}

interface Step {
  name: string
  function: Function
  actor: Actor
  input?: string    // Variable name from previous step
  output?: string   // Variable name for next step
}

Example: Content Publishing Pipeline

const contentPublishingPipeline: Pipeline = {
  id: 'content-publishing',
  name: 'Content Publishing Pipeline',
  stages: [
    {
      name: 'Content Creation',
      type: 'sequential',
      steps: [
        {
          name: 'Generate Draft',
          function: generateBlogPost,
          actor: model,
          output: 'draft'
        },
        {
          name: 'Enhance Content',
          function: enhanceContent,
          actor: agent,
          input: 'draft',
          output: 'enhanced'
        }
      ]
    },
    {
      name: 'Quality Checks',
      type: 'parallel',
      steps: [
        {
          name: 'Check Grammar',
          function: checkGrammar,
          actor: model,
          input: 'enhanced',
          output: 'grammarCheck'
        },
        {
          name: 'Check SEO',
          function: analyzeSEO,
          actor: agent,
          input: 'enhanced',
          output: 'seoAnalysis'
        },
        {
          name: 'Check Brand Voice',
          function: checkBrandVoice,
          actor: model,
          input: 'enhanced',
          output: 'brandCheck'
        }
      ]
    },
    {
      name: 'Review',
      type: 'sequential',
      steps: [
        {
          name: 'Human Review',
          function: reviewContent,
          actor: editor,
          input: 'enhanced',
          output: 'reviewed'
        }
      ]
    },
    {
      name: 'Publishing',
      type: 'sequential',
      steps: [
        {
          name: 'Generate Social Cards',
          function: generateSocialCards,
          actor: serviceAccount,
          input: 'reviewed',
          output: 'socialCards'
        },
        {
          name: 'Publish to CMS',
          function: publishToCMS,
          actor: serviceAccount,
          input: 'reviewed',
          output: 'published'
        },
        {
          name: 'Send Notifications',
          function: notifyTeam,
          actor: serviceAccount,
          input: 'published'
        }
      ]
    }
  ]
}

Visualizing Pipelines

┌─────────────────┐
│ Content Creation │
└────────┬────────┘
         │
    ┌────▼────┐
    │ Generate│
    │  Draft  │
    └────┬────┘
         │
    ┌────▼────────┐
    │   Enhance   │
    │   Content   │
    └────┬────────┘
         │
┌────────┴────────┐
│ Quality Checks  │ (Parallel)
└────────┬────────┘
    ┌────┼────┐
    │    │    │
┌───▼─┐ │ ┌──▼────┐
│Grammar │ │  Brand │
│Check  │ │ │ Voice  │
└───┬─┘ │ └──┬────┘
    │   │    │
    │ ┌─▼──┐ │
    │ │SEO │ │
    │ │Analysis│
    │ └─┬──┘ │
    └───┼────┘
        │
   ┌────▼──────┐
   │   Review  │
   └────┬──────┘
        │
   ┌────▼────────┐
   │  Publishing │
   └────┬────────┘
        │
   ┌────▼─────────┐
   │Generate Cards│
   └────┬─────────┘
        │
   ┌────▼──────┐
   │ Publish to│
   │    CMS    │
   └────┬──────┘
        │
   ┌────▼─────┐
   │  Notify  │
   └──────────┘

Pipeline MDXLD

---
$type: Workflow
$id: https://example.com/workflows/content-publishing
name: Content Publishing Pipeline
kind: Pipeline
---

# Content Publishing Pipeline

Automated content creation, review, and publishing workflow.

## Stages

<Pipeline>
  <Stage name="Content Creation" type="sequential">
    <Step name="Generate Draft" function={generateBlogPost} actor="Model" />
    <Step name="Enhance Content" function={enhanceContent} actor="Agent" />
  </Stage>

  <Stage name="Quality Checks" type="parallel">
    <Step name="Check Grammar" function={checkGrammar} actor="Model" />
    <Step name="Check SEO" function={analyzeSEO} actor="Agent" />
    <Step name="Check Brand Voice" function={checkBrandVoice} actor="Model" />
  </Stage>

  <Stage name="Review" type="sequential">
    <Step name="Human Review" function={reviewContent} actor="Human" />
  </Stage>

  <Stage name="Publishing" type="sequential">
    <Step name="Generate Social Cards" function={generateSocialCards} actor="ServiceAccount" />
    <Step name="Publish to CMS" function={publishToCMS} actor="ServiceAccount" />
    <Step name="Send Notifications" function={notifyTeam} actor="ServiceAccount" />
  </Stage>
</Pipeline>

<PipelineDiagram />

How Actors Execute Functions in Workflows

Workflows coordinate Actors executing Functions to accomplish business processes.

The Execution Model

// Workflow orchestrates Actors and Functions
async function executeWorkflow(workflow: Workflow, context: any): Promise<any> {
  let state = workflow.initialState
  let data = context

  while (state !== 'completed') {
    const currentState = workflow.states[state]

    // Execute entry actions
    if (currentState.entry) {
      for (const action of currentState.entry) {
        data = await executeAction(action, data)
      }
    }

    // Wait for event
    const event = await waitForEvent(workflow, state)

    // Find transition
    const transition = currentState.on[event.type]
    if (!transition) continue

    // Check guard condition
    if (transition.guard && !transition.guard(data)) {
      continue
    }

    // Execute exit actions
    if (currentState.exit) {
      for (const action of currentState.exit) {
        data = await executeAction(action, data)
      }
    }

    // Execute transition actions
    if (transition.actions) {
      for (const action of transition.actions) {
        data = await executeAction(action, data)
      }
    }

    // Transition to next state
    state = transition.target
  }

  return data
}

async function executeAction(action: Action, data: any): Promise<any> {
  switch (action.type) {
    case 'code':
      // ServiceAccount executes Code function
      return await action.function(data)

    case 'generative':
      // Model executes Generative function
      return await action.function(data)

    case 'agentic':
      // Agent performs Agentic task
      const task: Task = {
        type: action.function.name,
        description: action.function.description,
        context: data
      }
      const result = await action.actor.do(task)
      return result.data

    case 'human':
      // Human performs Human task
      const humanTask: HumanTask = {
        type: action.function.name,
        assignee: action.actor,
        description: action.function.description,
        data: data,
        ui: action.function.ui
      }
      const humanResult = await action.actor.do(humanTask)
      return humanResult.data
  }
}

Actor-Function Mapping in Workflows

Action TypeActorFunctionExecution
Code actionServiceAccountCode functionDirect call
Generative actionModelGenerative functionLLM call
Agentic actionAgentAgentic functionTask assignment
Human actionHumanHuman functionTask assignment

Workers Performing Tasks

Workers (Agents and Humans) perform tasks rather than execute functions directly.

The Worker Interface in Workflows

interface Worker {
  ask(prompt: string): Promise<Response>
  do(task: Task): Promise<Result>
}

// In a workflow, Workers receive Tasks
async function performReviewTask(invoice: Invoice, worker: Worker): Promise<Review> {
  const task: Task = {
    type: 'review',
    description: `Review invoice ${invoice.number}`,
    context: { invoice }
  }

  const result = await worker.do(task)
  return result.data
}

// Worker can be Agent or Human
const agentReview = await performReviewTask(invoice, reviewAgent)
const humanReview = await performReviewTask(invoice, seniorReviewer)

Hybrid Human-AI Workflows

Workflows can intelligently route tasks between Agents and Humans:

const smartApprovalWorkflow: StateMachine = {
  id: 'smart-approval',
  name: 'Smart Approval',
  initialState: 'pending',
  states: {
    pending: {
      name: 'Pending Approval',
      on: {
        review: {
          target: 'reviewing',
          actions: [{
            type: 'agentic',
            actor: async (invoice) => {
              // Route to Agent for routine approvals
              if (invoice.amount < 1000 && invoice.vendor.trusted) {
                return reviewAgent
              }
              // Route to Human for high-value or risky approvals
              return humanApprover
            },
            function: approveInvoice
          }]
        }
      }
    },
    reviewing: {
      name: 'Under Review',
      on: {
        approved: { target: 'approved' },
        rejected: { target: 'rejected' },
        escalate: {
          target: 'escalated',
          actions: [{
            type: 'human',
            actor: manager,
            function: approveInvoice
          }]
        }
      }
    },
    approved: { name: 'Approved' },
    rejected: { name: 'Rejected' },
    escalated: {
      name: 'Escalated to Manager',
      on: {
        approved: { target: 'approved' },
        rejected: { target: 'rejected' }
      }
    }
  }
}

Complete Workflow Example

Here's a complete example showing how Actors execute Functions and Workers perform Tasks:

Order Fulfillment Workflow

const orderFulfillmentWorkflow: StateMachine = {
  id: 'order-fulfillment',
  name: 'Order Fulfillment',
  initialState: 'received',
  states: {
    received: {
      name: 'Order Received',
      entry: [{
        type: 'code',
        actor: serviceAccount,
        function: notifyOrderReceived
      }],
      on: {
        validate: {
          target: 'validating',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: validateOrder
          }]
        }
      }
    },
    validating: {
      name: 'Validating Order',
      on: {
        validated: {
          target: 'fraud_check',
          actions: [{
            type: 'generative',
            actor: model,
            function: checkFraud
          }]
        },
        invalid: {
          target: 'cancelled'
        }
      }
    },
    fraud_check: {
      name: 'Fraud Check',
      on: {
        safe: {
          target: 'processing',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: authorizePayment
          }]
        },
        suspicious: {
          target: 'manual_review',
          actions: [{
            type: 'human',
            actor: fraudAnalyst,
            function: reviewFraudCase
          }]
        }
      }
    },
    manual_review: {
      name: 'Manual Fraud Review',
      on: {
        approved: { target: 'processing' },
        rejected: { target: 'cancelled' }
      }
    },
    processing: {
      name: 'Processing Order',
      on: {
        process: {
          target: 'fulfilling',
          actions: [
            {
              type: 'code',
              actor: serviceAccount,
              function: capturePayment
            },
            {
              type: 'code',
              actor: serviceAccount,
              function: createShipment
            }
          ]
        }
      }
    },
    fulfilling: {
      name: 'Fulfilling Order',
      on: {
        shipped: {
          target: 'shipped',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: notifyShipped
          }]
        },
        backorder: {
          target: 'backordered'
        }
      }
    },
    shipped: {
      name: 'Shipped',
      on: {
        delivered: {
          target: 'delivered',
          actions: [{
            type: 'code',
            actor: serviceAccount,
            function: notifyDelivered
          }]
        }
      }
    },
    delivered: {
      name: 'Delivered',
      entry: [{
        type: 'generative',
        actor: model,
        function: sendThankYouEmail
      }]
    },
    backordered: {
      name: 'Backordered',
      on: {
        available: { target: 'fulfilling' },
        cancel: { target: 'cancelled' }
      }
    },
    cancelled: {
      name: 'Cancelled',
      entry: [{
        type: 'code',
        actor: serviceAccount,
        function: refundPayment
      }]
    }
  }
}

This workflow demonstrates:

  • ServiceAccounts executing Code functions (validation, payments, notifications)
  • Models executing Generative functions (fraud check, thank you email)
  • Humans performing tasks (fraud review)
  • State transitions based on events
  • Parallel actions during transitions
  • Entry/exit actions for states

Workflow Patterns

Sequential Pattern

Execute functions one after another:

async function sequentialWorkflow(data: any): Promise<any> {
  const step1 = await function1(data)
  const step2 = await function2(step1)
  const step3 = await function3(step2)
  return step3
}

Parallel Pattern

Execute functions concurrently:

async function parallelWorkflow(data: any): Promise<any> {
  const [result1, result2, result3] = await Promise.all([
    function1(data),
    function2(data),
    function3(data)
  ])
  return combineResults([result1, result2, result3])
}

Conditional Pattern

Choose execution path based on conditions:

async function conditionalWorkflow(data: any): Promise<any> {
  if (condition1(data)) {
    return await path1(data)
  } else if (condition2(data)) {
    return await path2(data)
  } else {
    return await defaultPath(data)
  }
}

Map-Reduce Pattern

Process collections in parallel and combine results:

async function mapReduceWorkflow(items: any[]): Promise<any> {
  const results = await Promise.all(
    items.map(item => processItem(item))
  )
  return reduceResults(results)
}

Retry Pattern

Retry failed operations with backoff:

async function retryWorkflow(
  func: Function,
  maxRetries: number = 3
): Promise<any> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await func()
    } catch (error) {
      if (i === maxRetries - 1) throw error
      await sleep(1000 * Math.pow(2, i)) // Exponential backoff
    }
  }
}

Circuit Breaker Pattern

Prevent cascading failures:

class CircuitBreaker {
  private failureCount = 0
  private state: 'closed' | 'open' | 'half-open' = 'closed'

  async execute(func: Function): Promise<any> {
    if (this.state === 'open') {
      throw new Error('Circuit breaker is open')
    }

    try {
      const result = await func()
      this.onSuccess()
      return result
    } catch (error) {
      this.onFailure()
      throw error
    }
  }

  private onSuccess() {
    this.failureCount = 0
    this.state = 'closed'
  }

  private onFailure() {
    this.failureCount++
    if (this.failureCount >= 3) {
      this.state = 'open'
    }
  }
}

Best Practices

1. Keep States Simple

Each state should have a clear purpose:

// Good
states: {
  draft: { ... },
  pending_approval: { ... },
  approved: { ... }
}

// Bad - too many states
states: {
  draft: { ... },
  draft_with_validation_errors: { ... },
  draft_being_edited: { ... },
  draft_auto_saved: { ... }
}

2. Use Meaningful Events

Events should describe what happened:

// Good
on: {
  approved: { target: 'approved' },
  rejected: { target: 'rejected' }
}

// Bad
on: {
  button_clicked: { target: 'approved' },
  action_1: { target: 'rejected' }
}

3. Choose the Right Actor

Match function complexity to actor capabilities:

// Good - simple validation uses Code function
actions: [{ type: 'code', actor: serviceAccount, function: validateEmail }]

// Bad - overkill to use Agent for simple validation
actions: [{ type: 'agentic', actor: agent, function: validateEmail }]

4. Handle Errors Gracefully

Add error states and transitions:

states: {
  processing: {
    on: {
      success: { target: 'completed' },
      error: { target: 'error', actions: [logError, notifyAdmin] },
      retry: { target: 'processing' }
    }
  },
  error: {
    on: {
      retry: { target: 'processing' },
      cancel: { target: 'cancelled' }
    }
  }
}

5. Use Guards for Validation

Validate before state transitions:

on: {
  approve: {
    target: 'approved',
    guard: (invoice) => invoice.amount > 0 && invoice.approver !== null,
    actions: [approveInvoice]
  }
}

Next Steps

  • Read Actors to understand who executes in workflows
  • See Functions to understand what gets executed
  • Explore Verbs for event and action naming
  • Review Contexts for workflow execution environments

On this page