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
- Durability - Workflow state is persisted to database
- Determinism - Code is not re-executed on retry (replay)
- At-Least-Once - Activities retry until success
- Event Sourcing - Complete audit trail of all events
- 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 PostgreSQLState 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: ConfirmOrderWhat 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 stateDuration: 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: AutoApproveWhat 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 conditionDuration: <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: EvaluateApprovalWhat 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 workflowDuration: 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: CompleteWhat 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 stateDuration: Maximum of all branches
Wait State
- name: WaitBeforeRetry
type: Wait
durationInSeconds: 3600 # 1 hour
next: RetryPaymentWhat 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: ProcessConfirmationWhat 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: DecideRouteWhat 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 stateDuration: <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 normallyDeterminism: 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 statesAt-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
| Operation | Latency | Notes |
|---|---|---|
| Task (Activity) | Seconds-minutes | Depends on activity duration |
| Choice (in-process) | <0.1ms | Evaluated immediately |
| HumanTask | Hours-days | Depends on user |
| Parallel | Max(branch times) | Not sum |
| Wait | Precise duration | Survives crashes |
| Receive | Indefinite | Until event arrives |
| EvaluatePolicy | <5ms (OPA), 10-50ms (DMN) | Depends on engine |
| State transition | <1ms | Temporal 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: PaymentConfirmedError 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
| Concept | Execution | Duration | Use For |
|---|---|---|---|
| Task | Activity call | Sec-min | Business logic, API calls |
| Choice | In-process | <0.1ms | Routing, branching |
| HumanTask | Wait for UI | Hours-days | Forms, approvals |
| Parallel | Concurrent | Max(branches) | Notifications, batch ops |
| Wait | Sleep | Precise | Delays, retries |
| Receive | Event wait | Indefinite | External triggers |
| Policy | Rule engine | <5-50ms | Business rules |
Next Steps
- Security Model - Defense in depth
- Core Concepts - Deep dive into CDL
- Workflow Development - Build real workflows
Ready to understand security? → Security Model