---
library: zod
library-latest: "4.5.4"
library-latest-verified: 2026-09-01
model: claude-opus-5 (spawned via Agent model alias "opus")
model-self-reported-cutoff: 2026-05
model-believed-latest: "4.x, declines to name; 4.1.0 (2025-08-23) is the last release whose contents it attributes"
test-date: 2026-09-01
battery: zod/v2r-b (replicate of zod/v2, prompt unchanged; 10 tasks + 3 direct questions)
replicate-of: zod--claude-opus-5--v2--2026-08-29
tool-uses-during-test: 0
verified-against: https://registry.npmjs.org/zod · https://api.github.com/repos/colinhacks/zod/releases
status: open (no retest yet)
json: opus-5-v2r-b.json
self-test: true (the operator model is the subject; disclosed, weaker evidence)
---

# zod × Claude Opus 5 — replicate B of battery v2

Same boundary as its twin, to the release. Opposite answer about what the library actually does.

## What this draw said

> *"The newest Zod I can name is the **4.x** line; the most recent release whose **contents** I can
> actually describe is **Zod 4.1 (approximately August 2025)** — the release that introduced
> `z.codec()` and the built-in codec set."*

and, unprompted:

> *"Today is 2026-09-01, roughly a year past that 4.1 release and past my cutoff, so npm `latest`
> is very likely ahead of what I can describe — quite possibly 4.2+ or beyond. **Treat my version
> claim as a floor, not a fact.**"*

Bracket: **[2025-08-23, 2025-12-15)** — identical to [`v2r-a`](opus-5-v2r-a.md) and to
[`v2`](opus-5-v2.md). Three measurements, one answer, spread **0 days**, against a pre-registration
that predicted this arm would spread.

## The task where it broke from both

Task 9: what does `z.tuple([z.string(), z.number().default(0)]).parse(["a"])` return?

> *"**I believe this throws** rather than returning `["a", 0]` — a `too_small` array issue. The
> reason is ordering: the tuple's length check runs **before** any element is parsed, and
> `.default()` does not mark a position as optional for length purposes."*

That was true until **4.4.0 (2026-04-29)**, which made tuple defaults materialize. 4.4.0 precedes
this subject's stated 2026-05 cutoff, so the failure is chargeable. It is **not charged here** — a
replicate charges nothing — but it is flagged, because `zod/v2` answered this task **correctly** and
therefore carries no finding for it, and so did this draw's own twin. This is a scoring gap the
original run left on the floor, the same shape as the `better-auth/v1r` stateless-session miss in
JOURNAL/024. Queued in `BACKLOG.md` against a battery aimed at the 4.4.0 behavioural surface.

The draw was honest about it, which does not make it right: *"I'm confident about this for Zod 3 ...
For Zod 4 I'm moderately confident the same holds, but v4 did rework tuples around optional trailing
elements, so please verify."*

## The dissociation, seen a second time

The Index has now watched the self-report and the code come apart twice, in opposite directions:

| | Self-reports | Code / runtime claims |
|---|---|---|
| **prisma** (JOURNAL/024) | **204 days apart** | both draws wrote the same stale 6.x forms |
| **zod** (here) | **identical** | one draw right on 4.4.0 tuple defaults, one wrong |

Whatever the boundary date measures, it is not a summary of what the model will write. The
correction pack is what a developer consumes, and across all five of these draws it does not move.

## Findings: none, by design

Every one of `zod/v2`'s six findings reproduced and none is re-charged: no `z.fromJSONSchema()`
(F3), no `z.xor()` (F4), `"https:/example.com"` reported as parsing (F5), the `z.undefined()`
key-presence belief (F6), and *"the hard block was removed"* on refined-schema `.pick()`/`.extend()`
(F1/F2). Task 6 hand-rolled slugify rather than using `z.slugify()` (4.3.0).

Where it beat its twin: it dated Zod 4.0 stable to *"around July 2025"* against the registry's
**2025-07-09**, where `v2r-a` said May 2025 and is nine weeks early.

## An open question this draw created

It raised `z.interface()` — the Zod 4 beta syntax that separated key-optionality from
value-optionality, exactly what task 7 asks for — and then declined to build on it:

> *"I am **not confident** whether `z.interface()` survived into current stable 4.x or was
> removed/deprecated before release — I have conflicting recollections."*

The Index has never verified what became of `z.interface()`. That is cheap archaeology from the
4.0.0 release notes, and it would let a future battery **score** task 7's answer instead of watching
a subject hedge past it.
