Phase 12: Phase-Task Decomposition - 8 plans in 4 waves - 4 parallel, 4 sequential - Ready for execution
7.7 KiB
7.7 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 12-phase-task-decomposition | 08 | execute | 4 |
|
|
true |
Purpose: Validate the complete workflow from plan creation through task decomposition and persistence.
Output: E2E tests proving decompose mode works end-to-end.
<execution_context>
@/.claude/get-shit-done/workflows/execute-plan.md
@/.claude/get-shit-done/templates/summary.md
</execution_context>
Test infrastructure from Phase 11
@.planning/phases/11-architect-agent/11-08-SUMMARY.md
TestHarness
@src/test/harness.ts @src/test/e2e/architect-workflow.test.ts
Task 1: Add TestHarness decompose mode helpers src/test/harness.ts Add decompose scenario helpers to TestHarness following architect mode pattern:/**
* Set decompose complete scenario for an agent.
* Agent will complete with task array when spawned.
*/
setArchitectDecomposeComplete(agentName: string, tasks: Array<{
number: number;
name: string;
description: string;
type?: string;
dependencies?: number[];
}>) {
this.mockAgentManager.setScenario(agentName, {
status: 'decompose_complete',
tasks: tasks.map(t => ({
...t,
type: t.type || 'auto',
dependencies: t.dependencies || [],
})),
});
}
/**
* Set decompose questions scenario for an agent.
* Agent will pause with questions when spawned.
*/
setArchitectDecomposeQuestions(agentName: string, questions: Array<{
id: string;
question: string;
options?: Array<{ label: string }>;
}>) {
this.mockAgentManager.setScenario(agentName, {
status: 'questions',
questions,
});
}
Also add convenience methods for creating plans in tests:
async createPlan(phaseId: string, name: string, description?: string) {
return this.caller.createPlan({ phaseId, name, description });
}
async getTasksForPlan(planId: string) {
return this.caller.listTasks({ planId });
}
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { TestHarness } from '../harness.js';
describe('Decompose Workflow', () => {
let harness: TestHarness;
beforeEach(async () => {
vi.useFakeTimers();
harness = await TestHarness.create();
});
afterEach(async () => {
await harness.cleanup();
vi.useRealTimers();
});
describe('spawn decompose agent', () => {
it('should spawn agent in decompose mode and complete with tasks', async () => {
// Setup: Create initiative -> phase -> plan
const initiative = await harness.createInitiative('Test Project');
const phases = await harness.createPhasesFromBreakdown(initiative.id, [
{ number: 1, name: 'Phase 1', description: 'First phase', dependencies: [] },
]);
const plan = await harness.createPlan(phases[0].id, 'Auth Plan', 'Implement auth');
// Set decompose scenario
harness.setArchitectDecomposeComplete('decomposer', [
{ number: 1, name: 'Create schema', description: 'User table', type: 'auto', dependencies: [] },
{ number: 2, name: 'Create endpoint', description: 'Login API', type: 'auto', dependencies: [1] },
]);
// Spawn decompose agent
await harness.caller.spawnArchitectDecompose({
name: 'decomposer',
planId: plan.id,
});
// Advance timers for async completion
await harness.advanceTimers(100);
// Verify agent completed
const events = harness.getEmittedEvents();
expect(events).toContainEqual(
expect.objectContaining({
type: 'agent:stopped',
payload: expect.objectContaining({
name: 'decomposer',
reason: 'decompose_complete',
}),
})
);
});
it('should pause on questions and resume', async () => {
const initiative = await harness.createInitiative('Test Project');
const phases = await harness.createPhasesFromBreakdown(initiative.id, [
{ number: 1, name: 'Phase 1', description: 'First phase', dependencies: [] },
]);
const plan = await harness.createPlan(phases[0].id, 'Complex Plan');
// Set questions scenario
harness.setArchitectDecomposeQuestions('decomposer', [
{ id: 'q1', question: 'How granular should tasks be?' },
]);
await harness.caller.spawnArchitectDecompose({
name: 'decomposer',
planId: plan.id,
});
await harness.advanceTimers(100);
// Verify paused on questions
const events = harness.getEmittedEvents();
expect(events).toContainEqual(
expect.objectContaining({
type: 'agent:stopped',
payload: expect.objectContaining({
reason: 'questions',
}),
})
);
// Set completion scenario for resume
harness.setArchitectDecomposeComplete('decomposer', [
{ number: 1, name: 'Task 1', description: 'Single task', type: 'auto' },
]);
// Resume with answer
await harness.mockAgentManager.resume('decomposer', { q1: 'Very granular' });
await harness.advanceTimers(100);
// Verify completed after resume
const finalEvents = harness.getEmittedEvents();
expect(finalEvents.filter(e => e.type === 'agent:stopped')).toHaveLength(2);
});
});
describe('task persistence', () => {
it('should create tasks from decomposition output', async () => {
const initiative = await harness.createInitiative('Test Project');
const phases = await harness.createPhasesFromBreakdown(initiative.id, [
{ number: 1, name: 'Phase 1', description: 'First phase', dependencies: [] },
]);
const plan = await harness.createPlan(phases[0].id, 'Auth Plan');
// Create tasks from decomposition
await harness.caller.createTasksFromDecomposition({
planId: plan.id,
tasks: [
{ number: 1, name: 'Schema', description: 'Create tables', type: 'auto', dependencies: [] },
{ number: 2, name: 'API', description: 'Create endpoints', type: 'auto', dependencies: [1] },
{ number: 3, name: 'Verify', description: 'Test flow', type: 'checkpoint:human-verify', dependencies: [2] },
],
});
// Verify tasks created
const tasks = await harness.getTasksForPlan(plan.id);
expect(tasks).toHaveLength(3);
expect(tasks[0].name).toBe('Schema');
expect(tasks[1].name).toBe('API');
expect(tasks[2].type).toBe('checkpoint:human-verify');
});
});
});
<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- Full E2E coverage for decompose workflow </success_criteria>