Consolidated non-blocking follow-ups from review of #1972 (unwrapNamedRecordUnionIntersections) and #1975 (canSafelyMerge object intersections). Identifier-boundary guard and readBalancedBody loud-fail were already addressed in-line on #1972. Remaining items below are explicitly flagged "non-blocking — file as issues" by reviewers.
From #1972 follow-up review
.d.ts regression grep is shape-fragile
test/lib/zod-schemas.test.js:46 — pattern z\.ZodIntersection<z\.ZodUnion<readonly \[z\.ZodRecord depends on Zod v4's exact emitter format (the readonly tuple prefix in particular). If Zod adjusts tuple stringification the test goes vacuously green.
Fix shape: pair with a positive assertion — confirm ProductSchema._def.typeName === 'ZodObject' so the test fails closed if regression returns under a different format.
Pass 3/4 docstrings should mention z.object( shape gate
scripts/generate-zod-from-ts.ts:585-592 (Pass 3) and :636-645 (Pass 4) describe the inline pattern but the body-prefix check is what makes the unwrap safe. Call that constraint out so the next editor doesn't broaden the right-hand side.
unionSchemaNames collection by recursive union-of-unions
Today collectRedundantRecordUnionSchemaNames only sees one-level unions. If ts-to-zod ever emits z.union([z.union([RecordA, RecordB]), z.union([RecordC, RecordD])]), the inner unions won't be detected as redundant. Pre-existing gap, not a regression.
From #1975 follow-up review
Silent skip in TOOL_INPUT_SHAPES construction
src/lib/schemas/index.ts:72-75 does flatMap → [] when shapeOf returns undefined. The added test catches the TOOL_REQUEST_SCHEMAS codepath, but any other consumer of .shape on a non-mergeable intersection still silently degrades. Replace the silent drop with an explicit throw at TOOL_INPUT_SHAPES construction so future regressions surface loudly.
Subsume earlier extractObjectSchema helper?
The extractObjectSchema / ProductObjectSchema workaround in the recent ProductSchema DX fix (#1971 draft) solved the same problem class one layer up. Worth checking whether this codegen-level fix lets us deprecate it.
isPlainZodObjectTail keep-in-sync
scripts/generate-zod-from-ts.ts:622 currently allows only .passthrough() / .strict() / .strip(). If ts-to-zod starts emitting .catchall(z.unknown()) for an index signature, safe merges silently stop happening. One-line comment near the tail-list pointing at ts-to-zod's emission would buy a heads-up later.
Typed-export regex
scripts/generate-zod-from-ts.ts:738 uses (?::[^=]+)? for optional type annotations. Today every typed export is plain; a future schema picking up a type annotation with = inside (default-typed generic) would silently fall out of the rewriter's view. Latent papercut.
Test harness spawns npx tsx per test
test/generate-zod-object-intersections.test.js:11 uses mkdtempSync inside the repo root. Cold tsx startup × 3 noticeably extends npm test, and a SIGKILL mid-run leaves orphan .zod-object-intersections-* dirs in the working tree. os.tmpdir() would be safer, or import the rewriter as a real export and skip the spawn.
None of these block #1972 or #1975. Filing as a single issue rather than per-item because they share an owner (the codegen post-processor) and would naturally cluster.
cc: surfaced by code-reviewer + javascript-protocol-expert parallel review.
Consolidated non-blocking follow-ups from review of #1972 (
unwrapNamedRecordUnionIntersections) and #1975 (canSafelyMergeobject intersections). Identifier-boundary guard andreadBalancedBodyloud-fail were already addressed in-line on #1972. Remaining items below are explicitly flagged "non-blocking — file as issues" by reviewers.From #1972 follow-up review
.d.tsregression grep is shape-fragiletest/lib/zod-schemas.test.js:46— patternz\.ZodIntersection<z\.ZodUnion<readonly \[z\.ZodRecorddepends on Zod v4's exact emitter format (thereadonlytuple prefix in particular). If Zod adjusts tuple stringification the test goes vacuously green.Fix shape: pair with a positive assertion — confirm
ProductSchema._def.typeName === 'ZodObject'so the test fails closed if regression returns under a different format.Pass 3/4 docstrings should mention
z.object(shape gatescripts/generate-zod-from-ts.ts:585-592(Pass 3) and:636-645(Pass 4) describe the inline pattern but the body-prefix check is what makes the unwrap safe. Call that constraint out so the next editor doesn't broaden the right-hand side.unionSchemaNamescollection by recursive union-of-unionsToday
collectRedundantRecordUnionSchemaNamesonly sees one-level unions. If ts-to-zod ever emitsz.union([z.union([RecordA, RecordB]), z.union([RecordC, RecordD])]), the inner unions won't be detected as redundant. Pre-existing gap, not a regression.From #1975 follow-up review
Silent skip in
TOOL_INPUT_SHAPESconstructionsrc/lib/schemas/index.ts:72-75doesflatMap → []whenshapeOfreturns undefined. The added test catches theTOOL_REQUEST_SCHEMAScodepath, but any other consumer of.shapeon a non-mergeable intersection still silently degrades. Replace the silent drop with an explicit throw atTOOL_INPUT_SHAPESconstruction so future regressions surface loudly.Subsume earlier
extractObjectSchemahelper?The
extractObjectSchema/ProductObjectSchemaworkaround in the recent ProductSchema DX fix (#1971 draft) solved the same problem class one layer up. Worth checking whether this codegen-level fix lets us deprecate it.isPlainZodObjectTailkeep-in-syncscripts/generate-zod-from-ts.ts:622currently allows only.passthrough()/.strict()/.strip(). Ifts-to-zodstarts emitting.catchall(z.unknown())for an index signature, safe merges silently stop happening. One-line comment near the tail-list pointing at ts-to-zod's emission would buy a heads-up later.Typed-export regex
scripts/generate-zod-from-ts.ts:738uses(?::[^=]+)?for optional type annotations. Today every typed export is plain; a future schema picking up a type annotation with=inside (default-typed generic) would silently fall out of the rewriter's view. Latent papercut.Test harness spawns
npx tsxper testtest/generate-zod-object-intersections.test.js:11usesmkdtempSyncinside the repo root. Cold tsx startup × 3 noticeably extendsnpm test, and aSIGKILLmid-run leaves orphan.zod-object-intersections-*dirs in the working tree.os.tmpdir()would be safer, or import the rewriter as a real export and skip the spawn.None of these block #1972 or #1975. Filing as a single issue rather than per-item because they share an owner (the codegen post-processor) and would naturally cluster.
cc: surfaced by
code-reviewer+javascript-protocol-expertparallel review.