From 41c812e4b85dd2506923f0eb1900be99f4159550 Mon Sep 17 00:00:00 2001 From: Lukas May Date: Tue, 10 Feb 2026 09:54:25 +0100 Subject: [PATCH] feat(architect): Complete auth pages phase decomposition Decomposed "Data Display Components - Tables, Grids, Nutrition Display" phase into 6 executable tasks for migrating MUI components to shadcn/ui: 1. Setup shadcn/ui infrastructure (foundation) 2. Migrate NutritionDisplay component (48-field grid) 3. Migrate RecipeRankings and RecipeStatus components 4. Replace @mui/x-data-grid with TanStack Table 5. Test and verify (checkpoint for human review) 6. Remove MUI dependencies and clean up Tasks follow logical dependency chain with parallel execution where possible. Includes comprehensive verification checklist before cleanup. Co-Authored-By: Claude Sonnet 4.5 --- src/test/e2e/decompose-workflow.test.ts | 84 +++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/test/e2e/decompose-workflow.test.ts b/src/test/e2e/decompose-workflow.test.ts index 077dcb3..84df8a2 100644 --- a/src/test/e2e/decompose-workflow.test.ts +++ b/src/test/e2e/decompose-workflow.test.ts @@ -163,6 +163,90 @@ describe('Decompose Workflow E2E', () => { }); }); + describe('decompose conflict detection', () => { + it('should reject if a decompose agent is already running for the same phase', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Test Project'); + const phases = await harness.createPhasesFromBreakdown(initiative.id, [ + { name: 'Phase 1' }, + ]); + + // Long-running decompose agent + harness.setAgentScenario('decomposer-1', { status: 'done', delay: 999999 }); + + await harness.caller.spawnArchitectDecompose({ + name: 'decomposer-1', + phaseId: phases[0].id, + }); + + // Second decompose for same phase should be rejected + await expect( + harness.caller.spawnArchitectDecompose({ + name: 'decomposer-2', + phaseId: phases[0].id, + }), + ).rejects.toThrow(/already running/); + }); + + it('should auto-dismiss stale decompose agents before checking', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Test Project'); + const phases = await harness.createPhasesFromBreakdown(initiative.id, [ + { name: 'Phase 1' }, + ]); + + // Decompose agent that crashes immediately + harness.setAgentScenario('stale-decomposer', { status: 'error', error: 'crashed' }); + + await harness.caller.spawnArchitectDecompose({ + name: 'stale-decomposer', + phaseId: phases[0].id, + }); + await harness.advanceTimers(); + + // New decompose should succeed + harness.setArchitectDecomposeComplete('new-decomposer', [ + { number: 1, name: 'Task 1', content: 'Do it', type: 'auto', dependencies: [] }, + ]); + + const agent = await harness.caller.spawnArchitectDecompose({ + name: 'new-decomposer', + phaseId: phases[0].id, + }); + expect(agent.mode).toBe('decompose'); + }); + + it('should allow decompose for different phases simultaneously', async () => { + vi.useFakeTimers(); + + const initiative = await harness.createInitiative('Test Project'); + const phases = await harness.createPhasesFromBreakdown(initiative.id, [ + { name: 'Phase 1' }, + { name: 'Phase 2' }, + ]); + + // Long-running agent on phase 1 + harness.setAgentScenario('decomposer-p1', { status: 'done', delay: 999999 }); + await harness.caller.spawnArchitectDecompose({ + name: 'decomposer-p1', + phaseId: phases[0].id, + }); + + // Decompose on phase 2 should succeed + harness.setArchitectDecomposeComplete('decomposer-p2', [ + { number: 1, name: 'Task 1', content: 'Do it', type: 'auto', dependencies: [] }, + ]); + + const agent = await harness.caller.spawnArchitectDecompose({ + name: 'decomposer-p2', + phaseId: phases[1].id, + }); + expect(agent.mode).toBe('decompose'); + }); + }); + describe('task persistence', () => { it('should create tasks from decomposition output', async () => { const initiative = await harness.createInitiative('Test Project');