Workflow Features

Spectral workflows define state machines with transitions, making it easy to model business processes, application flows, and complex state management.

Basic Workflow Syntax

A workflow consists of states and transitions between those states:

workflow OrderFlow {
    Pending -> Processing
    Processing -> Shipped
    Shipped -> Delivered
}
✨ Edit in Studio

Initial State Keyword

Use the initial keyword to explicitly define which state a workflow should start in. This is optional—if not specified, the workflow will default to the first state.

Setting Initial State

workflow TaskFlow {
    initial Todo                // Explicitly set initial state
    Todo -> InProgress
    InProgress -> Done
    Done -> Archived
}
✨ Edit in Studio

Without Initial State

If you don't specify an initial state, the workflow uses the first state mentioned:

workflow TaskFlow {
    Todo -> InProgress          // Todo becomes the initial state
    InProgress -> Done
    Done -> Archived
}
✨ Edit in Studio

Workflow States

Simple States

workflow ContentFlow {
    initial Draft
    Draft -> Review
    Review -> Published
    Published -> Archived
}
✨ Edit in Studio

Multiple Transitions from a State

workflow OrderFlow {
    initial Pending
    Pending -> Processing
    Pending -> Cancelled        // Multiple transitions from Pending
    Processing -> Shipped
    Processing -> Cancelled     // Can cancel from Processing too
    Shipped -> Delivered
}
✨ Edit in Studio

Bidirectional Transitions

Model workflows where states can move back and forth:

workflow DocumentFlow {
    initial Draft
    Draft -> Review
    Review -> Draft             // Can return to draft
    Review -> Approved
    Approved -> Published
    Published -> Archived
    Archived -> Draft           // Can revive from archive
}
✨ Edit in Studio

Complex Workflows

Multi-Path Workflows

workflow PaymentFlow {
    initial Pending
    
    // Success path
    Pending -> Authorized
    Authorized -> Captured
    Captured -> Completed
    
    // Alternative paths
    Pending -> Failed
    Authorized -> Voided
    Captured -> Refunded
    
    // All paths can reach Completed
    Refunded -> Completed
    Failed -> Completed
}
✨ Edit in Studio

Approval Workflows

workflow ApprovalFlow {
    initial Submitted
    
    Submitted -> UnderReview
    UnderReview -> Approved
    UnderReview -> Rejected
    UnderReview -> NeedsChanges
    
    NeedsChanges -> Submitted   // Loop back
    Rejected -> Closed
    Approved -> Closed
}
✨ Edit in Studio

Content Management

workflow ContentLifecycle {
    initial Draft
    
    // Creation phase
    Draft -> Review
    
    // Review phase
    Review -> InRevision
    Review -> Approved
    InRevision -> Review        // Loop back
    
    // Publication phase
    Approved -> Scheduled
    Scheduled -> Published
    
    // Maintenance phase
    Published -> Unpublished
    Unpublished -> Archived
    Archived -> Draft           // Can revive content
}
✨ Edit in Studio

Code Generation

TypeScript Output

Workflows generate TypeScript enums and state machine classes:

// Generated from workflow TaskFlow
export enum TaskFlowState {
  Todo = "Todo",
  InProgress = "InProgress",
  Done = "Done",
  Archived = "Archived"
}

export class TaskFlowWorkflow {
  private currentState: TaskFlowState;
  
  constructor(initialState?: TaskFlowState, context?: SpectralContext) {
    // Uses the initial state from the workflow definition
    this.currentState = initialState ?? TaskFlowState.Todo;
    this.context = context || new SpectralContext();
  }
  
  canTransitionTo(state: TaskFlowState): boolean {
    const validTransitions = this.getValidTransitions();
    return validTransitions.includes(state);
  }
  
  transitionTo(state: TaskFlowState): boolean {
    if (this.canTransitionTo(state)) {
      const oldState = this.currentState;
      this.currentState = state;
      this.context.addEvent({
        type: "state-transition",
        workflow: "TaskFlow",
        from: oldState,
        to: state,
        timestamp: new Date()
      });
      return true;
    }
    return false;
  }
  
  getCurrentState(): TaskFlowState {
    return this.currentState;
  }
  
  private getValidTransitions(): TaskFlowState[] {
    switch (this.currentState) {
      case TaskFlowState.Todo:
        return [TaskFlowState.InProgress];
      case TaskFlowState.InProgress:
        return [TaskFlowState.Done];
      case TaskFlowState.Done:
        return [TaskFlowState.Archived];
      default:
        return [];
    }
  }
}

Python Output

from enum import Enum
from typing import List, Optional
from datetime import datetime

class TaskFlowState(Enum):
    TODO = "Todo"
    IN_PROGRESS = "InProgress"
    DONE = "Done"
    ARCHIVED = "Archived"

class TaskFlowWorkflow:
    def __init__(
        self,
        initial_state: Optional[TaskFlowState] = None,
        context: Optional[SpectralContext] = None
    ):
        # Uses the initial state from the workflow definition
        self.current_state = initial_state or TaskFlowState.TODO
        self.context = context or SpectralContext()
    
    def can_transition_to(self, state: TaskFlowState) -> bool:
        valid_transitions = self._get_valid_transitions()
        return state in valid_transitions
    
    def transition_to(self, state: TaskFlowState) -> bool:
        if self.can_transition_to(state):
            old_state = self.current_state
            self.current_state = state
            self.context.add_event({
                "type": "state-transition",
                "workflow": "TaskFlow",
                "from": old_state.value,
                "to": state.value,
                "timestamp": datetime.now()
            })
            return True
        return False
    
    def get_current_state(self) -> TaskFlowState:
        return self.current_state
    
    def _get_valid_transitions(self) -> List[TaskFlowState]:
        transitions = {
            TaskFlowState.TODO: [TaskFlowState.IN_PROGRESS],
            TaskFlowState.IN_PROGRESS: [TaskFlowState.DONE],
            TaskFlowState.DONE: [TaskFlowState.ARCHIVED]
        }
        return transitions.get(self.current_state, [])

Workflow with Types

Combine workflows with type definitions for complete business logic:

enum OrderStatus {
    Pending
    Processing
    Shipped
    Delivered
    Cancelled
}

type Order {
    id: string
    customerId: string
    status: OrderStatus
    items: OrderItem[]
    total: number
}

type OrderItem {
    productId: string
    quantity: number
    price: number
}

workflow OrderFlow {
    initial Pending
    
    Pending -> Processing
    Pending -> Cancelled
    Processing -> Shipped
    Processing -> Cancelled
    Shipped -> Delivered
}

visitor OrderManager {
    can create_order {
        created_order {
            can view_order
            can cancel_order {
                cancelled_order {
                    can view_order
                }
            }
        }
    }
    
    can process_order {
        processing_order {
            can ship_order
            can cancel_order
        }
    }
}
✨ Edit in Studio

Best Practices

1. Use Descriptive State Names

workflow PaymentFlow {
    initial AwaitingPayment     // ✓ Good: Clear what this state means
    AwaitingPayment -> Processing
}

// Avoid:
workflow PaymentFlow {
    initial State1              // ✗ Bad: Not descriptive
    State1 -> State2
}
✨ Edit in Studio

2. Set Initial States Explicitly

workflow TaskFlow {
    initial Todo                // ✓ Good: Explicit initial state
    Todo -> InProgress
}
✨ Edit in Studio

3. Model All Valid Transitions

workflow OrderFlow {
    initial Pending
    Pending -> Processing
    Pending -> Cancelled        // ✓ Good: All valid paths from each state
    Processing -> Shipped
    Processing -> Cancelled
}
✨ Edit in Studio

4. Keep Workflows Focused

Break large workflows into smaller, focused workflows:

// Instead of one massive workflow:
workflow OrderAndShipping {
    // 20+ states...
}

// Break it down:
workflow OrderFlow {
    initial Pending
    Pending -> Processing
    Processing -> Ready
}

workflow ShippingFlow {
    initial Preparing
    Preparing -> Shipped
    Shipped -> Delivered
}
✨ Edit in Studio

5. Align Workflows with Enums

enum TaskStatus {
    Todo
    InProgress
    Done
    Archived
}

workflow TaskFlow {
    initial Todo                // ✓ Good: States match enum values
    Todo -> InProgress
    InProgress -> Done
    Done -> Archived
}
✨ Edit in Studio

Examples

Blog Publishing

enum PostStatus {
    Draft
    Review
    Scheduled
    Published
    Archived
}

type Post {
    id: string
    title: string
    content: string
    status: PostStatus
    author: User
    publishDate: datetime
}

workflow PostFlow {
    initial Draft
    
    Draft -> Review
    Review -> Draft             // Send back for changes
    Review -> Scheduled
    Scheduled -> Published
    Published -> Archived
    Archived -> Draft           // Revive old content
}
✨ Edit in Studio

Task Management

enum Priority {
    Low
    Medium
    High
    Critical
}

enum TaskStatus {
    Todo
    InProgress
    Blocked
    Done
    Cancelled
}

type Task {
    id: string
    title: string
    status: TaskStatus
    priority: Priority
    assignee: User
    subtasks: Task[]
}

workflow TaskFlow {
    initial Todo
    
    Todo -> InProgress
    Todo -> Cancelled
    InProgress -> Blocked
    InProgress -> Done
    InProgress -> Cancelled
    Blocked -> InProgress
    Blocked -> Cancelled
}
✨ Edit in Studio

Incident Management

enum IncidentSeverity {
    Low
    Medium
    High
    Critical
}

type Incident {
    id: string
    title: string
    description: string
    severity: IncidentSeverity
    assignee: User
    reportedBy: User
}

workflow IncidentFlow {
    initial Reported
    
    Reported -> Triaged
    Triaged -> Assigned
    Assigned -> InProgress
    InProgress -> Resolved
    Resolved -> Verified
    Verified -> Closed
    
    // Can reopen
    Closed -> Reopened
    Reopened -> Assigned
    
    // Can escalate
    Assigned -> Escalated
    Escalated -> Assigned
}
✨ Edit in Studio

Validation

Spectral validates:

  • ✓ Initial state exists in the workflow
  • ✓ All referenced states are defined
  • ✓ No duplicate transitions
  • ✓ Workflow has at least one state
  • ✓ State names are valid identifiers

See Also