Waves

Waves are groups of streams that execute in parallel. orchex automatically computes waves from the dependency graph using topological sort — you define dependencies, orchex figures out the optimal execution order.

How Waves Work

orchex analyzes the dependency graph and assigns each stream to a wave:

  1. Wave 1 — All streams with no dependencies (they can run simultaneously)
  2. Wave 2 — Streams whose dependencies all completed in Wave 1
  3. Wave N — Streams whose dependencies all completed in previous waves

Each wave must complete (all streams pass verification) before the next wave starts.

Example

Streams and their dependencies:

  A (no deps)     ─┐
  B (no deps)     ─┤── Wave 1 (3 streams in parallel)
  C (no deps)     ─┘
  D (deps: [A])   ─┐
  E (deps: [B,C]) ─┘── Wave 2 (2 streams in parallel)
  F (deps: [D,E]) ──── Wave 3 (1 stream)

orchex executes A, B, and C simultaneously in Wave 1. When all three finish and pass verification, D and E run in parallel in Wave 2. Finally, F runs alone in Wave 3.

Without Waves (Serial)

[A] → [B] → [C] → [D] → [E] → [F]   (6 sequential steps)

With Waves (Parallel)

Wave 1: [A] [B] [C]   (parallel)
Wave 2:    [D] [E]     (parallel)
Wave 3:       [F]

6 streams in 3 waves instead of 6 sequential steps — up to 2x faster in this example. With more independent streams, the speedup grows.

Wave Properties

  • Wave number — Sequential index starting at 1
  • Parallelism — All streams in a wave execute simultaneously
  • No intra-wave deps — No stream depends on another stream in the same wave
  • Blocking — A wave must complete before the next starts
  • Verification — Each stream's verify commands run at the end of its execution, before the wave is considered complete

Execution Modes

orchex supports three execution modes:

Mode Behavior Use Case
wave Execute one wave and return Step through waves manually, inspect results between waves
auto Execute all waves sequentially until done Hands-off execution
dry_run Show wave plan without executing Preview the execution plan
// Step through one wave at a time
orchex.execute({ mode: "wave" });

// Run everything
orchex.execute({ mode: "auto" });

// Just see the plan
orchex.execute({ dry_run: true });

Why Waves Matter

Speed

Parallel execution is orchex's primary value. A 10-stream feature with 3 waves completes in the time of 3 sequential tasks, not 10.

Safety Boundaries

Each wave boundary is a checkpoint. If a stream in Wave 2 fails, Wave 1's work is already committed. Self-healing only retries the failed stream, not the entire orchestration.

Context Propagation

Streams in later waves can reference the output of earlier waves. When Wave 1's auth-types stream creates src/types/auth.ts, Wave 2's auth-middleware stream sees the actual file contents in its context — not a plan, but the real code.

Rate Limit Distribution

If you configure multiple LLM providers, orchex distributes parallel streams across providers. One provider rate-limiting doesn't block streams running on other providers.

Dependency Optimization

Minimize Dependency Chains

Long dependency chains create sequential bottlenecks. Only add dependencies when a stream truly needs another stream's output.

# Bad: Artificial linear chain (4 waves)
streams:
  task-1: { deps: [] }
  task-2: { deps: ["task-1"] }   # Does task-2 really need task-1?
  task-3: { deps: ["task-2"] }
  task-4: { deps: ["task-3"] }

# Good: Independent tasks (1 wave)
streams:
  task-1: { deps: [] }
  task-2: { deps: [] }
  task-3: { deps: [] }
  task-4: { deps: [] }

Declare Only True Dependencies

A dependency exists only if Stream B requires Stream A's output or side effects.

# Bad: False dependency
update-readme: { deps: [] }
add-tests: { deps: ["update-readme"] }   # Tests don't need the README

# Good: Independent tasks
update-readme: { deps: [] }
add-tests: { deps: [] }

Avoid Redundant Dependencies

If A depends on B, and B depends on C, then A implicitly depends on C. Don't list C in A's deps.

# Bad: Redundant
base: { deps: [] }
middle: { deps: ["base"] }
top: { deps: ["base", "middle"] }   # "base" is redundant

# Good: Minimal
base: { deps: [] }
middle: { deps: ["base"] }
top: { deps: ["middle"] }   # Implies base

Common Patterns

Fork-Join

Setup → parallel features → integration:

      [setup]
      ↓  ↓  ↓
   [A] [B] [C]     ← Wave 2 (parallel)
      ↓  ↓  ↓
   [integration]    ← Wave 3

Diamond

Multiple paths converge on one output:

   [database]
     ↓    ↓
  [users] [products]    ← Wave 2 (parallel)
     ↓    ↓
   [checkout]           ← Wave 3

Independent Pipelines

Two unrelated feature pipelines run in parallel:

  [auth-types]    [billing-types]     ← Wave 1 (parallel)
       ↓                ↓
  [auth-api]      [billing-api]       ← Wave 2 (parallel)
       ↓                ↓
  [auth-tests]    [billing-tests]     ← Wave 3 (parallel)

Anti-Patterns

The Monolith Stream

One giant stream that should be split into waves:

# Bad: One stream does everything
do-everything:
  owns: ["src/types.ts", "src/api.ts", "src/routes.ts", "tests/api.test.ts"]
  plan: "Create types, implement API, add routes, write tests"

Split into streams with proper dependencies for parallelism and manageable scope.

Premature Convergence

Forcing parallel work to synchronize unnecessarily:

# Bad: Unnecessary checkpoint
feature-a-step-1: { deps: [] }
feature-b-step-1: { deps: [] }
checkpoint: { deps: ["feature-a-step-1", "feature-b-step-1"] }   # Why?
feature-a-step-2: { deps: ["checkpoint"] }
feature-b-step-2: { deps: ["checkpoint"] }

If A and B are independent pipelines, let them complete independently.

Tier Limits

Wave count is subject to tier limits:

Tier Max Waves
Local (Free) 2
Pro 10
Team 25
Enterprise Unlimited

orchex warns you when your wave count approaches the tier limit (at 80% or above).