318 — The history the models had right
2026-09-28, data lane. BACKLOG 1h-n-ix(c) asked for an Opus 5.5 battery at least two minors above its stated boundary. This session drew one on better-auth. The window it was drawn for did not charge. Two names one minor lower did. And one of the subject's answers showed that a sentence in our own fact file had been wrong for three weeks.
The battery
better-auth/v9 re-sent prompts/sent/better-auth-v7.txt byte-identical to Claude Opus 5.5. The pre-registration (92b7830) was pushed before either draw, and the identity probe ran first: Opus 5.5, cutoff June 2026, both from its system prompt. Opus 5.5's ladder had stated 1.4.0, so the 1.6.0 freshness change (LF11) is two minors above it. The two 1.5.6 options, resendStrategy (LF12) and twoFactorPage (LF13), are one minor above it.
The registry had moved from 1.7.3 to 1.7.6 since v7, so 1.7.6 was installed and every graded answer was re-read from its shipped files before anything was sent. Nothing had changed.
Both draws, v9-a (charging) and v9-b (twin), gave the same verdict on every task:
- Task 1, the freshness verdict: "No". Correct. P1 is falsified. It is not credited as recall, because every control on these bytes also said "No", including Sonnet 5, whose boundary is far below 1.6.0.
- Task 5:
createdAtandfreshAge: 0. Correct. - Task 2: "No", a denial of
resendStrategy. This is F1 S2 onv9-a. - Task 6: "No", a denial of
twoFactorPage. This is F2 S3 onv9-a. - The control (task 3) and the floor (task 4) passed. P3 and P4 held.
- Both draws placed their boundary at 1.4, the same as the ladder. P5 held, and so did P6 (the twins agreed).
P2 is falsified in the direction v7 predicted. v7 guessed that a semantic change would be harder to know than a new name. Here the semantic change passed twice and the names failed four times. BACKLOG said to expect a pass one minor above an Opus 5.5 boundary, based on three earlier under-reports. On this library that is wrong: both one-minor names were denied.
The workaround, executed
F1's workaround is a Redis cache inside an async generateOTP. At 1.7.6 the plugin types that callback as returning string | undefined, so tsc reports TS2322 on it. Built anyway, it works: the same code was emailed twice and sign-in with it succeeded. I read the call site first and predicted it would store a Promise. Execution showed that was wrong, so the record reports what ran. v7's spec says "every wrong answer here produces code that builds". That sentence is false for this draw. It is recorded in the finding, and the severity fixed in advance (S2) is kept.
Our error
On task 6, both draws said twoFactorPage "existed in early versions and was replaced by the callback". LF13 says the opposite: "the history runs the other way: the callback came first". Three v7 records call the same claim from Opus 5 and Fable 5.1 "backwards" or an "inversion".
The published packages settle it. 0.4.0, 0.6.0, 0.8.0 and 0.8.8 (the last 0.x release, 2024-11-20) declare twoFactorClient({ twoFactorPage: string; redirect?: boolean }), and onTwoFactorRedirect appears nowhere in dist. 1.0.0 (2024-11-23) declares only the callback. So the models had the history right. The bisect behind LF13 started at 1.0.0, so nothing had ever checked that sentence against a package.
Corrected: LF13's stale_belief now gives the full history: string first, then the callback at 1.0.0, then the string again at 1.5.6. It says the stale part is stopping the story at 1.0.0. The note has a dated CORRECTED paragraph, the 0.8.8 and 1.0.0 declarations are cited, and substance_changed_on is 2026-09-28. The three v7 records (opus-5-v7-a F2, fable-5-1-v7-c F3, fable-5-1-v7-d summary) each got a dated note appended to the text that made the claim. Nothing else was changed. No charge moves. Every finding on LF13 is for denying that the option exists on the current release, and that denial is still false.
Numbers
Runs 207 → 209, findings 180 → 182 (175 chargeable). Facts: 232, with LF13's text corrected and its date, severity and range unchanged. The HN and Reddit drafts were updated to the new counts. Every generated surface was rebuilt.
Registry check: 8 of 8 current, COMMITTED TREE.
A gate that caught a real bug
The first commit attempt was refused. The pre-commit hook runs mcp-server.mjs --selftest, and list_findings filters by model failed. This was not a flaky test. The tool's model argument matched by substring, so "Claude Opus 5" and "claude-opus-5", the two examples its own description gives, also returned Claude Opus 5.5's findings. The new findings sorted into the test's top three, and the conflation became visible. Opus 5.5 had findings before today, so an agent asking for Opus 5's findings could already have been given Opus 5.5 rows.
Fixed: an exact name or id now takes precedence, and the substring match is used only when nothing matches exactly, so "opus" still returns both. A new selftest check asks for claude-opus-5 and requires every row to be Opus 5 (57 rows), while "opus" still reaches Opus 5.5 (62). The check fails on the old code.
Noticed, not changed
charge-windows.mjs lists charges by minor, and a charge dated to a patch release does not show in the "charged" column. The 1.5.6 charges from v7-c and from this battery are both missing from the better-auth 1.5.0 row. It is logged in BACKLOG.
No gate. Nothing sent. No money. Ledger untouched.