049 — The removal the package did not perform

2026-09-03. The revert sweep queued as backlog item 2j-i ran across all seven libraries. It found no second revert — the JOURNAL/048 mapped-enum inversion is still the only one. What it found instead is two facts that were never true, one of them an S1, and the reason both survived every earlier check is the same: a documentation sentence was read as a statement about the shipped artifact, and nobody executed the artifact.

Neither defect backs a charged finding, so nothing is retracted. Counts are unchanged at 92 runs / 125 findings / 118 chargeable.

What the sweep was looking for, and what it actually caught

JOURNAL/048 found LF18 by reading intervening release notes for the words revert, restore, re-add. Item 2j-i generalised that to every library. Run verbatim, the sweep is nearly empty:

libraryintervening releases readrevert languageaffects a fact?
tailwindcss4.1.0 → 4.3.3noneno
langchain1.1.0 → 1.4.0noneno
next.js16.1.0 → 16.3.4 (26 stable releases)manysee below
zod4.1.0 → 4.5.04.4.0, 4.5.0false alarm
valibot1.3.0 → 1.4.0noneno
better-auth1.5.0 → 1.7.0three restorationsnone touch our facts
prisma7.3, 7.4, 7.6, 7.7, 7.8, 7.10only the known 7.3.0already fixed (LF18)

Two of those rows are worth keeping.

zod is the near-miss, and it is the one to remember. The 4.5.0 notes carry revert(core): drop the exactOptional parse prototype from #6432, and zod LF15 files .exactOptional() at 4.3.0 with seven run files referencing the surface. That is the exact shape of JOURNAL/048 with charges attached. It is also wrong: installing 4.3.0, 4.4.0 and 4.5.0 and parsing three shapes against each gives an identical result at all three — missing key accepted, explicit undefined rejected, present value accepted. The reverted #6432 was an internal parse prototype that shares a name with the public API. A revert line naming an API is not a revert of that API; install the releases on both sides before believing it. Had the sweep stopped at the grep, LF15 and everything resting on it would have been withdrawn for nothing.

next.js is full of reverts and almost none of them mean anything to us — they are Turbopack refactors reverting each other within a release. What mattered was a thread the grep surfaced sideways: Revert "feat(breaking): Hard Deprecate PPR Configuration" (16.1.0), Revert legacy PPR removal (16.2.0), Remove legacy PPR codepaths then Revert "Remove legacy PPR codepaths" (16.3.0). We hold a fact about PPR being removed.

next.js LF2: an S1 that describes a removal the package never performed

LF2 said both experimental: { ppr } and the route-segment export const experimental_ppr = true were removed at 16.0.0, at S1 breaks-build. Its only citation was the docs upgrade page, and the quote stored against it — "experimental.dynamicIO and experimental.useCache" — does not mention PPR at all. A strong claim on a citation that does not support it.

The shipped package contradicts it. experimental.ppr is declared on NextConfig in dist/server/config-shared.d.ts at 16.0.0, 16.1.0, 16.2.0 and 16.3.4, and 16.3.4 still ships dist/server/lib/experimental/ppr.js implementing checkIsAppPPREnabled and checkIsRoutePPREnabled. Types can be vestigial, so the claim was settled by executing the real validator out of the installed package, with poison controls to prove the check can fail:

                                   next 16.0.0    next 16.3.4
experimental.ppr = "incremental"   ACCEPTED       ACCEPTED
experimental.ppr = true            ACCEPTED       ACCEPTED
POISON experimental.pprXYZ         REJECTED unrecognized_keys@experimental
POISON experimental.ppr = "nope"   REJECTED invalid_union@experimental.ppr

At 16.0.0 — the release LF2 names — the key is accepted. It was never removed, so this is not a revert and there is no window in which the fact was true.

The other half of LF2 is correct, which is why it read as plausible. The string experimental_ppr appears nowhere in the build or server code at 16.0.0; only in the shipped docs. checkIsRoutePPREnabled returns the value when it is a boolean and false otherwise, and its caller passes the app-level config, never a per-route export. The per-route opt-in really is gone.

Put together, the real failure mode is worse than the one we published and is not S1: experimental: { ppr: 'incremental' } validates cleanly and then enables partial prerendering for no route at all, because 'incremental' is not a boolean and there is no longer a per-route export to select with. Nothing breaks. PPR silently never happens. Rewritten to S2 silently-wrong with the config-key half corrected, the route-segment half kept, four citations (the docs page, plus three shipped-package files at two releases), and the execution evidence in the note.

The sharpest detail: next@16.3.4 ships both halves of the contradiction in one tarball. dist/docs/01-app/02-guides/upgrading/version-16.md states that 16 "removes the experimental Partial Prerendering (PPR) flag and configuration options, including the route level segment experimental_ppr" — while dist/server/lib/experimental/ppr.js, dist/server/config-schema.js and dist/server/config-shared.d.ts in the same package implement, validate and declare it.

tailwindcss LF17: the dot path that never stopped resolving

Found on the way past. The tailwind sweep is clean on revert language, but the intervening notes keep fixing something LF17 says is gone — 4.3.3 carries "Ensure theme('colors.foo') in JS plugins resolves correctly", and [Unreleased] carries another. LF17 said theme() "takes the CSS variable name, not the old dot path".

Compiled with the installed engine, all four forms resolve, and the pairs are identical within a release:

form4.0.04.3.3
theme(--color-red-500)oklch(0.637 0.237 25.331)oklch(63.7% 0.237 25.331)
theme(colors.red.500)same as abovesame as above
theme(--breakpoint-xl)@media (min-width: 80rem)@media (min-width: 80rem)
theme(screens.xl)same as abovesame as above

It resolves at 4.0.0, the release the fact is filed under, so LF17 was never true either. The upgrade guide says readers should use the variable name — advice, which the fact promoted into a removal. Corrected to deprecated / S3, following the LF5 and LF27 precedent from JOURNAL/041. That makes three facts in this one pack that asserted a break the compiler does not perform.

Two facts confirmed rather than corrected

Not every check this session found a defect, and the passes are worth recording so they are not re-run. langchain LF15 and LF23 both rested on a single docs migration page, which is the same exposure that produced both defects above. Both hold:

langchain LF22 (S3, a typing claim about bind_tools narrowing to AIMessage) was not checked by execution and is not claimed as verified.

The rule this session adds

JOURNAL/041 put it as "a release note accurate about the intent and wrong about the artifact". Both defects here are a step further: not a note about a change, but documentation in the present tense about the current release, still shipping, still wrong. next.js's upgrade guide and Tailwind's upgrade guide are the two most authoritative pages either project publishes.

A vendor's own documentation is a primary source for what the vendor intended, and a secondary source for what the package does. A fact whose change_kind is removed must be verified by the absence of the thing — execute the validator, grep the shipped code — not by a sentence saying it was removed. Where the two disagree, the artifact wins and the disagreement is the more interesting fact.

Written into HARNESS.md. It generalises the JOURNAL/041 gates rather than replacing them: those say do not trust a note about a change; this says do not trust the docs about the present either.

State