Summary
Add runtime payload validation using Zod for agent output parsing and external IPC inputs to prevent malformed data from crashing UI components or causing silent failures.
Problem Statement
Currently, the codebase relies solely on TypeScript compile-time types for data validation. While this is sufficient for internal service communication, it leaves vulnerabilities at system boundaries:
- Agent output parsing: Claude/Gemini agents may return malformed JSON or unexpected data structures
- IPC boundaries: Renderer could send malformed payloads (though unlikely with TypeScript)
- External data sources: File contents, git output, package.json parsing
These can cause runtime errors like "Cannot read property of undefined" or silent failures where components render with incomplete data.
Context
The current type system provides excellent compile-time safety via:
CanopyEventMap with 33+ typed events
- TypeScript interfaces for all data structures
- Type guards in IPC handlers
However, runtime validation is missing at critical boundaries, especially for:
- Agent state machine transitions based on terminal output
- CopyTree progress parsing
- AI summary generation responses
Deliverables
Code Changes
New Files to Create:
electron/schemas/agent.ts - Zod schemas for agent events and state
electron/schemas/ipc.ts - Zod schemas for IPC payloads
electron/schemas/external.ts - Zod schemas for external data (package.json, git output)
Files to Modify:
Implementation Details
1. Install Zod:
2. Create Agent Event Schemas:
// electron/schemas/agent.ts
import { z } from "zod";
export const AgentSpawnedSchema = z.object({
agentId: z.string(),
terminalId: z.string(),
type: z.enum(["claude", "gemini", "shell", "custom"]),
worktreeId: z.string().optional(),
timestamp: z.number(),
});
export const AgentStateChangedSchema = z.object({
agentId: z.string(),
state: z.enum(["idle", "working", "waiting", "completed", "failed"]),
previousState: z.enum(["idle", "working", "waiting", "completed", "failed"]),
timestamp: z.number(),
});
export const AgentOutputSchema = z.object({
agentId: z.string(),
data: z.string(),
timestamp: z.number(),
});
3. Validate at Boundaries:
// In PtyManager.ts
import { AgentOutputSchema } from "../schemas/agent.js";
ptyProcess.onData((data) => {
const result = AgentOutputSchema.safeParse({
agentId: id,
data,
timestamp: Date.now(),
});
if (!result.success) {
console.error("Invalid agent output", result.error);
return;
}
this.emit("data", id, result.data.data);
// Continue with state machine...
});
4. IPC Payload Validation:
// In ipc/handlers.ts
import { TerminalSpawnOptionsSchema } from "../schemas/ipc.js";
const handleTerminalSpawn = async (event, options) => {
const result = TerminalSpawnOptionsSchema.safeParse(options);
if (!result.success) {
throw new Error(`Invalid spawn options: ${result.error.message}`);
}
// Proceed with validated data
const { cwd, shell, cols, rows } = result.data;
// ...
};
Tests
- Unit tests for schema validation (valid and invalid inputs)
- Integration tests for IPC handlers with malformed payloads
- Test agent output parsing with edge cases
Documentation
- Document validation approach in architecture docs
- Add JSDoc comments to schemas explaining each field
- Update error handling documentation
Technical Specifications
Footprint:
- New:
electron/schemas/ directory (~3 files)
- Modified: 4-5 existing service files
- Dependencies:
zod package (~10KB gzipped)
Performance Considerations:
- Zod validation is very fast (<1ms for typical payloads)
- Only validate at system boundaries, not internal events
- Use
safeParse() to avoid throwing exceptions
Validation Strategy:
- High Priority: Agent outputs, AI responses, external data
- Medium Priority: IPC payloads from renderer
- Low Priority: Internal TypedEventBus events (already type-safe)
Dependencies
None - can be implemented independently
Tasks
Acceptance Criteria
Edge Cases & Risks
- Risk: Over-validation can add unnecessary overhead - only validate at boundaries
- Risk: Schema drift from TypeScript types - consider using
zod-to-ts or ts-to-zod for sync
- Edge case: Unknown fields in payloads - use
.passthrough() or .strict() strategically
- Benefit: Catches bugs before they reach UI components
- Benefit: Provides actionable error messages for debugging
Alternatives Considered
- io-ts: More functional programming style, steeper learning curve
- Joi: Larger bundle size, primarily for server-side validation
- AJV: JSON Schema-based, less TypeScript-native
- Custom validators: More code to maintain, less standardized
Why Zod: TypeScript-first, excellent type inference, small bundle, widely adopted in modern projects.
Additional Context
This is a preventative measure to improve system reliability. The current system hasn't experienced crashes from malformed data, but adding validation at boundaries is a best practice for production systems, especially when integrating with external AI agents.
Summary
Add runtime payload validation using Zod for agent output parsing and external IPC inputs to prevent malformed data from crashing UI components or causing silent failures.
Problem Statement
Currently, the codebase relies solely on TypeScript compile-time types for data validation. While this is sufficient for internal service communication, it leaves vulnerabilities at system boundaries:
These can cause runtime errors like "Cannot read property of undefined" or silent failures where components render with incomplete data.
Context
The current type system provides excellent compile-time safety via:
CanopyEventMapwith 33+ typed eventsHowever, runtime validation is missing at critical boundaries, especially for:
Deliverables
Code Changes
New Files to Create:
electron/schemas/agent.ts- Zod schemas for agent events and stateelectron/schemas/ipc.ts- Zod schemas for IPC payloadselectron/schemas/external.ts- Zod schemas for external data (package.json, git output)Files to Modify:
electron/services/PtyManager.ts- validate agent output before state transitionselectron/services/AgentStateMachine.ts- validate event payloadselectron/ipc/handlers.ts- validate critical IPC payloadselectron/services/ai/worktree.ts- validate AI response JSONImplementation Details
1. Install Zod:
2. Create Agent Event Schemas:
3. Validate at Boundaries:
4. IPC Payload Validation:
Tests
Documentation
Technical Specifications
Footprint:
electron/schemas/directory (~3 files)zodpackage (~10KB gzipped)Performance Considerations:
safeParse()to avoid throwing exceptionsValidation Strategy:
Dependencies
None - can be implemented independently
Tasks
npm install zodelectron/schemas/agent.tswith agent event schemaselectron/schemas/ipc.tswith IPC payload schemaselectron/schemas/external.tsfor external dataPtyManager.tsfor agent outputhandlers.tsAcceptance Criteria
Edge Cases & Risks
zod-to-tsorts-to-zodfor sync.passthrough()or.strict()strategicallyAlternatives Considered
Why Zod: TypeScript-first, excellent type inference, small bundle, widely adopted in modern projects.
Additional Context
This is a preventative measure to improve system reliability. The current system hasn't experienced crashes from malformed data, but adding validation at boundaries is a best practice for production systems, especially when integrating with external AI agents.