Skip to Content
Why Cascade?Two Primitives

The Two Primitives: Deep Dive

You learned that TASK and WAITFORINPUT are the two building blocks for 90% of workflows. This page explores them in depth—patterns, edge cases, and when they work best.


The Complete Picture

TASK: Execute Work Immediately

Definition: A state that executes code and returns when done.

- name: ValidateOrder type: Task resource: "urn:cascade:action:validate_order" parameters: order_id: "$.input.order_id" resultPath: "$.state.validation_result" next: CheckInventory

Execution Model:

TASK starts Call your activity (Go function) Activity executes (can take milliseconds to seconds) TASK receives result Result stored in state Next state begins

Characteristics:

  • ⚡ Fast (milliseconds to seconds)
  • 🔄 Idempotent (safe to retry)
  • 🎯 Single purpose (one activity per TASK)
  • 📊 Produces output (state changes)
  • 🛡️ Error handling (try/catch/retry)

What TASK Can Do: ✅ Call APIs (Stripe, Twilio, AWS) ✅ Query databases ✅ Evaluate policies (OPA, DMN) ✅ Run AI agents ✅ Send notifications ✅ Transform data ✅ Execute calculations ✅ Call webhooks

What TASK Cannot Do: ❌ Wait for humans ❌ Wait for external events ❌ Time-based delays (use Wait state) ❌ Have unknown duration


WAITFORINPUT: Pause and Resume

Definition: A state that pauses the workflow until an external signal arrives.

- name: ManagerApproval type: Task resource: "urn:cascade:waitFor:human" input_schema: approval_form resultPath: "$.state.approval" next: ProcessOrder

Execution Model:

WAITFORINPUT starts Workflow PAUSES (saved to database) Human/system provides input (minutes to months later) Workflow RESUMES from exact point Result stored in state Next state begins

Characteristics:

  • ⏸️ Pauseable (saves state to database)
  • 🔔 External trigger (human, webhook, event)
  • ⏱️ Unknown duration (could be months)
  • 🎯 Single purpose (one input type per state)
  • 🛡️ Resumable (survives deployments, crashes)

What WAITFORINPUT Can Do: ✅ Wait for human form submission ✅ Wait for webhook callback (payment, shipping) ✅ Wait for external event (system notification) ✅ Wait for manual override (admin action) ✅ Collect structured data via forms ✅ Route based on user decision

What WAITFORINPUT Cannot Do: ❌ Execute code immediately ❌ Call APIs directly (TASK does this) ❌ Return instantly ❌ Have deterministic duration


WAITFORINPUT: The Four Input Types

All WAITFORINPUT states use the same state type but differ in how the trigger arrives:

1️⃣ Human Input (Form Submission)

Scenario: Employee submits leave request form

- name: SubmitLeaveRequest type: Task resource: "urn:cascade:waitFor:human" input_schema: leave_request_form assignment: assigned_to: "$.context.employee_id" next: ManagerReview

Flow:

Workflow pauses System generates form from schema Employee fills and submits form Data saved to workflow state Next state begins

Use Cases:

  • Leave requests
  • Expense approvals
  • Performance reviews
  • Customer feedback

2️⃣ Webhook Callback (External System)

Scenario: Wait for payment provider to confirm charge

- name: WaitForPaymentConfirmation type: Task resource: "urn:cascade:waitFor:webhook" webhook: path: "/webhooks/stripe-charge-confirmed" method: POST next: ShipOrder

Flow:

Workflow pauses System registers webhook listener External system calls webhook (Stripe, Shopify, etc.) Webhook payload saved to state Next state begins

Use Cases:

  • Payment confirmations
  • Shipping notifications
  • Email delivery confirmations
  • Third-party API callbacks

3️⃣ Event Arrival (Internal/External Events)

Scenario: Wait for order shipped event from logistics system

- name: WaitForShipment type: Task resource: "urn:cascade:waitFor:event" events: - name: "order.shipped" - name: "order.failed" next: RetryShipment timeout: 7d timeoutNext: CheckStatusManually

Flow:

Workflow pauses System subscribes to NATS events Event published (shipment tracking updated) Event payload saved to state Next state begins

Use Cases:

  • Event-driven workflows
  • Real-time notifications
  • System integrations
  • Multi-service orchestration

4️⃣ Manual Override (Admin Action)

Scenario: Admin manually approves/rejects pending item

- name: WaitForAdminOverride type: Task resource: "urn:cascade:waitFor:manual" form: title: "Manual Review Required" fields: - decision: "string" - notes: "string" next: ProcessDecision

Flow:

Workflow pauses Admin sees pending item in queue Admin submits form (approve/reject + notes) Decision saved to state Next state begins

Use Cases:

  • Escalations
  • Manual interventions
  • Edge case handling
  • Exception management

Real-World Patterns: How They Combine

Pattern 1: Simple Automation (3 TASKs)

order_processing: start: ValidateOrder states: - name: ValidateOrder type: Task resource: "urn:cascade:action:validate" next: ChargePayment - name: ChargePayment type: Task resource: "urn:cascade:action:stripe.charge" retry: { max_attempts: 3, backoff_rate: 2.0 } catch: - error: PaymentFailed next: NotifyCustomer next: ShipOrder - name: ShipOrder type: Task resource: "urn:cascade:action:ship" end: true

Characteristics:

  • ⚡ Fast (completes in seconds)
  • 🤖 Fully automated
  • 📈 Predictable flow
  • ✅ Best for: E-commerce, event processing

Pattern 2: Human-in-Loop (TASK → WAITFORINPUT → TASK)

leave_request: start: ValidateRequest states: - name: ValidateRequest type: Task resource: "urn:cascade:action:validate" next: ManagerApproval - name: ManagerApproval type: Task resource: "urn:cascade:waitFor:human" input_schema: approval_form next: NotifyEmployee - name: NotifyEmployee type: Task resource: "urn:cascade:action:send_email" end: true

Characteristics:

  • ⏸️ Pauses for human decision
  • 🔄 Resumes when input arrives
  • 📅 Could take hours/days
  • ✅ Best for: Approvals, reviews

Pattern 3: Multi-Path Decision (TASK → Choice → Multiple Paths)

fraud_detection: start: AnalyzeRisk states: - name: AnalyzeRisk type: Task resource: "urn:cascade:agent:fraud-scorer" next: RouteByRisk - name: RouteByRisk type: Choice choices: - condition: "$.state.risk_score > 0.9" next: BlockTransaction - condition: "$.state.risk_score > 0.7" next: RequireVerification default: AutoApprove - name: BlockTransaction type: Task resource: "urn:cascade:action:block_and_notify" end: true - name: RequireVerification type: Task resource: "urn:cascade:waitFor:human" input_schema: verification_form next: ProcessAfterVerification - name: AutoApprove type: Task resource: "urn:cascade:action:approve" end: true

Characteristics:

  • 🧠 Intelligent routing
  • 🔀 Multiple branches
  • 🔄 Mix of TASK and WAITFORINPUT
  • ✅ Best for: Risk assessment, prioritization

Pattern 4: Parallel Processing (Multiple TASKs Simultaneously)

notification_system: start: PrepareNotification states: - name: PrepareNotification type: Task resource: "urn:cascade:action:prepare" next: SendNotifications - name: SendNotifications type: Parallel branches: - start: SendEmail states: - name: SendEmail type: Task resource: "urn:cascade:action:send_email" end: true - start: SendSMS states: - name: SendSMS type: Task resource: "urn:cascade:action:send_sms" end: true - start: SendSlack states: - name: SendSlack type: Task resource: "urn:cascade:action:send_slack" end: true next: LogCompletion - name: LogCompletion type: Task resource: "urn:cascade:action:log" end: true

Characteristics:

  • 🚀 Execute multiple TASKs at once
  • ⏱️ Wait for all to complete
  • 📊 Aggregate results
  • ✅ Best for: Notifications, batch operations

Edge Cases & Advanced Scenarios

Edge Case 1: Very Long WAITFORINPUT

Scenario: Waiting for physical delivery (could be weeks)

- name: WaitForDelivery type: Task resource: "urn:cascade:waitFor:event" events: - name: "delivery.completed" timeout: 30d timeoutNext: CheckDeliveryStatus

Considerations:

  • ✅ Workflow survives deployments
  • ✅ State persisted to database
  • ✅ Configurable timeout
  • ✅ Can escalate if timeout exceeded

Edge Case 2: TASK with Variable Duration

Scenario: Calling an external API that could take 30+ seconds

- name: GenerateReport type: Task resource: "urn:cascade:action:generate_report" parameters: dataset_size: "$.state.size" timeout: 5m # Set timeout for long-running tasks catch: - error: TimeoutError next: RequestAsync

Considerations:

  • ✅ Set timeout to prevent infinite waits
  • ✅ Catch timeout errors
  • ✅ Escalate to async if needed
  • ✅ Use WAITFORINPUT for callback

Edge Case 3: Nested Decision Making

Scenario: Complex multi-tier approval

approval_chain: start: EvaluateAmount states: - name: EvaluateAmount type: Task resource: "urn:cascade:policy:approval_tier" next: RouteToApprover - name: RouteToApprover type: Choice choices: - condition: "$.state.tier == 'manager'" next: ManagerApproval - condition: "$.state.tier == 'director'" next: DirectorApproval - condition: "$.state.tier == 'executive'" next: ExecutiveApproval - name: ManagerApproval type: Task resource: "urn:cascade:waitFor:human" assignment: { assigned_to: "$.state.manager_id" } next: FinalApproval - name: DirectorApproval type: Task resource: "urn:cascade:waitFor:human" assignment: { assigned_to: "$.state.director_id" } next: FinalApproval - name: ExecutiveApproval type: Task resource: "urn:cascade:waitFor:human" assignment: { assigned_to: "$.state.ceo_id" } next: FinalApproval - name: FinalApproval type: Task resource: "urn:cascade:action:process_approval" end: true

Considerations:

  • ✅ Route based on data
  • ✅ Assign to right person
  • ✅ Converge back to single path
  • ✅ Clear separation of concerns

Decision: When to Use Each

Use TASK When:

  • ✅ Work is deterministic (takes known time)
  • ✅ Calling an external API
  • ✅ Processing data
  • ✅ Making instant decisions
  • ✅ Result available immediately
  • Duration: Milliseconds to seconds
  • Example: “Charge card” (happens immediately or fails)

Use WAITFORINPUT When:

  • ✅ Need external signal
  • ✅ Waiting for human decision
  • ✅ Waiting for system callback
  • ✅ Waiting for event
  • ✅ Result has unknown arrival time
  • Duration: Minutes to months
  • Example: “Manager approval” (could take days)

Use Choice When:

  • ✅ Routing based on instant decision
  • ✅ Conditional branching
  • ✅ Multiple paths based on data
  • ✅ Performance-critical (<1ms needed)
  • No wait: Evaluates instantly
  • Example: “Route by amount” (<1ms decision)

Use Parallel When:

  • ✅ Multiple independent TASKs
  • ✅ Wait for all to complete
  • ✅ Aggregating results
  • Performance: Faster than sequential
  • Example: “Send 3 notification channels” (faster in parallel)

Performance Characteristics

PatternSpeedStateExample
TASKFast (< 1s)Changes immediatelyAPI call
WAITFORINPUTSlow (min to months)Paused in DBHuman approval
ChoiceInstant (< 1ms)No changeRoute by value
ParallelConcurrentAll completeParallel notifications
Sequential TASK→TASKSequential sumChanges each stepValidate → Charge → Ship

Anti-Patterns: What NOT to Do

❌ Anti-Pattern 1: Using WAITFORINPUT for API Callback

Wrong:

- name: WaitForCallback type: Task resource: "urn:cascade:waitFor:human" # Wrong!

Right:

- name: WaitForCallback type: Task resource: "urn:cascade:waitFor:webhook" # Correct!

❌ Anti-Pattern 2: Putting TASK Where WAITFORINPUT Needed

Wrong:

- name: GetApproval type: Task resource: "urn:cascade:action:request_approval" # Doesn't wait! next: ContinueProcess # Immediately goes here!

Right:

- name: GetApproval type: Task resource: "urn:cascade:waitFor:human" # Pauses until submitted next: ContinueProcess # Resumes after submission

❌ Anti-Pattern 3: Too Many Sequential TASKs

Wrong (N+1 states):

states: - name: Validate type: Task next: Charge - name: Charge type: Task next: Ship - name: Ship type: Task next: Notify - name: Notify type: Task end: true

Better (Parallel where possible):

states: - name: Validate type: Task next: ProcessPaymentAndShip - name: ProcessPaymentAndShip type: Parallel branches: - [Charge] - [Ship] next: Notify - name: Notify type: Task end: true

Summary: The Two Primitives Mastery

Master these concepts and you can build 90% of workflows:

ConceptDurationTriggerUse For
TASKSecondsAutomaticWork execution
WAITFORINPUTMinutes-monthsExternalHuman/system input
ChoiceInstantAutomaticRouting
ParallelConcurrentAutomaticParallel work

Remember:

  • TASK when you have work to do NOW
  • WAITFORINPUT when you need to pause and wait for someone/something ELSE
  • Combine them for complex workflows

Next Steps

Now that you master the two primitives:

  1. Application-as-Data - Why structured YAML enables this
  2. AI-Native Development - How LLMs use this
  3. Agent-First Platform - Agents orchestrating these patterns
  4. Comparisons - How we compare to competitors

You’re now a Two Primitives expert! 🎓 Time to see how AI leverages this. →

Last updated on