docs(11-01): expand schemas with prompt alignment

- Add Decision and PhaseBreakdown reusable types
- Add complete JSON schemas for Claude CLI --json-schema
- Add comments explaining prompt-schema contract
- Schemas match what prompts tell agent to output
This commit is contained in:
Lukas May
2026-01-31 19:00:34 +01:00
parent 93a939ea99
commit 1942d98ebb

View File

@@ -78,63 +78,244 @@ Output: Extended agent schema with mode field, mode-specific output schemas, upd
<name>Task 2: Create mode-specific output schemas</name>
<files>src/agent/schema.ts</files>
<action>
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<typeof decisionSchema>;
```
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<typeof phaseBreakdownSchema>;
```
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<typeof discussOutputSchema>;
```
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<typeof breakdownOutputSchema>;
```
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.
</action>
<verify>npm run build passes, new schemas are exported</verify>
<done>Three mode-specific output schemas exist with JSON schema versions</done>
<done>Three mode-specific output schemas with JSON schema versions, aligned with agent prompts</done>
</task>
<task type="auto">
@@ -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
</verification>
@@ -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
</success_criteria>