Why this issue exists
Pinned zod at 3.25.76 (current resolved) in PR fix/macos-tui-hardening. zod is 1 major behind (latest 4.3.6, stable since mid-2025). Tracking the migration as a separate focused PR — biggest blast radius of the four bump candidates.
How we use it
Blast radius: 53 files import zod. API distribution:
| Method |
Calls |
z.string |
216 |
z.number |
72 |
z.enum |
52 |
z.array |
49 |
z.boolean |
42 |
z.object |
29 |
z.literal |
16 |
z.record |
14 |
z.union |
12 |
z.null |
4 |
z.unknown |
1 |
z.preprocess |
1 |
Used everywhere there's structured validation: src/types/genie-config.ts (the central config schema), event payload schemas under src/lib/events/schemas/, wish/task validators, MCP tool schemas, executor input/output validation. This is one of the most foundational dependencies in the codebase.
Two import shapes:
import type { z } from 'zod';
import { z } from 'zod';
What's changed (3.25.76 → 4.3.6)
Architecture change
- v4 splits the package into three:
zod — the high-level API (what we use today)
@zod/mini — tree-shakable variant (smaller bundle, separate import path)
@zod/core — shared parser/logic primitives, version-pinned to zod
- Stable since
4.0.0; current latest 4.3.6 (Jan 2026).
Notable v4 highlights (from migration guide)
- Faster parsing — major perf rewrite, especially for
z.object and z.string validation hot paths.
- Better TypeScript inference —
.parse() and .safeParse() return types are narrower; intersections and unions infer cleaner.
- Tree-shaking via
@zod/mini — every method is a standalone import, so unused validators don't bloat dist/genie.js.
- Discriminated union improvements — automatic discrimination, no need for
.discriminatedUnion().
- Native JSON schema export removed from core (extracted to a separate package).
z.coerce API may have changed signature.
z.record(K, V) — v3 allowed z.record(V) shorthand; v4 may require both args.
- Error formatting —
z.ZodError shape and .format() output changed.
.refine() and .transform() chaining — order semantics tightened.
(Full migration guide: https://zod.dev/v4/changelog)
What we gain
- Bundle size.
dist/genie.js is currently ~305KB minified. zod v3 contributes meaningfully; @zod/mini could shave 10-30KB depending on tree-shake quality. Faster cold-start for the CLI.
- Validation perf. Every CLI invocation parses
genie-config.yaml through a deeply-nested schema. v4's hot-path rewrites measurably reduce startup cost.
- Better DX. Cleaner inferred types across our 53-file footprint — fewer
as casts, fewer z.infer<typeof X> workarounds.
- Maintainership. v3 is on a slow-moving deprecation path; future bug fixes and security patches will land in v4 first.
- Ecosystem alignment. Most modern TypeScript libraries (tRPC, drizzle, etc.) are migrating to or supporting v4.
Audit needed
# Discriminated union sites
grep -rn "discriminatedUnion\|z.union" src/
# z.record shorthand vs full
grep -rE "z\.record\([^,)]+\)" src/ # one-arg form may need migration
# coerce usage
grep -rn "z.coerce" src/
# error formatting consumers
grep -rn "ZodError\|\.format()\|\.flatten()\|\.errors\b" src/
# refine/transform chains
grep -rn "\.refine\(\|\.transform\(" src/
Each match needs a v4-vs-v3 behavior check.
Migration cost estimate
- 2-3 days of focused work given the 53-file blast radius and the depth of usage in
genie-config.ts + event schemas.
- Risk: medium-high. Validation regressions are the worst kind — they're often silent until production data hits an edge case.
- Strong test coverage helps: we have
genie-config.test.ts and event-schema tests. Run those first, fix breakage, re-run.
Acceptance criteria
Context
- Followup from PR
fix/macos-tui-hardening (pinned packages for supply-chain hygiene post-CanisterWorm)
- Companion follow-ups: commander v14, inquirer v8, uuid v14
- Largest of the four — recommend doing this LAST after the other three land cleanly.
Why this issue exists
Pinned
zodat3.25.76(current resolved) in PRfix/macos-tui-hardening.zodis 1 major behind (latest4.3.6, stable since mid-2025). Tracking the migration as a separate focused PR — biggest blast radius of the four bump candidates.How we use it
Blast radius: 53 files import
zod. API distribution:z.stringz.numberz.enumz.arrayz.booleanz.objectz.literalz.recordz.unionz.nullz.unknownz.preprocessUsed everywhere there's structured validation:
src/types/genie-config.ts(the central config schema), event payload schemas undersrc/lib/events/schemas/, wish/task validators, MCP tool schemas, executor input/output validation. This is one of the most foundational dependencies in the codebase.Two import shapes:
What's changed (3.25.76 → 4.3.6)
Architecture change
zod— the high-level API (what we use today)@zod/mini— tree-shakable variant (smaller bundle, separate import path)@zod/core— shared parser/logic primitives, version-pinned tozod4.0.0; current latest4.3.6(Jan 2026).Notable v4 highlights (from migration guide)
z.objectandz.stringvalidation hot paths..parse()and.safeParse()return types are narrower; intersections and unions infer cleaner.@zod/mini— every method is a standalone import, so unused validators don't bloatdist/genie.js..discriminatedUnion().z.coerceAPI may have changed signature.z.record(K, V)— v3 allowedz.record(V)shorthand; v4 may require both args.z.ZodErrorshape and.format()output changed..refine()and.transform()chaining — order semantics tightened.(Full migration guide: https://zod.dev/v4/changelog)
What we gain
dist/genie.jsis currently ~305KB minified. zod v3 contributes meaningfully;@zod/minicould shave 10-30KB depending on tree-shake quality. Faster cold-start for the CLI.genie-config.yamlthrough a deeply-nested schema. v4's hot-path rewrites measurably reduce startup cost.ascasts, fewerz.infer<typeof X>workarounds.Audit needed
Each match needs a v4-vs-v3 behavior check.
Migration cost estimate
genie-config.ts+ event schemas.genie-config.test.tsand event-schema tests. Run those first, fix breakage, re-run.Acceptance criteria
zodto4.3.6inpackage.json(exact pin)zodor split high-frequency hot paths to@zod/minifor bundle-size savingsdist/genie.jssize delta (target: ≤ current 305KB; ideally smaller)bun testclean — particularlysrc/types/genie-config.test.tsand any event-schema testsContext
fix/macos-tui-hardening(pinned packages for supply-chain hygiene post-CanisterWorm)