diff --git a/.planning/phases/11-architect-agent/11-01-PLAN.md b/.planning/phases/11-architect-agent/11-01-PLAN.md index 591017c..035b6a6 100644 --- a/.planning/phases/11-architect-agent/11-01-PLAN.md +++ b/.planning/phases/11-architect-agent/11-01-PLAN.md @@ -78,63 +78,244 @@ Output: Extended agent schema with mode field, mode-specific output schemas, upd Task 2: Create mode-specific output schemas src/agent/schema.ts -Add mode-specific output schemas following the existing discriminated union pattern: +Add mode-specific output schemas following the existing discriminated union pattern. + +**IMPORTANT:** These schemas define the contract between the agent prompt and the system. +The prompt tells the agent what format to output; these schemas validate that output. 1. Keep existing agentOutputSchema as 'execute' mode schema (already handles done/questions/error) -2. Add discussOutputSchema for discussion mode: +2. Add decisionSchema for reuse: ```typescript + /** + * A decision captured during discussion. + * Prompt instructs: { "topic": "Auth", "decision": "JWT", "reason": "Stateless" } + */ + const decisionSchema = z.object({ + topic: z.string(), + decision: z.string(), + reason: z.string(), + }); + + export type Decision = z.infer; + ``` + +3. Add phaseBreakdownSchema for reuse: + ```typescript + /** + * A phase from breakdown output. + * Prompt instructs: { "number": 1, "name": "...", "description": "...", "dependencies": [0] } + */ + const phaseBreakdownSchema = z.object({ + number: z.number().int().positive(), + name: z.string().min(1), + description: z.string(), + dependencies: z.array(z.number().int()).optional().default([]), + }); + + export type PhaseBreakdown = z.infer; + ``` + +4. Add discussOutputSchema for discussion mode: + ```typescript + /** + * Discuss mode output schema. + * Agent asks questions OR completes with decisions. + * + * Prompt tells agent: + * - Output "questions" status with questions array when needing input + * - Output "context_complete" status with decisions array when done + */ export const discussOutputSchema = z.discriminatedUnion('status', [ + // Agent needs more information z.object({ status: z.literal('questions'), questions: z.array(questionItemSchema), - context: z.string().optional(), // Summary of what's been discussed so far }), + // Agent has captured all decisions z.object({ status: z.literal('context_complete'), - decisions: z.array(z.object({ - topic: z.string(), - decision: z.string(), - reason: z.string(), - })), - summary: z.string(), + decisions: z.array(decisionSchema), + summary: z.string(), // Brief summary of all decisions }), + // Unrecoverable error z.object({ status: z.literal('unrecoverable_error'), error: z.string(), }), ]); + + export type DiscussOutput = z.infer; ``` -3. Add breakdownOutputSchema for breakdown mode: +5. Add breakdownOutputSchema for breakdown mode: ```typescript + /** + * Breakdown mode output schema. + * Agent asks questions OR completes with phases. + * + * Prompt tells agent: + * - Output "questions" status when needing clarification + * - Output "breakdown_complete" status with phases array when done + */ export const breakdownOutputSchema = z.discriminatedUnion('status', [ + // Agent needs clarification z.object({ status: z.literal('questions'), questions: z.array(questionItemSchema), }), + // Agent has decomposed initiative into phases z.object({ status: z.literal('breakdown_complete'), - phases: z.array(z.object({ - number: z.number(), - name: z.string(), - description: z.string(), - dependencies: z.array(z.number()).optional(), // Phase numbers this depends on - })), + phases: z.array(phaseBreakdownSchema), }), + // Unrecoverable error z.object({ status: z.literal('unrecoverable_error'), error: z.string(), }), ]); + + export type BreakdownOutput = z.infer; ``` -4. Export all schemas and types. +6. Create JSON schema versions for --json-schema flag: + ```typescript + /** + * JSON Schema for discuss mode (passed to Claude CLI --json-schema) + */ + export const discussOutputJsonSchema = { + type: 'object', + oneOf: [ + { + properties: { + status: { const: 'questions' }, + questions: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + question: { type: 'string' }, + options: { + type: 'array', + items: { + type: 'object', + properties: { + label: { type: 'string' }, + description: { type: 'string' }, + }, + required: ['label'], + }, + }, + multiSelect: { type: 'boolean' }, + }, + required: ['id', 'question'], + }, + }, + }, + required: ['status', 'questions'], + }, + { + properties: { + status: { const: 'context_complete' }, + decisions: { + type: 'array', + items: { + type: 'object', + properties: { + topic: { type: 'string' }, + decision: { type: 'string' }, + reason: { type: 'string' }, + }, + required: ['topic', 'decision', 'reason'], + }, + }, + summary: { type: 'string' }, + }, + required: ['status', 'decisions', 'summary'], + }, + { + properties: { + status: { const: 'unrecoverable_error' }, + error: { type: 'string' }, + }, + required: ['status', 'error'], + }, + ], + }; -5. Create JSON schema versions for --json-schema flag (discussOutputJsonSchema, breakdownOutputJsonSchema). + /** + * JSON Schema for breakdown mode (passed to Claude CLI --json-schema) + */ + export const breakdownOutputJsonSchema = { + type: 'object', + oneOf: [ + { + properties: { + status: { const: 'questions' }, + questions: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + question: { type: 'string' }, + options: { + type: 'array', + items: { + type: 'object', + properties: { + label: { type: 'string' }, + description: { type: 'string' }, + }, + required: ['label'], + }, + }, + }, + required: ['id', 'question'], + }, + }, + }, + required: ['status', 'questions'], + }, + { + properties: { + status: { const: 'breakdown_complete' }, + phases: { + type: 'array', + items: { + type: 'object', + properties: { + number: { type: 'integer', minimum: 1 }, + name: { type: 'string', minLength: 1 }, + description: { type: 'string' }, + dependencies: { + type: 'array', + items: { type: 'integer' }, + }, + }, + required: ['number', 'name', 'description'], + }, + }, + }, + required: ['status', 'phases'], + }, + { + properties: { + status: { const: 'unrecoverable_error' }, + error: { type: 'string' }, + }, + required: ['status', 'error'], + }, + ], + }; + ``` + +7. Export all schemas, types, and JSON schemas. npm run build passes, new schemas are exported - Three mode-specific output schemas exist with JSON schema versions + Three mode-specific output schemas with JSON schema versions, aligned with agent prompts @@ -170,6 +351,8 @@ Before declaring plan complete: - [ ] npm test passes (all existing tests still work) - [ ] AgentMode type is exported from src/agent/types.ts - [ ] All three output schemas are exported from src/agent/schema.ts +- [ ] JSON schemas (discussOutputJsonSchema, breakdownOutputJsonSchema) are exported +- [ ] Decision and PhaseBreakdown types are exported for use by other modules - [ ] MockAgentManager handles mode parameter @@ -177,7 +360,11 @@ Before declaring plan complete: - All tasks completed - Agent mode tracking works end-to-end -- Mode-specific output schemas are validated by Zod +- Mode-specific output schemas match the prompt output format: + - discuss: questions OR context_complete with decisions array + - breakdown: questions OR breakdown_complete with phases array + - execute: done/questions/unrecoverable_error (existing) +- JSON schemas can be passed to Claude CLI --json-schema flag - Existing tests pass (backwards compatible) - No TypeScript errors