Skip to content

Wave-based task scheduler for dependency-aware parallel execution #56

@chanakyav

Description

@chanakyav

Parent Epic

Part of #52 — Parallel stacked workstreams

Summary

Implement a wave-based task scheduler that runs independent tasks in parallel and waits for dependencies to complete before starting dependent tasks. Respects a configurable concurrency limit.

Context

When an epic has N sub-issues, some can run in parallel (independent) while others must wait (dependent on a previous task's completion). The scheduler determines which tasks to start and when.

Example with 4 sub-issues where #2 depends on #1, and #3/#4 are independent:

Wave 1: #1, #3, #4   (3 worktrees, 3 parallel agents)
Wave 2: #2            (starts after #1 reaches COMPLETE)

Changes

New: scheduler.py (or in orchestrator.py)

  • EpicScheduler class that takes an epic_id and manages task lifecycle
  • get_ready_tasks() — returns tasks whose dependencies are all COMPLETE
  • get_running_tasks() — returns tasks currently in non-terminal, non-INIT states
  • run() — main loop:
    1. Get ready tasks (dependencies satisfied)
    2. Filter to max_parallel_tasks concurrency limit
    3. Launch ready tasks (create worktree, start tmux session)
    4. Poll task states periodically
    5. When a task completes, check if new tasks become ready
    6. When all tasks complete or any task fails (configurable), stop

config.py

  • Add max_parallel_tasks (int, default 3) to DEFAULTS
  • Add validation: must be >= 1

persistence.py

  • Use get_epic_tasks() and get_dependent_tasks() from the schema sub-issue

Depends On

Design Decisions

  • Fail-fast vs. continue: When one task fails, should sibling tasks continue? Default: continue (independent tasks aren't affected by a sibling's failure). Dependent tasks pause.
  • Concurrency: max_parallel_tasks caps how many agents run simultaneously. Respects system resources.
  • Polling interval: Scheduler checks task states every 5 seconds (fast enough to detect completions, low overhead).

Testing

  • 3 independent tasks → all start in wave 1
  • Task with dependency → waits until parent is COMPLETE
  • Concurrency limit respected (max 2 → only 2 tasks run at once)
  • Parent task fails → dependent task stays in INIT, independent tasks continue
  • All tasks complete → scheduler exits cleanly

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions