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 Type | Actor | Function | Execution |
|---|---|---|---|
| Code action | ServiceAccount | Code function | Direct call |
| Generative action | Model | Generative function | LLM call |
| Agentic action | Agent | Agentic function | Task assignment |
| Human action | Human | Human function | Task 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]
}
}