flowmarkup:
  title: Circuit Breaker Patterns
  version: 1
  description: Demonstrates circuit breaker patterns for fault-tolerant service invocations.

  requires:
    EXEC: [curl]
    SERVICES: [payment, claude]
    REQUEST: ["api.example.com"]

  input:
    order:
      $kind: MAP
      _notes_: Order object with id and total fields
    analysis_prompt:
      $kind: TEXT
      _notes_: Prompt text for AI analysis
    cached_result:
      $kind: ANY
      $nullable: true
      _notes_: Cached fallback result for AI analysis
    endpoints:
      $kind: ARRAY
      _notes_: List of endpoint objects with name and url fields
    health_check_url:
      $kind: URL
      _notes_: URL for the health check endpoint

  vars:
    result:
    fallback_used: false

  do:
    # Step-level circuit breaker with retry and GLOBAL scope
  - call:
      _id_: payment_call
      _label_: Process Payment
      service: =SERVICES.payment
      operation: charge
      circuitBreaker:
        name: com.example.orders.payment_service
        threshold: 5
        window: 1m
        resetTimeout: 30s
        halfOpenAttempts: 2
        scope: GLOBAL
        nonCountable: [ValidationError, AuthenticationError]
      retry:
        maxAttempts: 3
        delay: 2s
        backoff: EXPONENTIAL
        nonRetryable: [ValidationError, AuthenticationError, CircuitOpenError]
      timeout: 10s
      params:
        order_id: =order.id
        amount: =order.total
      result:
        payment_tx_id: =RESULT.tx_id

    # catch: inline handler — avoids try/catch wrapper for simple fallback
  - call:
      _label_: AI Analysis
      service: =SERVICES.claude
      operation: generate
      circuitBreaker:
        name: com.example.orders.ai_service
        threshold: 3
        window: 30s
        resetTimeout: 1m
        scope: GLOBAL
      retry: 2/1s/EXPONENTIAL
      params:
        prompt: =analysis_prompt
      result:
        ai_result: =RESULT.text
      catch:
        CircuitOpenError:
        - log: AI circuit OPEN — using cached fallback
        - set: {result: =cached_result, fallback_used: true}
        TimeoutError:
        - log: AI timed out — using cached fallback
        - set: {result: =cached_result, fallback_used: true}
        default:
        - logError: "AI failed: {{ERROR.MESSAGE}}"
        - set: {result: =cached_result, fallback_used: true}

    # String shorthand — threshold/name (uses defaults for window, resetTimeout, etc.)
  - call:
      _label_: Check Inventory
      service: =SERVICES.payment
      operation: check_stock
      circuitBreaker: "5/com.example.orders.inventory"
      retry: 3/1s/EXPONENTIAL
      timeout: 5s
      params:
        order_id: =order.id
      result:
        in_stock: =RESULT.available

    # Integer shorthand — threshold only, name derived from _id_
  - call:
      _id_: shipping_quote
      _label_: Get Shipping Quote
      service: =SERVICES.payment
      operation: quote
      circuitBreaker: 3
      timeout: 5s
      params:
        order_id: =order.id
      result:
        quote: =RESULT.price

    # Dynamic name — per-endpoint circuit breakers; dynamic window/resetTimeout from global config
  - forEach:
      items: =endpoints
      as: endpoint
      maxItems: 1000
      concurrent: true
      do:
      - request:
          _label_: Fetch endpoint data
          method: GET
          url: =endpoint.url
          circuitBreaker:
            name: "='endpoint:' + endpoint.name"
            threshold: 3
            window: =GLOBAL.cb_window              # dynamic — driven from config
            resetTimeout: =GLOBAL.cb_reset         # dynamic — driven from config
            scope: GLOBAL
          timeout: 5s
          result:
            endpoint_result: =RESULT.body

    # Circuit breaker on exec
  - exec:
      _label_: Health check
      command: curl
      args: [--fail, --silent, =health_check_url]
      circuitBreaker:
        name: com.example.infra.health_check
        threshold: 10
        window: 5m
        resetTimeout: 2m
        scope: GLOBAL
        errors: [ExecError, TimeoutError]
      timeout: 3s
      result:
        health_status: =RESULT.stdout
