diff --git a/src/test/e2e/architect-workflow.test.ts b/src/test/e2e/architect-workflow.test.ts index 6c59f85..2c01596 100644 --- a/src/test/e2e/architect-workflow.test.ts +++ b/src/test/e2e/architect-workflow.test.ts @@ -149,6 +149,93 @@ describe('Architect Workflow E2E', () => { }); }); + describe('breakdown conflict detection', () => { + it('should reject if a breakdown agent is already running', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Auth System'); + + // Set up a long-running breakdown agent (never completes during this test) + harness.setArchitectBreakdownComplete('first-breakdown', [ + { number: 1, name: 'Phase 1', description: 'First', dependencies: [] }, + ]); + // Use a delay so it stays running + harness.setAgentScenario('first-breakdown', { status: 'done', delay: 999999 }); + + await harness.caller.spawnArchitectBreakdown({ + name: 'first-breakdown', + initiativeId: initiative.id, + }); + + // Agent should be running + const agents = await harness.caller.listAgents(); + expect(agents.find(a => a.name === 'first-breakdown')?.status).toBe('running'); + + // Second breakdown should be rejected + await expect( + harness.caller.spawnArchitectBreakdown({ + name: 'second-breakdown', + initiativeId: initiative.id, + }), + ).rejects.toThrow(/already running/); + }); + + it('should auto-dismiss stale breakdown agents before checking', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Auth System'); + + // Set up a breakdown agent that crashes immediately + harness.setAgentScenario('stale-breakdown', { status: 'error', error: 'crashed' }); + + await harness.caller.spawnArchitectBreakdown({ + name: 'stale-breakdown', + initiativeId: initiative.id, + }); + await harness.advanceTimers(); + + // Should be crashed + const agents = await harness.caller.listAgents(); + expect(agents.find(a => a.name === 'stale-breakdown')?.status).toBe('crashed'); + + // New breakdown should succeed (stale one gets auto-dismissed) + harness.setArchitectBreakdownComplete('new-breakdown', [ + { number: 1, name: 'Phase 1', description: 'First', dependencies: [] }, + ]); + + const agent = await harness.caller.spawnArchitectBreakdown({ + name: 'new-breakdown', + initiativeId: initiative.id, + }); + expect(agent.mode).toBe('breakdown'); + }); + + it('should allow breakdown for different initiatives', async () => { + vi.useFakeTimers(); + + const init1 = await harness.createInitiative('Initiative 1'); + const init2 = await harness.createInitiative('Initiative 2'); + + // Long-running agent on initiative 1 + harness.setAgentScenario('breakdown-1', { status: 'done', delay: 999999 }); + await harness.caller.spawnArchitectBreakdown({ + name: 'breakdown-1', + initiativeId: init1.id, + }); + + // Breakdown on initiative 2 should succeed + harness.setArchitectBreakdownComplete('breakdown-2', [ + { number: 1, name: 'Phase 1', description: 'First', dependencies: [] }, + ]); + + const agent = await harness.caller.spawnArchitectBreakdown({ + name: 'breakdown-2', + initiativeId: init2.id, + }); + expect(agent.mode).toBe('breakdown'); + }); + }); + describe('full workflow', () => { it('should complete discuss -> breakdown -> phases workflow', async () => { vi.useFakeTimers();