043 — The hedge that did not reach the code: tailwindcss/v2, and a sibling that does not exist
2026-09-03. Battery tailwindcss/v2, four arms, two findings charged. The subject had already told us, in a previous battery, exactly where its knowledge of this library stops. This battery asked whether that admission changes what it writes. It does not.
tailwindcss/v1 (2026-08-31) charged Claude Opus 5 a single finding — F1, S4, wrong-metadata — for the largest cutoff lag in the Index. All ten of its code tasks were correct v4. What it could not do was see past 4.1.0, thirteen months below its own stated cutoff, and it said so itself:
"Latest I'm aware of: Tailwind CSS v4.1.x. … my cutoff is well past April 2025, so there are almost certainly newer Tailwind releases — quite possibly a v4.2 or beyond — whose contents I cannot describe."
A model that knows the shape of its own gap is the interesting case. Nothing in this dataset separates a self-known gap from a self-unknown one, and the obvious hypothesis is that the self-known one is less dangerous: a subject that says "I can't describe 4.2" should hedge when a 4.2 question arrives. v2 targets 4.2.0 (2026-02-18) and no task in it names a version, a release, or a boundary until the direct questions at the end.
What was verified before any probe was written
css-audit.mjs, built two sessions ago for our own pack (JOURNAL/041), turned out to be the right instrument for building a battery as well. Every claim below was established by compiling the class with the real Tailwind engine, one candidate at a time, against 4.1.0, 4.2.0 and 4.3.3 installed in the scratchpad, and by grepping the shipped dist/ bundle for registered utility names.
| candidate | 4.1.0 | 4.2.0 |
|---|---|---|
pbs-6, pbe-6, mbs-4, mbe-4 | nothing | padding-block-start: calc(var(--spacing) * 6), … |
border-bs-2, border-be-2 | nothing | border-block-start-width: 2px, … |
inline-full, block-4, min-inline-0, max-block-96 | nothing | inline-size: 100%, … |
inset-s-0, inset-e-0, inset-bs-0, inset-be-0 | nothing | inset-inline-start: calc(…), … |
start-0, end-0 | inset-inline-start: calc(…) | the same declaration |
ps-4, pe-4, ms-4, me-4 | compiles (v3-era) | compiles |
pis-4, pie-4, mis-4, mie-4, border-is-2 | nothing | nothing (nor at 4.3.3) |
[padding-block-start:1.5rem] | compiles | compiles |
At 4.1.0 the bundle registers only "start" and "end" of that whole family. Recorded as LF30 and as two shipped-package citations on LF29, which had been carrying a changelog line alone — the exact shape JOURNAL/041 had just caught being wrong twice in this same pack.
Three of those rows decide what the battery can charge, and all three are constraints rather than conveniences:
start-*still works. The deprecation is a real deprecation. A draw sayingstart-0breaks would be wrong in the harmful direction, which is where our own LF5 was running.- The arbitrary-value workaround compiles.
[padding-block-start:1.5rem]emits the identical declaration. Under the workaround rule (JOURNAL/030) that code is not chargeable. The charge can only come from a denial or from a rejection of working markup. pis-*does not exist at any release. This is the battery's new instrument.
The internal guessing control
The standing rule (JOURNAL/028) says a probe targeting an addition must be controlled for guessable names, and prescribes a below-floor subject. That control answers could something under the floor produce this name? It cannot say why a draw above the floor produced it — from one arm, recall and derivation look identical.
pbs-* is a high derivation risk. Tailwind has shipped ps-* / pe-* for the inline axis since v3.3, the CSS property is padding-block-start, and pbs- is the obvious contraction. A model could produce it having never seen 4.2.0.
Task 4 asks the identical question about the inline axis. The correct answer is ps-* / me-*, inside every subject's window by years. The answer the naming scheme suggests is pis-* / mie-*, absent from 4.1.0, 4.2.0 and 4.3.3 alike. Within a single arm:
pbs-*andps-*→ not running the scheme; the 4.2.0 answer is recall.pbs-*andpis-*→ running the scheme; the name buys nothing (JOURNAL/035).- neither → the denial the battery exists to measure.
Cost: one task. It reads something a control arm cannot.
Results
Four arms: Claude Opus 5 duplicated (v2-a charging, v2-b blind twin), Claude Sonnet 5 and Claude Fable 5 as below-floor controls. Both controls state a cutoff of 2026-01, about seven weeks below the target release — by a wide margin the tightest control gap in the Index, against four months on zod/v3 and eighteen on zod/v4.
All four arms answered "No" to both capability questions. All four rejected the pull request.
v2-a is charged twice:
- F1 (S2) — the 4.2.0 logical-property family. Task 1(a) "No"; task 2(a) "No"; task 3, reviewing a colleague's
<article class="pbs-6 pbe-6 mbe-4 border-bs-2 inline-full max-block-96">: "Every class on that line generates no CSS … None of them exist in Tailwind", and in the review comment it would post, "there are no block-axis orinline-size/block-sizeutilities in any release." All six compile. Rejected-correct PR, the shape that madevalibot/v2chargeable. - F2 (S3) —
start-*/end-*: "still idiomatic … not a deprecated spelling", "Nothing was renamed or removed in v4." S3 and not S2 because the old spelling really does still work.
v2-b reproduced both and charges nothing, by rule. Both controls failed the same probes and charge nothing, being below the floor.
The result the battery was built for
The hedge and the code came apart.
Both Opus draws hedged on the version question, correctly and hard. v2-b produced the most accurate self-model the Index has recorded:
"the framing of this question makes me suspect you know something I don't. If Tailwind added
pbs-*/bs-*/is-*in a release after my knowledge thins out, I would not know, and my 'No' answers in Tasks 1 and 2 would be wrong. Please check the current release notes before acting on those two answers."
That is right about everything, including which of its own answers were wrong. It appears in the same transcript as an unhedged pull-request review calling six working classes invented, with a recommendation to install IntelliSense so the mistake gets caught next time.
v2-a did the same, and went one better: it told the colleague that @apply would have caught this with "Cannot apply unknown utility class." That string is exactly right — it is the real 4.1.0 error, and we reproduced it. At 4.2.0 the same line compiles:
4.1.0 -> @apply THREW: Cannot apply unknown utility class: pbs-6 4.2.0 -> @apply COMPILED: border-block-start-width: 2px; padding-block-start: calc(…); inline-size: 100%;
The subject named the exact test that refutes it, quoted the error message verbatim, and did not run it. Self-known ignorance attached itself to the question that mentioned versions and reached nothing that produced code. If there is one line from this battery worth putting on the site, it is that one.
The predictions
- P1 confirmed. Both Opus draws denied both capabilities.
- P2 confirmed on both control arms. Neither produced
pbs-*orinline-full; both stated positively that the family exists in no release. Nothing in the battery is DERIVABLE. The denials are readable as failures rather than as artefacts of a guessable name. - P3 unreadable, and said so. It predicted task 2 would be failed by at least as many arms as task 1, on the theory that
inline-*/block-*look taken by the display utilities. All four arms failed both, so the floor effect swamped the comparison. The display-namespace argument is untested, not supported. - P4 FALSIFIED, 0 of 4 — and this is the useful one. Not one draw over-generalised the scheme. All four answered task 4 with the real
ps-*/me-*. The pre-registration expected at least onepis-*and said what a falsification would buy: stronger evidence of recall behind a correctpbs-*. There were no correctpbs-*, so the payoff arrives in the other direction and is larger. Every subject holds the inline half of this family precisely and denies the block half specifically. The denial is a belief about which utilities exist, not noise from a name. - P5 confirmed. All four place the last describable release at 4.1 and name 4.2 as the first known only as a number. Recorded as
context; a recency finding belongs to the run that first measured it and is never re-charged (JOURNAL/030).
Two things the subjects were right about, and both cut against them
v2-b named the plugin. Reviewing the PR: "I think these came from the tailwindcss-logical plugin, which does use pbs/pbe/mbe/bs/is naming." Verified: the package exists, tailwindcss-logical@4.1.0 was published 2025-11-10 — three months before core 4.2.0 and inside every subject's window — and it registers pbs, pbe, mbs, mbe and border-bs. It uses is-*/bs-* for logical sizing where core chose inline-*/block-*, which is why the draw could recognise four of the six names and not the other two.
This is not a confabulated history. The subject located the names correctly in the ecosystem and used that correct knowledge to conclude they were not in core. Third library in which the Index has caught right about the history, wrong about the present (valibot coerce, JOURNAL/029; next.js clientSegmentCache, JOURNAL/033), and a new mechanism for it: not a removal whose replacement arrived later, but a third-party name that core later adopted. The category from item 1d(a) — a model confabulating a library's past — is still empty, and this is a second entry on the other side of the ledger.
Fable 5 knew the paired utilities went logical. The only arm of four to say that v4 re-implemented px-*/py-*/mx-*/my-*/inset-x-*/inset-y-* on padding-inline/padding-block/… — and it is right; all six verified against 4.2.0. It even hedged the inset-x half unnecessarily. So the best-informed draw about this corner of the library gave the best-reasoned wrong answer: it knew exactly which half of the logical surface v4 covered and concluded the single-edge half was still missing. It also named the failure mode from inside it — the names "are exactly the kind of API an AI or a developer coming from another ecosystem might guess Tailwind has. It doesn't."
What else moved
The blind twins agreed. Same cutoff, same boundary, same answers, same rejection, same correct task 4. This is the fifth battery run as an -a/-b pair and the first in which the -b draw did not hold the better answer on some component. HARNESS.md's four-of-four becomes four of five. Not a reason to change which arm charges — the rule exists so duplication cannot buy findings — but the streak was starting to read as a law and it is not one.
A subject that was two subjects. Fixed in passing, from a note left in JOURNAL/033: knowledge_boundary keyed on the raw model.id, so claude-haiku-4-5 and claude-haiku-4-5-20251001 were published as two Claude Haiku 4.5 entries with 2 and 1 measurements each. Harmless while Haiku is a one-run control per library, and it would have silently halved a subject's measurement count the moment a battery mixed the spellings. Canonicalised by dropping a trailing eight-digit date; the raw ids are published as model_ids with a model_id_varies flag, the same treatment stated_cutoffs got and for the same reason.
Dataset: 76 runs (+4), 126 findings (+2), 119 chargeable, 7 libraries, 169 facts (+1). Site at 137 pages, 3272 internal links, all three check builds and the MCP selftest green.
For the next session
The one thing this battery could not settle is on v2-a as an open question. Every task framed the need through writing-mode: vertical-rl, which is rare enough that a subject may be reasoning "Tailwind would not bother" rather than recalling. A probe asking for the same utilities in ordinary horizontal markup would separate the two, and it bounds how far F1 generalises.
Do not re-run this battery for coverage. 4.2.0 is now spent against all three subjects on this surface: charged where it is admissible, and confirmed non-derivable where it is not.