Description
Claude's TodoWrite tool sends todo items with {content, status, activeForm}, but the TodoItemSchema in shared/src/schemas.ts requires priority (enum) and id (string) as mandatory fields. This causes TodosSchema.safeParse() to fail silently, and the web UI never displays any todos.
Expected behavior
Todo items created by Claude's TodoWrite tool should appear in the web UI's todo list panel.
Actual behavior
The todo list is always empty. safeParse() returns { success: false } because:
priority is required but Claude never sends it
id is required but Claude never sends it
activeForm is sent by Claude but not in the schema (rejected by strict parsing)
Root cause
// shared/src/schemas.ts — current (broken)
export const TodoItemSchema = z.object({
content: z.string(),
status: z.enum(['pending', 'in_progress', 'completed']),
priority: z.enum(['high', 'medium', 'low']), // ← required, but Claude never sends this
id: z.string() // ← required, but Claude never sends this
})
In hub/src/sync/todos.ts, all three extraction functions use TodosSchema.safeParse() which silently returns null on validation failure.
Suggested fix
Make priority and id optional with sensible defaults, and add activeForm as an optional field:
export const TodoItemSchema = z.object({
content: z.string(),
status: z.enum(['pending', 'in_progress', 'completed']),
priority: z.enum(['high', 'medium', 'low']).optional().default('medium'),
id: z.string().optional().default(''),
activeForm: z.string().optional()
})
This is backwards-compatible — existing data with all fields present will still parse correctly.
Description
Claude's
TodoWritetool sends todo items with{content, status, activeForm}, but theTodoItemSchemainshared/src/schemas.tsrequirespriority(enum) andid(string) as mandatory fields. This causesTodosSchema.safeParse()to fail silently, and the web UI never displays any todos.Expected behavior
Todo items created by Claude's
TodoWritetool should appear in the web UI's todo list panel.Actual behavior
The todo list is always empty.
safeParse()returns{ success: false }because:priorityis required but Claude never sends itidis required but Claude never sends itactiveFormis sent by Claude but not in the schema (rejected by strict parsing)Root cause
In
hub/src/sync/todos.ts, all three extraction functions useTodosSchema.safeParse()which silently returnsnullon validation failure.Suggested fix
Make
priorityandidoptional with sensible defaults, and addactiveFormas an optional field:This is backwards-compatible — existing data with all fields present will still parse correctly.