---
library: next.js
library-latest: "16.3.3"
library-latest-verified: 2026-08-31
model: claude-sonnet-5 (spawned via Agent model alias "sonnet")
model-self-reported-cutoff: 2026-01
model-believed-latest: "Next.js 15 GA, ~October 2024 — declines to name a current version at all"
test-date: 2026-08-31
battery: next.js/v1 (10 idiomatic tasks + 3 direct questions; probes restricted to 16.0.0, published 2025-10-22)
tool-uses-during-test: 0
verified-against: https://nextjs.org/blog/next-16 · https://nextjs.org/docs/app/guides/upgrading/version-16 · https://api.github.com/repos/vercel/next.js/releases
status: open (no retest yet)
json: sonnet-5.json
---

# Next.js × Claude Sonnet 5 — battery v1 findings

The widest gap this dataset has recorded. **Next.js 16.0.0 shipped on 2025-10-22. This model's
stated training cutoff is 2026-01. It cannot describe a single thing in that release.**

Its Next.js knowledge stops at 15.0.0, which shipped 2024-10-21 — fifteen months before the
cutoff it reports. Seven findings, all chargeable, three of them S1.

What makes the run worth reading is that the model is not overconfident. It flags uncertainty on
almost every wrong answer, refuses to name a latest version, and tells you to check the docs. It
is honest and it is fifteen months out of date, and those two facts do not cancel out: the code
it hands you still does not build.

## F1 · S3 deprecated · generates `middleware.ts`

```ts
// middleware.ts
export function middleware(request: NextRequest) { ... }
export const config = { matcher: ['/dashboard/:path*'] };
```

Next.js 16 renamed the convention to `proxy.ts` with a `proxy` export. Asked directly at question
(c), the model said it has "no knowledge of it being renamed or deprecated."

> "`middleware.ts` filename | Rename to `proxy.ts` to clarify network boundary and routing focus"
> — Next.js 16 release announcement, Deprecations table, 2025-10-21

The old file still works and warns, so this is S3 rather than S1. Two things the model cannot warn
you about: removal is planned, and the `proxy` runtime is nodejs and cannot be configured, which
matters if you were relying on the edge runtime.

## F2 · S1 breaks-build · single-argument `revalidateTag`

```ts
revalidateTag('products');
```

> "revalidateTag now requires a second argument specifying a cacheLife profile. The single-argument
> form is deprecated and will produce a TypeScript error."
> — Upgrading: Version 16

The prompt specified a TypeScript app, and `next build` type-checks by default, so this fails the
build. Correct forms:

```ts
revalidateTag('products', 'max');   // stale-while-revalidate
updateTag('products');              // read-your-writes, Server Actions only
```

**All three subjects generated this identical line.** It is the single most reliable finding in
the battery and it leads the correction pack.

## F3 · S1 breaks-build · `experimental.ppr` and `experimental_ppr`

```js
// next.config.js
experimental: { ppr: 'incremental' }
// app/page.tsx
export const experimental_ppr = true;
```

Both were **removed** in 16.0.0; the replacement is top-level `cacheComponents: true`. The Suspense
structure the model wrapped around the dynamic subtree is correct — the configuration that turns
the feature on is dead.

> "`experimental.ppr` flag | PPR flag removed; evolving into Cache Components programming model"
> — Next.js 16 release announcement, Removals table

## F4 · S1 breaks-build · `"lint": "next lint"`

Generated with no caveat, in a scripts block presented as current for a new app.

> "`next lint` command | Use Biome or ESLint directly; `next build` no longer runs linting."
> — Next.js 16 release announcement, Removals table

`npm run lint` exits non-zero on an unknown command. The second half of that quote is the quieter
problem: a project that relied on `next build` to lint stops linting and says nothing.

## F5 · S4 wrong-metadata · "next build uses Webpack by default"

> "Bundler used by `next build`: Webpack, by default ... I can't confirm with confidence whether
> that has since flipped."

It flipped in 16.0.0. Turbopack is the default for dev and build; `next build --webpack` is the
opt-out. The unflagged consequence: a project with a custom webpack config now **fails the build**
unless it opts out, and the model does not warn about that because it does not know the default
changed.

## F6 · S2 silently-wrong · image cache TTL of 60 seconds

This is the most interesting failure in the run, because the model very nearly gets there:

> "I also have a weaker, less certain memory of seeing this default raised substantially (something
> on the order of hours) in a more recent release ... I don't trust that second memory enough to
> state a specific new number as fact. So: my confident answer is 60 seconds."

The true default is 4 hours — 14400 seconds — as of 16.0.0. The model holds a fragment of the
correct answer, correctly identifies it as more recent, and then discards it in favour of the
stale one. Someone debugging why an updated upstream image will not refresh now has a wrong
constant to reason from.

## F7 · S4 wrong-metadata · knowledge stops at 15.0.0

> "The most recent release whose actual contents I can describe with real confidence is Next.js 15
> (general availability around October 2024)."

15.0.0 shipped 2024-10-21. The first release it cannot describe is 15.1.0, 2024-12-10 — thirteen
months before its stated cutoff, which is what this finding is anchored to. Every other finding
here follows from that gap.

Recorded in its favour: refusing to name a specific "latest version" is the correct calibration
move, and it made it unprompted.

## Not findings

- **Task 3, OG image** — wrote `params: Promise<{ slug }>` and awaited it. Correct for 16.0.0,
  which made metadata-image-route params a Promise. Attributed to 15, but the code is right.
- **Task 7** — `images.remotePatterns`, not the deprecated `images.domains`. Correct.
- **Task 9** — included `@panel/default.tsx`, so the layout builds. The stated reason is the pre-16
  one (404 on hard reload) rather than the current one (the build fails without it), but the file
  is there.
- **Task 5** — `unstable_cache`. Still functional on 16.x, so this is a miss, not a finding: it
  does not know `cacheLife` and `cacheTag` lost their `unstable_` prefix.
- **Task 8, image quality** — said 90 is served, but surfaced the `images.qualities` allowlist,
  flagged its own uncertainty, and told the user to set `qualities: [90]`, which is the correct
  fix. Not charged, under the code-vs-claim rule introduced with this battery: *generated code
  that fails on the current version is always a finding; a hedged prose claim that names the
  correct fix is an imprecision.*
