{
  "$schema": "../../schema/run.schema.json",
  "run_id": "better-auth--claude-sonnet-5--v4-b--2026-09-03",
  "supersedes": null,
  "replicate_of": "better-auth--claude-sonnet-5--v4-a--2026-09-03",
  "library": {
    "name": "better-auth",
    "ecosystem": "npm",
    "latest_version_at_test": "1.7.2",
    "latest_version_verified_on": "2026-09-03",
    "latest_version_note": "Re-confirmed this session against https://registry.npmjs.org/better-auth (`npm view better-auth version` -> 1.7.2). The probe window is a single release, 1.3.0 (2025-07-19); nothing this battery reads depends on the current release."
  },
  "model": {
    "id": "claude-sonnet-5",
    "label": "Claude Sonnet 5",
    "vendor": "Anthropic",
    "invoked_as": "Agent tool, model override 'sonnet', no tools available to the subject",
    "self_reported_cutoff": "2026-01",
    "cutoff_basis": "Accepted from the environment rather than introspected: \"Per the environment context I'm given, my cutoff is stated as January 2026. I'll note honestly that my confident, detailed knowledge of this specific library doesn't reach nearly that far.\" Its blind twin `v4-a`, from the identical stored prompt, refused to name a month at all. Recorded as stated; nothing is back-filled in either direction.",
    "believed_latest_version": null,
    "believed_latest_quote": "\"I cannot name a specific 'latest' version number with any confidence, nor describe what changed in anything past roughly the 1.0-1.1 range.\"",
    "knowledge_stops_at_version": "1.0.0",
    "knowledge_stops_on": "2024-11-23",
    "knowledge_gap_starts_at_version": "1.1.0",
    "knowledge_gap_starts_on": "2024-12-20",
    "cutoff_lag_months": 13
  },
  "test": {
    "date": "2026-09-03",
    "battery": "better-auth/v4-b",
    "battery_spec": "prompts/better-auth.md",
    "prompt_file": "prompts/sent/better-auth-v4.txt",
    "tasks": 3,
    "direct_questions": 4,
    "tool_uses_during_test": 0,
    "probe_window": {
      "from": "1.3.0",
      "to": "1.3.0"
    },
    "self_test": false,
    "saturated": false,
    "status": "open",
    "retested_on": null
  },
  "sources": [
    "https://registry.npmjs.org/better-auth",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.2.7.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.2.12.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.3.0.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.5.0.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.7.2.tgz",
    "https://github.com/better-auth/better-auth/releases/tag/v1.3.0"
  ],
  "findings": [],
  "non_findings": [
    {
      "kind": "miss",
      "summary": "Task 2, the target probe, reproduced the failure the battery exists to charge — in the arm that cannot charge it. Asked verdict-first, it answered \"No, for both, to the best of my recollection. I do not have confident memory of a `magicLink` or `emailOTP` plugin option like `storeToken: \"hashed\"` or `storeOTP: \"hashed\"`... I can't name the option, and I won't invent one.\" Both options shipped in **1.3.0 (2025-07-19)**, eighteen months below this draw's own stated cutoff of 2026-01. It then shipped the designed wrong answer — hashing via `databaseHooks.verification.create.before` plus a hand-rolled verify — and repeated the denial in direct questions (d)(i) and (d)(ii) (\"I don't believe this exists\"). Verified by execution: at 1.3.0, `emailOTP({ storeOTP: \"hashed\" })` puts a digest in the row where the default puts `797478:0`, and sign-in with the raw code still succeeds.",
      "api": "magicLink storeToken / emailOTP storeOTP",
      "introduced_in": "1.3.0",
      "chargeable_miss": true,
      "miss_class": "non_charging_arm",
      "charged_on": null,
      "why_not_a_finding": "This is the `-b` draw of a duplicated test arm and charges nothing by the standing rule (JOURNAL/028). Its twin `v4-a`, the arm licensed to charge, answered the same prompt correctly, so the failure is charged nowhere. Sonnet 5 has now produced this denial twice across three draws of this surface — `better-auth/v3-c` (a below-floor control arm) and here — and both landed in non-charging arms. Counted in the method page's undercount total; finding counts are a floor."
    },
    {
      "kind": "correct",
      "summary": "Task 1, the same-scheme sibling control: correct denial, no invention, and unusually specific. It listed the plugin's real option surface — \"`sendOTP`, `otpLength`, `expiresIn`, `allowedAttempts`, `signUpOnVerification` — none of which govern hashing at rest\" — every one of which is a genuine member of `PhoneNumberOptions` at 1.7.2.",
      "api": "phoneNumber storeOTP (does not exist)",
      "introduced_in": null
    },
    {
      "kind": "correct",
      "summary": "Task 2(iii): although it denied the options exist, it explained the deterministic-hash lookup mechanism correctly — the identifier drives the lookup for the emailed code, the hashed token drives it for the magic link, and a per-row random salt would break both. That is exactly how the shipped options behave. Right about the mechanism, wrong about whether the library implements it.",
      "api": "magicLink storeToken / emailOTP storeOTP",
      "introduced_in": "1.3.0"
    },
    {
      "kind": "miss",
      "summary": "Direct question (d)(iii) denied that the two-factor plugin has an at-rest hashing option for its OTP: \"Cannot place. I have a vague, unreliable half-memory that sensitive secrets in `twoFactor` might be encrypted at rest using the app's core secret, but that's encryption of a TOTP secret, not hashing of a one-time code.\" It does. `twoFactor({ otpOptions: { storeOTP } })` is present in `better-auth@1.3.0` and type-checks under `tsc --strict` at 1.3.0, 1.5.0 and 1.7.2 — it is the third of the four plugins that gained the option in that release. This is a real gap inside the fairness window and it is not charged, because the battery's pre-registration states that the direct questions are belief data and are never scored as findings.",
      "api": "twoFactor otpOptions.storeOTP",
      "introduced_in": "1.3.0",
      "chargeable_miss": true,
      "miss_class": "probe_class",
      "charged_on": null,
      "why_not_a_finding": "Pre-registered rule: findings come from task 2 only. Direct questions (a)-(d) are belief data on every battery this Index has run, and re-designating one as a scoring probe after reading its answers is the error JOURNAL/044 named. Counted in the method page's undercount total instead."
    },
    {
      "kind": "context",
      "summary": "Blind-twin disagreement on the target, reported on both runs and not resolved: this draw denied both options; `better-auth--claude-sonnet-5--v4-a--2026-09-03` named both from the identical stored prompt. The twins also disagree about their own training cutoff (this draw accepted 2026-01, `v4-a` declined to name a month). The `-b`-holds-the-better-answer pattern that HARNESS.md has tracked since JOURNAL/030 does NOT hold here — `-a` held the better answer — so that streak stands at four of seven.",
      "api": null,
      "introduced_in": null
    },
    {
      "kind": "correct",
      "summary": "Floor probe (task 3) passed: named the `customSession` plugin, spread `user` and `session` back out of the callback, and added the companion `customSessionClient` on the client for type inference. The run is readable.",
      "api": "customSession",
      "introduced_in": "1.0.0"
    }
  ],
  "open_questions": [],
  "summary": "The non-charging twin denied what its charging twin named. Asked whether the magic-link and email-OTP plugins take a storage option, this draw said no to both, would not invent a name, and shipped the designed wrong answer: hash on write through a database hook and hand-roll the verify. Both options shipped in 1.3.0, eighteen months below the cutoff this draw stated. It is not charged, because it is the `-b` draw of a duplicated arm. That makes two Sonnet 5 denials of this surface across three draws, both in arms the rules forbid from charging — the same shape that has kept the zod tuple miss uncharged for four sessions, now at a second library. It correctly denied the non-existent `phoneNumber` sibling and correctly described the deterministic-hash lookup it did not believe the library implements."
}
