docs(10): create phase plan

Phase 10: Multi-Question Schema
- 4 plans in 3 waves
- Wave 1: Schema & types (01)
- Wave 2: Manager implementations + unit tests (02, 03 parallel)
- Wave 3: E2E tests (04)
- Ready for execution
This commit is contained in:
Lukas May
2026-01-31 17:51:18 +01:00
parent b3941e5431
commit 9dd0e46060
4 changed files with 591 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
---
phase: 10-multi-question-schema
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/agent/schema.ts
- src/agent/types.ts
- src/events/types.ts
autonomous: true
---
<objective>
Extend agent output schema to return array of questions; update types throughout.
Purpose: Enable agents to ask multiple questions in one pause, reducing round-trips.
Output: Updated Zod schema, TypeScript types, and event payloads supporting questions array.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08.1-agent-output-schema/08.1-01-SUMMARY.md
@src/agent/schema.ts
@src/agent/types.ts
@src/events/types.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Update agent output schema to questions array</name>
<files>src/agent/schema.ts</files>
<action>
Change `question` status variant from single question to array:
Before:
```typescript
z.object({
status: z.literal('question'),
question: z.string(),
options: z.array(optionSchema).optional(),
multiSelect: z.boolean().optional(),
})
```
After:
```typescript
z.object({
status: z.literal('questions'), // plural
questions: z.array(z.object({
id: z.string(), // unique identifier for matching answers
question: z.string(),
options: z.array(optionSchema).optional(),
multiSelect: z.boolean().optional(),
})),
})
```
Update `agentOutputJsonSchema` to match the Zod schema.
Key points:
- Rename status from 'question' to 'questions' (plural) for clarity
- Each question has an `id` field so answers can be matched
- Single question case: `questions: [{ id: "q1", question: "..." }]`
</action>
<verify>npm run typecheck passes</verify>
<done>Schema exports updated AgentOutput type with questions array</done>
</task>
<task type="auto">
<name>Task 2: Update PendingQuestion type to PendingQuestions</name>
<files>src/agent/types.ts</files>
<action>
Rename and restructure PendingQuestion:
Before:
```typescript
export interface PendingQuestion {
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}
```
After:
```typescript
export interface QuestionItem {
id: string;
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}
export interface PendingQuestions {
questions: QuestionItem[];
}
```
Update AgentManager interface:
- Rename `getPendingQuestion` to `getPendingQuestions`
- Return type: `Promise<PendingQuestions | null>`
</action>
<verify>npm run typecheck passes</verify>
<done>Types updated to array-based questions</done>
</task>
<task type="auto">
<name>Task 3: Update AgentWaitingEvent payload</name>
<files>src/events/types.ts</files>
<action>
Update AgentWaitingEvent payload to use questions array:
Before:
```typescript
export interface AgentWaitingEvent extends DomainEvent {
type: 'agent:waiting';
payload: {
agentId: string;
name: string;
taskId: string;
sessionId: string;
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
};
}
```
After:
```typescript
export interface AgentWaitingEvent extends DomainEvent {
type: 'agent:waiting';
payload: {
agentId: string;
name: string;
taskId: string;
sessionId: string;
questions: Array<{
id: string;
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}>;
};
}
```
</action>
<verify>npm run typecheck passes</verify>
<done>Event payload uses questions array</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run typecheck` passes
- [ ] `npm run build` succeeds
- [ ] Schema changes are consistent across all three files
</verification>
<success_criteria>
- All tasks completed
- Agent output schema uses `questions` array (not single question)
- PendingQuestions type uses array
- AgentWaitingEvent payload uses questions array
- Each question has `id` field for answer matching
</success_criteria>
<output>
After completion, create `.planning/phases/10-multi-question-schema/10-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,145 @@
---
phase: 10-multi-question-schema
plan: 02
type: execute
wave: 2
depends_on: ["10-01"]
files_modified:
- src/agent/manager.ts
- src/agent/mock-manager.ts
autonomous: true
---
<objective>
Update ClaudeAgentManager and MockAgentManager to handle questions array and batched answers.
Purpose: Implementations must match the updated schema and types from Plan 01.
Output: Both managers handle multi-question flow with batched resume answers.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/10-multi-question-schema/10-01-SUMMARY.md
@src/agent/schema.ts
@src/agent/types.ts
@src/agent/manager.ts
@src/agent/mock-manager.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Update ClaudeAgentManager for questions array</name>
<files>src/agent/manager.ts</files>
<action>
Update handleAgentCompletion to handle 'questions' status (was 'question'):
1. In switch statement, rename case 'question' to case 'questions'
2. Store pendingQuestions (plural) with the full questions array:
```typescript
case 'questions': {
if (active) {
active.pendingQuestions = {
questions: agentOutput.questions,
};
}
// ... emit event with questions array
}
```
3. Update ActiveAgent interface:
```typescript
interface ActiveAgent {
subprocess: ResultPromise;
result?: AgentResult;
pendingQuestions?: PendingQuestions; // was pendingQuestion
}
```
4. Rename getPendingQuestion to getPendingQuestions, return pendingQuestions
5. Update resume() signature to accept answers map:
```typescript
async resume(agentId: string, answers: Record<string, string>): Promise<void>
```
The answers map keys are question IDs, values are the user's answers.
Format answers as structured prompt for Claude: one line per answer.
</action>
<verify>npm run typecheck passes</verify>
<done>ClaudeAgentManager handles questions array and batched answers</done>
</task>
<task type="auto">
<name>Task 2: Update MockAgentManager for questions array</name>
<files>src/agent/mock-manager.ts</files>
<action>
1. Update MockAgentScenario type:
```typescript
| {
status: 'questions'; // was 'question'
questions: Array<{
id: string;
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}>;
delay?: number;
}
```
2. Update MockAgentRecord interface:
```typescript
pendingQuestions?: PendingQuestions; // was pendingQuestion
```
3. Update completeAgent() switch case from 'question' to 'questions':
```typescript
case 'questions':
record.info.status = 'waiting_for_input';
record.pendingQuestions = {
questions: scenario.questions,
};
// emit event with questions array
```
4. Rename getPendingQuestion to getPendingQuestions
5. Update resume() signature:
```typescript
async resume(agentId: string, answers: Record<string, string>): Promise<void>
```
Clear pendingQuestions on resume.
</action>
<verify>npm run typecheck passes</verify>
<done>MockAgentManager handles questions array and batched answers</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run typecheck` passes
- [ ] `npm run build` succeeds
- [ ] Both managers use questions array consistently
- [ ] resume() accepts answers map on both managers
</verification>
<success_criteria>
- All tasks completed
- ClaudeAgentManager handles 'questions' status
- MockAgentManager handles 'questions' status
- Both resume() methods accept Record<string, string> answers
- getPendingQuestions returns array-based type
</success_criteria>
<output>
After completion, create `.planning/phases/10-multi-question-schema/10-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,139 @@
---
phase: 10-multi-question-schema
plan: 03
type: execute
wave: 2
depends_on: ["10-01"]
files_modified:
- src/test/harness.ts
- src/agent/manager.test.ts
- src/agent/mock-manager.test.ts
- src/test/harness.test.ts
- src/dispatch/manager.test.ts
autonomous: true
---
<objective>
Update TestHarness and all tests to use questions array schema.
Purpose: Tests must exercise the new multi-question flow.
Output: All tests pass with updated schema, TestHarness provides multi-question helpers.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/10-multi-question-schema/10-01-SUMMARY.md
@src/test/harness.ts
@src/agent/mock-manager.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Update TestHarness convenience methods</name>
<files>src/test/harness.ts</files>
<action>
1. Update setAgentQuestion to setAgentQuestions (or keep name but accept array):
```typescript
setAgentQuestions(
agentName: string,
questions: Array<{
id: string;
question: string;
options?: Array<{ label: string; description?: string }>;
multiSelect?: boolean;
}>,
delay?: number
): void {
this.agentManager.setScenario(agentName, {
status: 'questions',
questions,
delay,
});
}
```
2. For single-question convenience, add helper that wraps:
```typescript
setAgentQuestion(
agentName: string,
questionId: string,
question: string,
options?: Array<{ label: string; description?: string }>,
delay?: number
): void {
this.setAgentQuestions(agentName, [{
id: questionId,
question,
options,
}], delay);
}
```
3. Rename getPendingQuestion to getPendingQuestions, return PendingQuestions | null
</action>
<verify>npm run typecheck passes</verify>
<done>TestHarness supports questions array with convenience helpers</done>
</task>
<task type="auto">
<name>Task 2: Update all tests for questions array</name>
<files>src/agent/manager.test.ts, src/agent/mock-manager.test.ts, src/test/harness.test.ts, src/dispatch/manager.test.ts</files>
<action>
Update all test files that use question scenarios:
1. manager.test.ts:
- Update mock CLI output to use 'questions' status with array
- Update assertions for getPendingQuestions
2. mock-manager.test.ts:
- Update all scenarios from { status: 'question', question: '...' }
to { status: 'questions', questions: [{ id: 'q1', question: '...' }] }
- Update assertions for pendingQuestions array
- Add test for multi-question scenario
3. harness.test.ts:
- Update setAgentQuestion calls to new signature
- Update getPendingQuestions assertions
4. dispatch/manager.test.ts:
- Update any question scenario mocks
Search pattern to find affected code:
- `status: 'question'``status: 'questions'`
- `question:` (in scenarios) → `questions: [{ id: '...', question: ... }]`
- `getPendingQuestion``getPendingQuestions`
- `.question` assertions → `.questions[0].question`
</action>
<verify>npm run test passes</verify>
<done>All tests updated and passing with questions array</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run typecheck` passes
- [ ] `npm run test` passes (all tests green)
- [ ] No references to old 'question' singular status remain
</verification>
<success_criteria>
- All tasks completed
- TestHarness provides setAgentQuestions and setAgentQuestion helpers
- All existing tests updated to questions array format
- New test for multi-question scenario exists
- All tests pass
</success_criteria>
<output>
After completion, create `.planning/phases/10-multi-question-schema/10-03-SUMMARY.md`
</output>

View File

@@ -0,0 +1,126 @@
---
phase: 10-multi-question-schema
plan: 04
type: execute
wave: 3
depends_on: ["10-02", "10-03"]
files_modified:
- src/test/e2e/edge-cases.test.ts
- src/test/e2e/recovery-scenarios.test.ts
autonomous: true
---
<objective>
Update E2E tests to use questions array schema and add multi-question E2E scenario.
Purpose: Prove multi-question flow works end-to-end through dispatch/coordination.
Output: E2E tests pass with new schema, multi-question scenario validated.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/10-multi-question-schema/10-02-SUMMARY.md
@.planning/phases/10-multi-question-schema/10-03-SUMMARY.md
@src/test/e2e/edge-cases.test.ts
@src/test/e2e/recovery-scenarios.test.ts
</context>
<tasks>
<task type="auto">
<name>Task 1: Update existing E2E tests for questions array</name>
<files>src/test/e2e/edge-cases.test.ts, src/test/e2e/recovery-scenarios.test.ts</files>
<action>
Update all E2E tests that use question scenarios:
1. edge-cases.test.ts:
- Find all setAgentQuestion calls, update to new signature with id
- Update any direct scenario setting to use questions array
- Update assertions checking question data
2. recovery-scenarios.test.ts:
- Update Q&A flow tests to use questions array format
- Update resume calls to pass answers as Record<string, string>
- Update assertions for getPendingQuestions
Replace patterns:
- `harness.setAgentQuestion(name, question)``harness.setAgentQuestion(name, 'q1', question)`
- `agentManager.resume(id, answer)``agentManager.resume(id, { q1: answer })`
</action>
<verify>npm run test:e2e passes</verify>
<done>Existing E2E tests pass with questions array schema</done>
</task>
<task type="auto">
<name>Task 2: Add multi-question E2E test</name>
<files>src/test/e2e/recovery-scenarios.test.ts</files>
<action>
Add new test in "Agent Q&A extended scenarios" describe block:
```typescript
it('should handle agent asking multiple questions at once', async () => {
// Setup: agent asks two questions
harness.setAgentQuestions('worker', [
{ id: 'q1', question: 'Which database?', options: [{ label: 'SQLite' }, { label: 'Postgres' }] },
{ id: 'q2', question: 'Include tests?', options: [{ label: 'Yes' }, { label: 'No' }] },
]);
// Dispatch task
await harness.dispatchManager.queueTask({ ... });
await harness.dispatchManager.processQueue();
await harness.waitForEvent('agent:waiting');
// Verify both questions present
const pending = await harness.getPendingQuestions('worker');
expect(pending?.questions).toHaveLength(2);
expect(pending?.questions[0].id).toBe('q1');
expect(pending?.questions[1].id).toBe('q2');
// Resume with answers for both questions
harness.setAgentDone('worker');
const agent = await harness.agentManager.getByName('worker');
await harness.agentManager.resume(agent!.id, {
q1: 'SQLite',
q2: 'Yes',
});
// Wait for completion
await harness.waitForEvent('agent:stopped');
// Verify task completed
const task = await harness.taskRepository.findById(...);
expect(task?.status).toBe('completed');
});
```
</action>
<verify>npm run test:e2e passes including new multi-question test</verify>
<done>Multi-question E2E test validates full flow</done>
</task>
</tasks>
<verification>
Before declaring plan complete:
- [ ] `npm run test` passes (all unit + E2E tests)
- [ ] Multi-question scenario test exists and passes
- [ ] No references to old single-question format in E2E tests
</verification>
<success_criteria>
- All tasks completed
- All E2E tests updated to questions array format
- New multi-question E2E test validates batched answers
- All tests pass
</success_criteria>
<output>
After completion, create `.planning/phases/10-multi-question-schema/10-04-SUMMARY.md`
</output>