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:
181
.planning/phases/10-multi-question-schema/10-01-PLAN.md
Normal file
181
.planning/phases/10-multi-question-schema/10-01-PLAN.md
Normal 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>
|
||||
145
.planning/phases/10-multi-question-schema/10-02-PLAN.md
Normal file
145
.planning/phases/10-multi-question-schema/10-02-PLAN.md
Normal 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>
|
||||
139
.planning/phases/10-multi-question-schema/10-03-PLAN.md
Normal file
139
.planning/phases/10-multi-question-schema/10-03-PLAN.md
Normal 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>
|
||||
126
.planning/phases/10-multi-question-schema/10-04-PLAN.md
Normal file
126
.planning/phases/10-multi-question-schema/10-04-PLAN.md
Normal 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>
|
||||
Reference in New Issue
Block a user