/** * WavePlanner - Plans execution waves for tasks * * Implements DD §7.3. * * @module packages/runtime/src/scheduler/WavePlanner */ import { TaskGraph, type TaskNode, type DependencyType } from './TaskGraph.js' export interface WavePlan { wave_id: number tasks: Array<{ task_id: string workspace: string model?: string agent_type: string }> can_parallelize: boolean resource_cap: number } export interface WriteArea { area: string tasks: string[] } export class WavePlanner { private resource_cap: number constructor(resource_cap: number = 4) { this.resource_cap = resource_cap } /** * Plan next execution wave from runnable tasks. */ plan(graph: TaskGraph): WavePlan[] { const runnable = graph.get_runnable_tasks() if (runnable.length === 0) return [] // Group by write areas to detect conflicts const write_areas = this.group_by_write_area(runnable) // Assign workspaces const assignments = this.assign_workspaces(write_areas, runnable) // Detect conflicts — same uncertain area → serialize const can_parallelize = this.can_parallelize(write_areas) // Cap resources const capped = assignments.slice(0, this.resource_cap) return [{ wave_id: Date.now(), tasks: capped, can_parallelize, resource_cap: this.resource_cap }] } /** * Group tasks by their write areas to detect potential conflicts. */ group_by_write_area(tasks: TaskNode[]): WriteArea[] { // Extract write areas from task metadata (stub) const areas: Map = new Map() for (const task of tasks) { const area = 'default' // Would be extracted from task spec if (!areas.has(area)) areas.set(area, []) areas.get(area)!.push(task.id) } return Array.from(areas.entries()).map(([area, tasks]) => ({ area, tasks })) } /** * Assign workspace to each task. * Different write areas → concurrent. * Same uncertain area → serialize. */ assign_workspaces(areas: WriteArea[], tasks: TaskNode[]): Array<{ task_id: string; workspace: string; agent_type: string }> { return tasks.map((task, index) => ({ task_id: task.id, workspace: `ws_${index}`, agent_type: 'executor' // Would be determined by task type })) } /** * Check if tasks can run in parallel (write areas don't conflict). */ can_parallelize(areas: WriteArea[]): boolean { // Different write areas → concurrent return areas.length > 1 } /** * Assign model for a task based on requirements. */ assign_model(task: TaskNode): string { // Would consult capability matrix return 'claude-sonnet-4-6' } }