Skip to Content
ArchitectureProcess Orchestration

Process Orchestration Architecture

How Cascade Platform orchestrates workflows from CDL definition to execution completion.


The CSL Interpretation Pipeline

When you submit a CDL application, it undergoes a transformation pipeline:

┌─────────────────────────────────────────────────────────┐ │ 1. CDL Application (YAML) │ │ ├── workflows: [name, start, states] │ │ ├── decisions: [policies] │ │ ├── agents: [definitions] │ │ └── ui: [form schemas] │ └──────────────┬──────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 2. CSL Interpreter │ │ ├── Parse YAML syntax │ │ ├── Validate schema │ │ ├── Resolve URN references │ │ ├── Type-check states & transitions │ │ └── Generate executable form │ └──────────────┬──────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 3. Validation Report │ │ ├── Success: Ready for deployment │ │ └── Error: Show specific issues │ └──────────────┬──────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 4. Temporal Workflow Creation │ │ ├── Create Temporal workflow definition │ │ ├── Register activities │ │ └── Store in Temporal server │ └──────────────┬──────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────┐ │ 5. Ready for Instance Execution │ │ → Process instances can now be started │ └─────────────────────────────────────────────────────────┘

Temporal Workflow Execution Model

Cascade uses Temporal for durable workflow execution. Here’s how it works:

Temporal Guarantees

  1. Durability - Workflow state is persisted to database
  2. Determinism - Code is not re-executed on retry (replay)
  3. At-Least-Once - Activities retry until success
  4. Event Sourcing - Complete audit trail of all events
  5. Visibility - Query workflow state anytime

Execution Flow

User submits instance Temporal Workflow starts Loop: Execute current state ├─ Read state definition ├─ Execute state type logic │ ├─ Task: Call activity │ ├─ Choice: Evaluate conditions │ ├─ HumanTask: Wait for user │ ├─ Parallel: Fork branches │ ├─ Wait: Sleep │ └─ Receive: Wait for event ├─ Save state output └─ Determine next state (repeat until end or error) Workflow completes / fails Result persisted to PostgreSQL

State Types & Execution Characteristics

Task State

- name: ProcessPayment type: Task resource: urn:cascade:activity:process_payment parameters: amount: $.input.amount card: $.input.payment_method resultPath: $.payment_result next: ConfirmOrder

What it does:

  • Calls a Temporal activity (your Go code)
  • Activity receives parameters, does work, returns result
  • Result stored in workflow state
  • Proceeds to next state

Execution:

Task State Call Activity: process_payment(amount=100, card=visa) Activity executes (may take seconds/minutes) Return result: {transactionId: "tx-123", status: "approved"} Save to $.payment_result Move to next state

Duration: Seconds to minutes (depends on activity)

Choice State

- name: DecideApprovalPath type: Choice choices: - variable: $.input.amount numericGreaterThan: 10000 next: ExecutiveReview - variable: $.context.department stringEquals: "Finance" next: FinanceApproval - default: AutoApprove

What it does:

  • Evaluates conditions in-process (no activity call)
  • Routes to different states based on conditions
  • Returns immediately

Execution:

Choice State (in-process) Check: $.input.amount > 10000? ├─ TRUE → Route to ExecutiveReview └─ FALSE → Check next condition

Duration: <0.1ms (in-process logic)

HumanTask State

- name: ManagerApproval type: HumanTask description: "Manager reviews and approves" ui: schema: urn:cascade:schema:approval_form target: appsmith next: EvaluateApproval

What it does:

  • Generates form from schema
  • Renders in UI framework (Appsmith, RJSF, etc.)
  • Waits for user interaction
  • Resumes when form is submitted

Execution:

HumanTask State Generate form from schema Render in Appsmith User opens form in browser [Waiting... could be hours/days] User fills form + submits Form data captured Resume workflow

Duration: Hours/days (depends on user response)

Parallel State

- name: SendNotifications type: Parallel branches: - name: SendToManager type: Task resource: urn:cascade:activity:email parameters: recipient: $.context.manager_email - name: SendToEmployee type: Task resource: urn:cascade:activity:email parameters: recipient: $.context.employee_email - name: UpdateCalendar type: Task resource: urn:cascade:activity:calendar_update next: Complete

What it does:

  • Executes multiple branches simultaneously
  • Waits for all to complete
  • Then proceeds to next state

Execution:

Parallel State ├─ Branch 1: SendToManager (2s) ┐ ├─ Branch 2: SendToEmployee (2s) ├─ All wait for slowest └─ Branch 3: UpdateCalendar (1s) ┘ ↓ (max duration) All completed after ~2 seconds Proceed to next state

Duration: Maximum of all branches

Wait State

- name: WaitBeforeRetry type: Wait durationInSeconds: 3600 # 1 hour next: RetryPayment

What it does:

  • Pauses workflow for specified duration
  • Resumes after duration expires
  • Precise timing (even across crashes)

Duration: Exactly as specified

Receive State

- name: WaitForWebhook type: Receive eventType: "payment.confirmed" next: ProcessConfirmation

What it does:

  • Waits for external event
  • Pauses indefinitely
  • Resumes when event arrives (via webhook/API)

Duration: Indefinite (until event)

EvaluatePolicy State

- name: CheckCompliance type: EvaluatePolicy policy: urn:cascade:policy:leave_compliance engine: opa parameters: days_requested: $.state.days department: $.context.department resultPath: $.policy_decision next: DecideRoute

What it does:

  • Calls policy evaluation engine
  • Engine runs business rules
  • Returns decision
  • Stores result in workflow state

Execution:

EvaluatePolicy State Call policy: leave_compliance Input: {days_requested: 5, department: "Engineering"} OPA evaluates Rego policy Return: {allowed: true, reason: "within_limit"} Store to $.policy_decision Next state

Duration: <5ms (OPA) to 50ms (DMN)


Temporal Guarantees in Action

Durability: Survive Crashes

Scenario: Workflow crashes mid-execution

Execute State 1 → (✓ saved) Execute State 2 → (crash!) Temporal replays from last saved state Execute State 2 → (✓ saved, retried) Continue normally

Determinism: No Re-execution

// Activity is called only once func ProcessPayment(ctx context.Context, amount int) (TransactionID, error) { // This code runs once txID := chargeCard(amount) // Charges once, not twice return txID, nil } // Even if activity code changes, determinism preserved // Temporal replays with original logic for past states

At-Least-Once Semantics

Activity fails Temporal retries after 1 second Activity fails again Temporal retries after 2 seconds Activity fails again Temporal retries after 4 seconds Activity succeeds ✓

Data Flow: State → Context → Result

Workflow State Structure

{ "id": "wf-123", "workflow": "leave_request_flow", "input": { "employee_id": "emp-456", "leave_type": "vacation" }, "state": { "employee_name": "Alice", "start_date": "2025-11-15", "end_date": "2025-11-20", "days_requested": 5, "manager_approved": true, "approval_result": { "approved": true, "approved_by": "bob@company.com" } }, "context": { "employee_name": "Alice", "department": "Engineering", "vacation_balance": 15, "manager_email": "bob@company.com" }, "result": { "status": "approved", "approved_timestamp": "2025-10-29T10:30:00Z" } }

JSONPath References

In your workflow YAML:

  • $.input.leave_type - Original input (immutable)
  • $.state.days_requested - Mutable state (changes as workflow executes)
  • $.context.vacation_balance - Environment data (immutable)
  • $.result.status - Final output

Orchestration Example: Leave Request Workflow

┌────────────────────────────────────────────────────────────┐ │ Instance Created │ │ input: {employee_id: emp-123} │ └──────────────┬─────────────────────────────────────────────┘ ┌──────────────┐ │ RequestForm │ HumanTask: Wait for form submission └──────┬───────┘ (user fills and submits) ┌───────────────────┐ │ CheckVacation │ Choice: Is balance sufficient? │ Balance │ ($state.days_requested ≤ $context.vacation_balance) └────────┬──────────┘ ┌──────┴──────┐ │ (True) │ (False) ▼ ▼ ┌─────────┐ ┌──────────┐ │ Evaluate│ │Insufficient │ Policy │ │Balance └────┬────┘ │(Email + End) │ └──────────┘ ┌──────────────┐ │ DecideRoute │ Choice: Based on policy decision │ │ (auto, manager, or executive) └────┬─────────┘ ┌────┴────┬────────┬─────────┐ ▼ ▼ ▼ ▼ ┌────────┐ ┌───────┐ ┌────────┐ ┌────┐ │Auto │ │Manager│ │Exec │ │... │ │Approve │ │Review │ │Approval│ └────┘ └────┬───┘ └───┬───┘ └────┬───┘ │ │ │ └────┬───┴──────┬───┘ │ │ ▼ ▼ ┌──────────────────────────┐ │ SendNotifications │ Parallel: Send emails │ (3 concurrent tasks) │ + Update calendar └──────────┬───────────────┘ ┌───────────────┐ │ Complete │ End state │ workflow │ └───────────────┘

Performance Characteristics

OperationLatencyNotes
Task (Activity)Seconds-minutesDepends on activity duration
Choice (in-process)<0.1msEvaluated immediately
HumanTaskHours-daysDepends on user
ParallelMax(branch times)Not sum
WaitPrecise durationSurvives crashes
ReceiveIndefiniteUntil event arrives
EvaluatePolicy<5ms (OPA), 10-50ms (DMN)Depends on engine
State transition<1msTemporal overhead

Error Handling & Retries

Activity Retry Logic

- name: ProcessPayment type: Task resource: urn:cascade:activity:process_payment retry: maxAttempts: 3 backoffMultiplier: 2 # 1s, 2s, 4s initialInterval: 1s catch: - resultPath: $.error next: PaymentFailed next: PaymentConfirmed

Error State

If activity fails after retries:

Activity fails Retry 1 (after 1s) → fails Retry 2 (after 2s) → fails Retry 3 (after 4s) → fails All retries exhausted Goto catch state (PaymentFailed)

Summary

ConceptExecutionDurationUse For
TaskActivity callSec-minBusiness logic, API calls
ChoiceIn-process<0.1msRouting, branching
HumanTaskWait for UIHours-daysForms, approvals
ParallelConcurrentMax(branches)Notifications, batch ops
WaitSleepPreciseDelays, retries
ReceiveEvent waitIndefiniteExternal triggers
PolicyRule engine<5-50msBusiness rules

Next Steps

Ready to understand security? → Security Model

Last updated on