π¦«ποΈ dispatch to foreman
π§ task enqueued
ββ priority = ?
ββ yieldage = ?
ββ leverage = ?
title
fix(deps): zod is a hard dep but leaks through the public type signature β pin-skew forces a 2nd ZodType identity and OOMs consumer tsc
description
fix(deps): zod is a hard dependency but leaks through the public type signature β a pin-skew forces a 2nd ZodType identity and OOMs consumer tsc
.what
sdk-aws-lambda declares zod as an exact hard dependency, while zod types appear
throughout its public .d.ts surface. any consumer that resolves a different zod version
gets a second ZodType identity, and tsc must reconcile two ~120k-line, near-identical
type graphs to infer TInput from genLambdaEndpoint. that reconciliation is super-linear and
exhausts the default ~4GB node heap.
ask: move zod to a peerDependency with a broad range (^4), keep an exact pin in
devDependencies for the sdk's own build.
.evidence β measured in ahbode/svc-notifications across the declapract upgrade
the pin, today (0.3.0)
zod leaks through the PUBLIC type surface
dist/domain.operations/genLambdaEndpoint/genLambdaEndpoint.forAskEndpoint.d.ts:4
import type { ZodSchema } from 'zod';
dist/domain.operations/genLambdaEndpoint/genLambdaEndpoint.forApiGateway.d.ts:4
import type { ZodSchema } from 'zod';
dist/domain.operations/genLambdaEndpoint/middleware/getValidatedOutput.d.ts:1
import type { ZodType } from 'zod';
dist/domain.operations/genLambdaEndpoint/middleware/getValidationError.d.ts:2
import type { ZodError } from 'zod';
dist/domain.objects/LambdaEndpointSchema.d.ts:1
import type { JSONSchema } from 'zod/v4/core/json-schema';
ZodSchema sits directly in the signature that infers:
genLambdaEndpoint<TInput, TOutput>(input: { schema: { input: ZodSchema<TInput> } })
so TInput inference is exactly where the cross-copy reconciliation happens.
the skew is live in the ehmpathy tree right now
sdk-aws-lambda 0.3.0 dependencies.zod = "4.4.3" β hard dep
sdk-config 0.2.3 dependencies.zod = "4.3.6" β hard dep, DIFFERENT exact pin
two ehmpathy sdks, two exact hard pins, both leak zod types. a consumer that installs both gets
two copies unless it hand-writes an override. we measured a third copy (4.3.4) in the same
tree before we collapsed it.
the failure mode we measured
| scope |
with 3 zod copies |
after collapse to one |
one trivial genLambdaEndpoint call |
OOM at ~225s / 4GB |
6.4s, 188MB, exit 0 |
full src build (tsconfig.build.json) |
OOM at ~760s / 4GB |
22.95s, 738MB, exit 0 |
full test:types |
OOM |
exit 0 |
the OOM masks the real error, which is:
_zod.version.minor: Type '4' is not assignable to type '3'
the versions genuinely are incompatible identities; the OOM is tsc's effort to prove otherwise.
the discriminator that isolates the cause
explicit type args at the call site skip inference:
genLambdaEndpoint<unknown, unknown>({ ... }) // OOM vanishes, seconds not minutes
this proves 100% of the cost is TInput inference, which unifies two ZodType graphs β not
code volume, not any single complex type, not the return type.
.why the current shape is wrong
rule.require.pinned-versions is written for a consumer app β pin exactly, eliminate
surprise. a library that surfaces a foreign type in its own public signature is the opposite
case: an exact hard pin imposes a duplicate identity on every consumer that resolves any other
version. the ecosystem convention for exactly this case is a peer range.
the tree already demonstrates the correct shape β every package that peer-declares zod dedupes
cleanly:
sdk-aws-lambda 0.3.0 βββ zod 4.4.3 β hard dep (the hazard)
sdk-config 0.2.3 βββ zod 4.3.6 β hard dep (the hazard)
rhachet 1.44.4 βββ zod 4.4.3 peer β correct
openai 5.8.2 βββ zod 4.4.3 peer β correct
@anthropic-ai/sdk βββ zod 4.4.3 peer β correct
.the ask
- one identity across the whole tree; consumers no longer need
pnpm.overrides
- a real zod-4 major break still surfaces at install as a peer-range violation, loud and early
- consider
peerDependenciesMeta if any zod-bound export should stay optional
domain-objects deserves the same audit β it also leaks types and is pinned inconsistently
(0.33.0 in sdk-aws-lambda vs 0.31.9 in sdk-config).
.scope note
this is a twin defect to sdk-config's identical shape (dispatched separately). the general
rule both sdks drifted past:
a package that surfaces a third-party type in its public .d.ts must declare that package as
a peer with a broad range β never as an exact hard dependency.
that rule is written down nowhere today, which is why both sdks reached the same shape
independently.
.workaround in place downstream (not a fix)
every consumer has to rediscover this. the fix belongs upstream.
.refs
- durable lesson (written across this migration):
ahbode/svc-notifications:.agent/repo=.this/role=any/briefs/lesson.tsc-oom-from-multi-version-schema-lib.md
- full bisection audit trail:
ahbode/svc-notifications:.route/v2026_07_28.declapract.upgrade/PERF.tsc-inference-fix.ledger.md
- a prior instance of the same single-identity class used
joi instead of zod β it recurs per
schema library until the peer-range convention is adopted
π¦«ποΈ dispatch to foreman
title
fix(deps): zod is a hard dep but leaks through the public type signature β pin-skew forces a 2nd ZodType identity and OOMs consumer tsc
description
fix(deps): zod is a hard dependency but leaks through the public type signature β a pin-skew forces a 2nd ZodType identity and OOMs consumer tsc
.what
sdk-aws-lambdadeclareszodas an exact harddependency, whilezodtypes appearthroughout its public
.d.tssurface. any consumer that resolves a differentzodversiongets a second
ZodTypeidentity, andtscmust reconcile two ~120k-line, near-identicaltype graphs to infer
TInputfromgenLambdaEndpoint. that reconciliation is super-linear andexhausts the default ~4GB node heap.
ask: move
zodto apeerDependencywith a broad range (^4), keep an exact pin indevDependenciesfor the sdk's own build..evidence β measured in ahbode/svc-notifications across the declapract upgrade
the pin, today (0.3.0)
zod leaks through the PUBLIC type surface
ZodSchemasits directly in the signature that infers:so
TInputinference is exactly where the cross-copy reconciliation happens.the skew is live in the ehmpathy tree right now
two ehmpathy sdks, two exact hard pins, both leak zod types. a consumer that installs both gets
two copies unless it hand-writes an override. we measured a third copy (
4.3.4) in the sametree before we collapsed it.
the failure mode we measured
genLambdaEndpointcalltsconfig.build.json)test:typesthe OOM masks the real error, which is:
the versions genuinely are incompatible identities; the OOM is tsc's effort to prove otherwise.
the discriminator that isolates the cause
explicit type args at the call site skip inference:
this proves 100% of the cost is
TInputinference, which unifies twoZodTypegraphs β notcode volume, not any single complex type, not the return type.
.why the current shape is wrong
rule.require.pinned-versionsis written for a consumer app β pin exactly, eliminatesurprise. a library that surfaces a foreign type in its own public signature is the opposite
case: an exact hard pin imposes a duplicate identity on every consumer that resolves any other
version. the ecosystem convention for exactly this case is a peer range.
the tree already demonstrates the correct shape β every package that peer-declares zod dedupes
cleanly:
.the ask
{ "peerDependencies": { "zod": "^4" }, "devDependencies": { "zod": "4.4.3" } }pnpm.overridespeerDependenciesMetaif any zod-bound export should stay optionaldomain-objectsdeserves the same audit β it also leaks types and is pinned inconsistently(
0.33.0in sdk-aws-lambda vs0.31.9in sdk-config)..scope note
this is a twin defect to sdk-config's identical shape (dispatched separately). the general
rule both sdks drifted past:
that rule is written down nowhere today, which is why both sdks reached the same shape
independently.
.workaround in place downstream (not a fix)
every consumer has to rediscover this. the fix belongs upstream.
.refs
ahbode/svc-notifications:.agent/repo=.this/role=any/briefs/lesson.tsc-oom-from-multi-version-schema-lib.mdahbode/svc-notifications:.route/v2026_07_28.declapract.upgrade/PERF.tsc-inference-fix.ledger.mdjoiinstead ofzodβ it recurs perschema library until the peer-range convention is adopted