/** * B1 regression: Scheduler wire-up to WorkerManager + workspace merge * Verifies state machine includes BLOCKED/CANCELLED, DISPATCHING spawns workers, * and MERGING calls workspace_manager.merge_workspace. */ import { describe, it, expect } from 'bun:test' import { Scheduler, type SchedulerState } from '../../src/scheduler/Scheduler.js' import { createNullEventIngestor } from '../../src/events/EventIngestor.js' describe('B1: Scheduler wire-up', () => { it('SchedulerState includes BLOCKED and CANCELLED', () => { const valid_states: SchedulerState[] = [ 'IDLE', 'LOADING_GRAPH', 'PLANNING_WAVE', 'DISPATCHING', 'MONITORING', 'COLLECTING_RESULTS', 'MERGING', 'REVIEWING_WAVE', 'REPAIRING_OR_CONTINUING', 'COMPLETED', 'TERMINATED', 'BLOCKED', 'CANCELLED' ] expect(valid_states).toContain('BLOCKED') expect(valid_states).toContain('CANCELLED') }) it('starts in IDLE state', () => { const scheduler = new Scheduler({ session_id: 'test-session' as any, project_id: 'test-project' as any, project_root: '/tmp/test' }) expect(scheduler.get_state()).toBe('IDLE') }) it('transitions IDLE → LOADING_GRAPH on step', async () => { const scheduler = new Scheduler({ session_id: 'test-session' as any, project_id: 'test-project' as any, project_root: '/tmp/test' }) await scheduler.step() expect(scheduler.get_state()).toBe('LOADING_GRAPH') }) it('completes with empty graph', async () => { const scheduler = new Scheduler({ session_id: 'test-session' as any, project_id: 'test-project' as any, project_root: '/tmp/test' }) const final_state = await scheduler.run_until_idle() expect(['COMPLETED', 'TERMINATED']).toContain(final_state) }) it('works without worker_manager (fallback mode)', async () => { const scheduler = new Scheduler( { session_id: 'test-session' as any, project_id: 'test-project' as any, project_root: '/tmp/test' }, undefined, createNullEventIngestor(), ) await scheduler.create_tasks([ { id: 't1' as any, type: 'code', title: 'Task 1' }, { id: 't2' as any, type: 'code', title: 'Task 2', depends_on: ['t1' as any] } ]) expect(scheduler.get_state()).toBe('PLANNING_WAVE') }) it('run_until_idle treats BLOCKED and CANCELLED as terminal', async () => { const scheduler = new Scheduler({ session_id: 'test-session' as any, project_id: 'test-project' as any, project_root: '/tmp/test' }) const result = await scheduler.run_until_idle() expect(['COMPLETED', 'TERMINATED', 'BLOCKED', 'CANCELLED']).toContain(result) }) })