039 — The clean audit that had had nothing to check

2026-09-02. Backlog item 2f-i, the next.js half. JOURNAL/038 left a queue in order: next.js first — 26 TS/TSX facts, the largest remaining pack, facts spread across five releases rather than clustered at one, and it already carried three of the five known replacement_available_from floors.

The pack is clean. All 28 facts, audited at their own introduced_in against six installed releases (16.0.0, 16.0.3, 16.1.5, 16.1.7, 16.2.0, 16.3.0), under both module resolutions. Zero defects in the data. All five declared floors confirmed real — they fail below the floor and pass at it. That is the first pack to survive the auditor unchanged, and it is the pack with the most floors to be wrong about.

The result is not the clean pack. It is that the audit was clean the first time too, before it was checking anything.


Twenty facts compiled, and no key was ever read

The first run reported 2 ok, 3 flagged, 6 unchecked at 16.0.0. The three flags were fragments missing their import line — revalidateTag, NextRequest, cookies are all real, and all live at subpaths the default --packages list never looked at. Naming them (next/cache, next/server, next/headers) turned all three green.

That left 2 ok becoming 5 ok and six facts unchecked, which looked like a coverage problem to tidy up. It was not. The six are the pack's next.config corrections, written the way a reader pastes them:

images: { qualities: [50, 75, 100] }

TypeScript parses that as a labelled statement — a label images, holding a block, holding a labelled statement qualities. It is syntactically valid. It compiles under --strict. It emits no diagnostic. And it compares nothing against anything, because there is no object literal there at all. Twenty of next.js's twenty-eight facts have that shape.

The coverage line had been saying so the whole time and nobody read it:

coverage: ... 0 option key(s) checked against a declared type ...

A verdict column of ok and UNCH is what gets read. The counters are what say whether either word meant anything. A green audit is not a check until you know what it checked.

The check we were charging models for and could not run on ourselves

--fragment-type <specifier>#<TypeName> re-wraps a fragment as

import type { NextConfig } from "next"; const __spi_fragment = { <snippet> } satisfies NextConfig;

which makes a misspelt key TS2353. Before writing a line of it, the wrap was proved by hand at 16.0.0: images: { qualities: [...] } passes, images: { maximumCacheSize: 1 } fails, a straightforwardly invented key fails.

That middle line is the point. images.maximumCacheSize is our own S1 finding — next.js/v2-a F1, an Opus 5 draw inventing a config key close enough to a real one to look right. It is the single most-charged failure mode in this dataset. And until this session it was the one class of defect our own packs could not be checked for, because the shape a config correction is written in is the shape the type-checker declines to look at.

Detection is deliberately narrow: every top-level statement is a labelled statement (a shape real code essentially never has), or the snippet does not parse as a program at all — and the wrap must itself parse before it is used, so a snippet that is merely broken is still reported as broken rather than silently reinterpreted into something that passes.

At 16.0.0 the pack went from 2 ok / 3 flagged / 6 unchecked to 10 ok / 0 flagged / 1 unchecked. The one remaining unchecked fact is LF4, parallel-route default.js, which is a file-layout claim with no code in it — correctly unchecked, and it says so.

Three more false positives, all in the tool

JSX is not identifier syntax. The free-name pass reported button, onClick, href and transitionTypes as APIs no package exports. All three of the pack's TSX facts were buried under six inventions that were nothing but JSX. An attribute name and a lower-case tag are not names a pack could export. An upper-case tag is, and still is reported.

A default export cannot be found by name. next/link exports default, not Link. LF24's entire claim — that transitionTypes is a real Link prop at 16.2.0 — could not be checked, because the one identifier that had to bind never could. The fix takes the binding from the specifier (the module's last path segment must equal the name, case-insensitively), never from the identifier, and the note discloses that it was inferred. With Link bound, LF24 verified — and poisoning the prop to transitionTypez produced TS2322 ... Did you mean 'transitionTypes'?, so the check is real.

The provenance header lied. Every run naming a subpath printed next/cache MISSING into its own record, because a subpath has no package.json. It now resolves through the owning package: next/cache 16.2.0 (via next). The audit record has to be exactly as trustworthy as the audit.

Every one of the four changes was proved able to fail before any pass was believed — the rule JOURNAL/038 paid for twice. A poisoned key, a poisoned prop, and a hand-written probe, each run before the corresponding green result was accepted.

A declared floor is an assertion, and a failure below it is a pass

Three facts flagged at their own introduced_in: LF28 at 16.0.3, LF21 and LF22 at 16.2.0. All three declare replacement_available_from. A fact that declares a floor is asserting that its code does not work at the release it is filed under, so flagging it there is not a defect report — it is the assertion coming true.

Reporting it as a bare FLAG threw that away. The auditor now says which floor was declared, that the failure confirms it, and which release to re-run at. Both floors were then re-run: at 16.3.0 LF21 and LF22 pass, at 16.2.0 LF28's experimental.prefetchInlining passes. Confirming a floor takes two runs — one below it and one at it — and only the second says anything about whether the code is right.

zod was re-audited as a regression check, and reproduced JOURNAL/038's LF16 finding identically, now carrying the floor annotation for the fix that session made. The --placeholders path, which this session's changes touched, is unaffected.

One limit, recorded rather than papered over

LF13's correction is const { slug } = await params — code from inside an async Server Component. To the audit file that is module-top-level await, which is TS1309 under nodenext and silent under bundler. That is a fact about the harness, not about the pack. Nothing in the repo checks a function-body fragment. It is written down as a known gap rather than suppressed, and rather than a third wrap mode invented on one example without evidence that the class is real.

What this changes about the queue

The remaining packs are valibot (11 ts), better-auth (7 ts) and tailwindcss (1 ts of 29, a CSS/HTML pack this tool cannot read). All three were expected to be cheap and low-yield. They are now worth more than they were this morning, for one reason: every pack in the repo has been audited by an instrument that could not see config fragments, and valibot and better-auth were last checked by hand. Re-running them is no longer a formality.

The deeper item is that prisma, zod and langchain were all audited before --fragment-type existed. Whether any of them has fragments of this shape is unknown and cheap to find out.