Skip to Content
ArchitectureDecision recordsADR 002: Dependency pins

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 children

The mechanism:

  1. nextra-theme-docsLayout destructures children out of its props and validates only the remainder against LayoutPropsSchema.
  2. LayoutPropsSchema is a z.strictObject that still declares children: reactNode as a required key.
  3. reactNode is built with z.custom(). On zod 4.1.x a missing key passed this check. On zod 4.4.x a missing key fails with expected 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-mermaid and mermaid are declared as direct dependencies even though the site never imports them itself. Nextra’s compiled MDX emits import { Mermaid } from '@theguild/remark-mermaid/mermaid', which pnpm’s strict node_modules layout will not resolve from a transitive dependency. Removing them from package.json breaks any page containing a Mermaid diagram.
  • Next 16 removed the eslint key from next.config. Linting is a separate gate step (pnpm lint, CI-3) and is deliberately not part of next build.
  • Dependency update automation (REQ-CI-4) must treat zod as 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.

Question? Give us feedbackDocuments Varaha Constellation Designer main (pre-release)
Last updated on