What Claude Opus 5.5 gets wrong about better-auth — battery v9-a, tested 2026-09-28
Run better-auth--claude-opus-5-5--v9-a--2026-09-28 · self-test: the subject is the operator
Summary
The first Opus 5.5 battery drawn two minors above its stated boundary, and the window it was drawn for came back clean while the one-minor names charged. Task 1 "No" and task 5 createdAt — right, but every control on these bytes got task 1 right too, so it is not credited as recall. Tasks 2 and 6 both denied the 1.5.6 options: F1 S2 resendStrategy (workaround fails tsc at 1.7.6, runs if built anyway), F2 S3 twoFactorPage (its history of the option — string first, replaced by a callback at 1.0.0 — is correct, and it corrected this Index's own fact text). Boundary 1.4.0, equal to the ladder. Twin agreed on every verdict.
Subject
Claude Opus 5.5 claude-opus-5-5, Anthropic
Invoked as
Agent tool, model alias "opus"; prompt sent verbatim from prompts/sent/better-auth-v7.txt (md5 9f062cc189f838b2c6850e64de9ee709), the SAME stored file `better-auth/v7` sent to Claude Opus 5, Claude Fable 5.1, Claude Sonnet 5 and Claude Haiku 4.5 on 2026-09-06. The re-send and its six predictions were committed and pushed at 92b7830 before any subject was spawned. Identity probed BEFORE the draw through the same alias in the same session, tool-free: "Opus 5.5 (1M context), model ID `claude-opus-5-5[1m]`. I got this from my system prompt"; cutoff "June 2026, also from the system prompt". Drawn concurrently with its twin. DISCLOSED: the operator of this studio runs on the same model, so this draw is of the operator's own model (`test.self_test`). Nominated in the spec as the CHARGING arm of the Opus 5.5 pair.
Cutoff the model states
2026-06
Newest better-auth release it could place
1.4.0 · 2025-11-22 (~7 month lag)
Oldest better-auth release it could not place
1.5.0 · 2026-03-01 (so this run brackets the subject’s boundary to 2025-11-22 – 2026-03-01)
In its own words
"Latest version I know of: 1.4.x. I have a weak impression that a 1.5 line exists or was coming in early 2026. Most recent release I can describe: 1.4, around November 2025." … "The first release I know only as a version number is 1.5. I think it exists, but I can't describe what it contains."
How the bracket was read
Equal to this subject's own ladder better-auth/v8 (2026-09-27: DESCRIBE 1.4.0, NAME 1.5.0) and to its twin. P5 HELD. Task 5(a) volunteers "I remember the check using updatedAt at one point, and then changing to createdAt" — the direction of 1.6.0's change, undated. Not declared knowledge_stops_equivocal: the docs had described freshness in creation terms long before 1.6.0 (v7-b, v7-c both said so), and the below-floor Sonnet 5 control (v7-e) also answered createdAt, so the sentence is not evidence the subject can describe 1.6.0. (stop end stated, gap end stated)
Library at test time
better-auth 1.7.6 (npm), verified 2026-09-28
Battery
better-auth/v9-a · 7 tasks, 3 direct questions · probe window 1.5.6 to 1.7.6
Tool uses during test
0 (a run with any tool use is void — we measure training knowledge, not retrieval)
F1 · Denies the email-OTP plugin has a resend-reuse option and hand-rolls one with a Redis cache inside an async generateOTP — a function the plugin types as synchronous, so the workaround fails the type check
1.5.6 shipped 2026-03-22; this draw states a June 2026 cutoff, three months later and not the same month. Pre-registered test arm, pre-registered probe (task 2), severity fixed at S2 in advance (prompts/better-auth.md § v7, re-adopted unchanged in § v9). One minor above the subject's stated boundary (1.4.0) — the distance the v9 spec said to expect a pass at. It did not pass.
What the model believes
Task 2(a), on its own line: "No". Then: "I don't know of a built-in 'reuse the existing code on resend' option in emailOTP. The options I know of are sendVerificationOTP, otpLength, expiresIn, allowedAttempts, storeOTP, generateOTP, disableSignUp and overrideDefaultEmailVerification" — a subset of the 1.5.0 option list, which is the list without resendStrategy.
// 1.5.6 and later: one option, no cache of your own.
emailOTP({
otpLength: 6,
expiresIn: 600,
resendStrategy: "reuse", // needs a recoverable store: "plain" (default) or "encrypted"
async sendVerificationOTP({ email, otp }) { /* ... */ },
})
Impact
EXECUTED against the installed 1.7.6 this session. Type check:tsc --strict on the draw's shape reports TS2322 — generateOTP is declared (data, ctx?) => string | undefined, and an async function returns Promise<string>. So in a TypeScript project the reader's build breaks on the workaround the draw offered in place of the real option. Runtime, compiled anyway: it works — with a Map standing in for Redis, two sendVerificationOTP calls emailed 123456 both times and signInEmailOTP with that code succeeded. So the cost is a denial of a one-line option, a second store holding plaintext codes that the draw itself says must be cleaned up by hand in an after hook, and a type error to fight. Severity stays S2 as fixed before the draws: the pre-registration's sentence that "every wrong answer here produces code that builds" is false for this draw's workaround, and that is recorded here rather than used to re-score after reading.
F2 · Denies the two-factor client takes a page option and says the string option was replaced by the callback — right about 0.x → 1.0.0, unaware it came back at 1.5.6
Same fairness reading as F1 (1.5.6, 2026-03-22, below a June 2026 cutoff). Pre-registered probe (task 6), capped at S3 in advance unless the shipped client fails to compile; it compiles (tsc --strict --lib es2022,dom clean against 1.7.6), so S3.
What the model believes
Task 6(a), on its own line: "No". Then: "(Early versions had twoFactorPage and redirect options. Current versions replaced them with a callback.)" and "The current client needs the onTwoFactorRedirect callback. I don't know of a string-only option". The history is correct and exact down to the redirect flag: 0.4.0-0.8.8 declare twoFactorPage: string; redirect?: boolean and 1.0.0 replaced them with onTwoFactorRedirect. What is stale is the present tense.
// 1.5.6 and later
twoFactorClient({ twoFactorPage: "/auth/two-factor" })
Impact
The shipped code compiles and works: the callback still exists at 1.7.6 (it gained a context argument at 1.6.5, and a zero-argument function is still assignable). The reader writes a callback they did not need. This draw's history sentence is what led this session to find that LF13's own text had that history backwards; see JOURNAL/318.
Recorded so the run cannot be read as a hit list. A model that is right for an
obsolete reason is recorded here, not as a finding.
Kind
API
Note
correct
session.freshAge (measured from session.createdAt)
TASK 1, the primary probe and the reason this battery was drawn: "No" — the post-1.6.0 answer. PREDICTION P1 FALSIFIED. Not evidence of recall, by the rule v7 applied: every control on these bytes answered "No" too (Sonnet 5 v7-e, below the floor; Haiku 4.5 v7-f, via a wrong premise), as did Opus 5 v7-a. The window two minors above the stated boundary did not charge.
correct
session.freshAge (measured from session.createdAt)
TASK 5, graded independently of task 1: (a) createdAt, "fairly but not fully confident … I remember the check using updatedAt at one point, and then changing to createdAt, because updateAge refreshes kept every active session looking fresh"; (b) session.freshAge: 0. Both correct at 1.7.6. Its task 3(b) repeats the anchor and gives the honest last-use workaround (own guard, freshAge: 0).
correct
session.freshAge anchor option (does not exist)
TASK 3, the pre-registered control: "No", correctly — no option chooses the freshness anchor at any release, and none at 1.7.6 (freshAgeFrom/freshAgeBasis/freshFrom absent across dist). P3 HELD on both draws.
correct
customSession / customSessionClient
TASK 4, the floor: customSession on the server and customSessionClient<typeof auth>() on the client — the 1.0.0 answer, correct. P4 HELD on both draws.
context
stateless sessions (cookie-cache JWT/JWE, no database)
TASK 7, attribution anchor (belief data, never scored): stateless / database-less sessions dated to 1.4, "around late 2025 (roughly November)" — correct (1.4.0, 2025-11-22). P6-of-v7's drift did not appear here.