ADR 002: Toolchain and dependency pins
Status: Accepted Date: 2026-08-09 Supersedes: none Context requirement: REQ-T-1, REQ-T-2, REQ-T-6
Context
The documentation site is built on Nextra, mandated by organizational standard. Two version questions had to be settled before content authoring began, because resolving them later would mean revalidating every page.
DEC-1: which Next.js major
nextra@4.6.1 is the latest stable Nextra release and declares a peer
dependency of next: ">=14". At the time of scaffolding, Next.js was at
16.3.0. A permissive peer range is not evidence that the combination has been
exercised: Nextra 4 was authored against Next 14 and 15.
Two forces pulled in opposite directions. Consistency with the other Varaha
property, varaha.space.website, which already runs Next 16, argued for 16.
Risk aversion argued for 15.
DEC-2: which Node major
The build machine had only Node 25.6.0, which is not an LTS line. Vercel’s supported runtimes track LTS majors, so building locally on 25 would mean local and production never agree, violating REQ-T-6.
Decision
DEC-1: Next.js 16.3.0. Validated by producing a full production build plus a Pagefind index before any content was authored. The combination works.
DEC-2: Node 24.19.0, installed keg-only via Homebrew so it coexists with the
machine’s Node 25. Pinned in .nvmrc and in the engines field.
Transitive pin: zod@4.1.12, enforced through pnpm.overrides.
Consequences
The zod pin is load-bearing, not cosmetic
nextra@4.6.1 and nextra-theme-docs@4.6.1 both declare zod: ^4.1.12. Left
to resolve freely, pnpm selected zod@4.4.3. Under that version every page
fails to prerender with:
Invalid input: expected nonoptional, received undefined
-> at childrenThe mechanism:
nextra-theme-docs’Layoutdestructureschildrenout of its props and validates only the remainder againstLayoutPropsSchema.LayoutPropsSchemais az.strictObjectthat still declareschildren: reactNodeas a required key.reactNodeis built withz.custom(). On zod 4.1.x a missing key passed this check. On zod 4.4.x a missing key fails withexpected nonoptional.
So the schema has always been internally inconsistent; zod 4.4 simply began enforcing it. This is a Nextra defect that a caret range exposed.
Verified failing on 4.4.3. Verified passing on 4.1.12.
Do not remove this pin without first confirming that a Nextra release has
fixed LayoutPropsSchema, and then running a full production build. The failure
is total, not partial, so it cannot reach production unnoticed, but it will
block a build at the least convenient moment.
Other consequences
@theguild/remark-mermaidandmermaidare declared as direct dependencies even though the site never imports them itself. Nextra’s compiled MDX emitsimport { Mermaid } from '@theguild/remark-mermaid/mermaid', which pnpm’s strictnode_moduleslayout will not resolve from a transitive dependency. Removing them frompackage.jsonbreaks any page containing a Mermaid diagram.- Next 16 removed the
eslintkey fromnext.config. Linting is a separate gate step (pnpm lint, CI-3) and is deliberately not part ofnext build. - Dependency update automation (REQ-CI-4) must treat
zodas pinned and not silently widen it.
Alternatives considered
Fall back to Next 15.5.23. Rejected: the validation build on Next 16 succeeded, so the fallback bought nothing and would have diverged from the other Varaha property.
shamefully-hoist=true for the Mermaid resolution. Rejected: it masks every
future phantom-dependency problem rather than the one under investigation.
Declaring the two packages explicitly states the real relationship.
Patch nextra-theme-docs with a pnpm patch. Rejected for Phase 1: a version
pin is smaller, more legible, and easier to undo than a patch file that must be
rebased on every Nextra release.
main (pre-release)