Skip to main content

scripts/worktree-feature/ — Zod Output Schemas

Ghost doc — These are real schema files from the scripts/worktree-feature/components/ directory. They demonstrate how to define Zod output schemas for each stage of a multi-agent pipeline, then register them with createSmithers.

Discover Schema

Defines the ticket structure output by the discovery agent.
// components/Discover.schema.ts
import { z } from "zod";

export const Ticket = z.object({
  id: z.string().describe("Unique slug identifier (lowercase kebab-case)"),
  title: z.string().describe("Short imperative title"),
  description: z.string().describe("Detailed description"),
  acceptanceCriteria: z.array(z.string()).describe("List of acceptance criteria"),
  filesToModify: z.array(z.string()).describe("Files to modify"),
  filesToCreate: z.array(z.string()).describe("Files to create"),
  dependencies: z.array(z.string()).nullable().describe("IDs of tickets this depends on"),
});
export type Ticket = z.infer<typeof Ticket>;

export const DiscoverOutput = z.object({
  tickets: z.array(Ticket).describe("All tickets ordered by dependency"),
  reasoning: z.string().describe("Why these tickets in this order"),
});
export type DiscoverOutput = z.infer<typeof DiscoverOutput>;

Implement Schema

Captures implementation results including files changed, tests, and commit messages.
// components/Implement.schema.ts
import { z } from "zod";

export const ImplementOutput = z.object({
  filesCreated: z.array(z.string()).nullable().describe("Files created"),
  filesModified: z.array(z.string()).nullable().describe("Files modified"),
  commitMessages: z.array(z.string()).describe("Git commit messages made"),
  whatWasDone: z.string().describe("Detailed description of what was implemented"),
  testsWritten: z.array(z.string()).describe("Test files written"),
  docsUpdated: z.array(z.string()).describe("Documentation files updated"),
  allTestsPassing: z.boolean().describe("Whether all tests pass after implementation"),
  testOutput: z.string().describe("Output from running tests"),
});
export type ImplementOutput = z.infer<typeof ImplementOutput>;

Validate Schema

Simple pass/fail result from running the test suite.
// components/Validate.schema.ts
import { z } from "zod";

export const ValidateOutput = z.object({
  allPassed: z.boolean().describe("Whether tests exited with status 0"),
  failingSummary: z.string().nullable().describe("Summary of what failed (null if all passed)"),
  fullOutput: z.string().describe("Full output from test runner"),
});
export type ValidateOutput = z.infer<typeof ValidateOutput>;

Review Schema

Structured review output with severity-classified issues and quality assessments.
// components/Review.schema.ts
import { z } from "zod";

export const ReviewOutput = z.object({
  reviewer: z.string().default("unknown").describe("Which agent reviewed (claude, codex)"),
  approved: z.boolean().describe("Whether the reviewer approves (LGTM)"),
  issues: z.array(z.object({
    severity: z.enum(["critical", "major", "minor", "nit"]),
    file: z.string(),
    line: z.number().nullable(),
    description: z.string(),
    suggestion: z.string().nullable(),
  })).describe("Issues found during review"),
  testCoverage: z.enum(["excellent", "good", "insufficient", "missing"]),
  codeQuality: z.enum(["excellent", "good", "needs-work", "poor"]),
  feedback: z.string().describe("Overall review feedback"),
});
export type ReviewOutput = z.infer<typeof ReviewOutput>;

ReviewFix Schema

Tracks fixes applied and false positives identified.
// components/ReviewFix.schema.ts
import { z } from "zod";

export const ReviewFixOutput = z.object({
  fixesMade: z.array(z.object({
    issue: z.string(),
    fix: z.string(),
    file: z.string(),
  })).describe("Fixes applied"),
  falsePositiveComments: z.array(z.object({
    file: z.string(),
    line: z.number(),
    issue: z.string().describe("The review issue that was a false positive"),
    rationale: z.string().describe("Why this is a false positive"),
  })).nullable().describe("False positives to suppress in future reviews"),
  commitMessages: z.array(z.string()).describe("Commit messages for fixes"),
  allIssuesResolved: z.boolean().describe("Whether all review issues were resolved"),
  summary: z.string().describe("Summary of fixes"),
});
export type ReviewFixOutput = z.infer<typeof ReviewFixOutput>;

Report Schema

Final report capturing implementation status and lessons learned.
// components/Report.schema.ts
import { z } from "zod";

export const ReportOutput = z.object({
  ticketTitle: z.string().describe("Title of the ticket"),
  status: z.enum(["completed", "partial", "failed"]).describe("Final status"),
  summary: z.string().describe("Concise summary of what was implemented"),
  filesChanged: z.number().describe("Number of files changed"),
  testsAdded: z.number().describe("Number of tests added"),
  reviewRounds: z.number().describe("How many review rounds it took"),
  struggles: z.array(z.string()).nullable().describe("Any struggles or issues encountered"),
  lessonsLearned: z.array(z.string()).nullable().describe("Lessons for future tickets"),
});
export type ReportOutput = z.infer<typeof ReportOutput>;

Registering Schemas with createSmithers

All schemas are registered in a single createSmithers call:
// smithers.ts
import { createSmithers } from "smithers-orchestrator";
import { DiscoverOutput } from "./components/Discover.schema";
import { ImplementOutput } from "./components/Implement.schema";
import { ValidateOutput } from "./components/Validate.schema";
import { ReviewOutput } from "./components/Review.schema";
import { ReviewFixOutput } from "./components/ReviewFix.schema";
import { ReportOutput } from "./components/Report.schema";

export const { Workflow, Task, useCtx, smithers, tables, outputs } = createSmithers({
  discover: DiscoverOutput,
  implement: ImplementOutput,
  validate: ValidateOutput,
  review: ReviewOutput,
  reviewFix: ReviewFixOutput,
  report: ReportOutput,
});
Then use outputs.discover, outputs.review, etc. as the output prop on <Task> components.

What This Demonstrates

  • Dual type export pattern — Each file exports both the Zod schema (DiscoverOutput) and its inferred TypeScript type (type DiscoverOutput = z.infer<...>), giving you runtime validation and compile-time types from one source.
  • .describe() annotations — Every field includes .describe() which Smithers passes to the LLM as field-level instructions in the structured output schema.
  • Nullable fields — Using .nullable() for optional data (e.g., dependencies, failingSummary, struggles) ensures the field is always present in the output JSON.
  • Enum constraintsz.enum(["critical", "major", "minor", "nit"]) restricts agent output to valid severity levels.
  • Schema compositionTicket is defined once and used in both DiscoverOutput.tickets and as a prop type throughout the pipeline.
  • createSmithers registration — All schemas are registered as keys in a single call. Smithers auto-generates SQLite tables, and the returned outputs object provides typed references for <Task output={outputs.xxx}>.