{
  "$schema": "../../schema/run.schema.json",
  "run_id": "better-auth--claude-fable-5-1--v7-d--2026-09-06",
  "supersedes": null,
  "replicate_of": null,
  "library": {
    "name": "better-auth",
    "ecosystem": "npm",
    "latest_version_at_test": "1.7.3",
    "latest_version_verified_on": "2026-09-06",
    "latest_version_note": "Read from https://registry.npmjs.org/better-auth this session (`dist-tags.latest` -> 1.7.3). Every claim this battery charges was re-checked against the installed 1.7.3 package as well as against 1.6.0, so the corrections are about what is true now and not only about what changed at the target release."
  },
  "model": {
    "id": "claude-fable-5-1",
    "label": "Claude Fable 5.1",
    "vendor": "Anthropic",
    "invoked_as": "Agent tool, model alias \"fable\"; the blind twin of v7-c, identical stored prompt, no tools available to the subject",
    "self_reported_cutoff": "2026-06",
    "cutoff_basis": "\"I'm told my training cutoff is June 2026, but my dependable knowledge of this particular library thins out around late 2025 — treat anything I say about releases after 1.3 as uncertain.\" Same date as its twin, same kind of density caveat. This arm charges nothing regardless, by the duplicated-arm rule.",
    "believed_latest_version": "1.4–1.5",
    "believed_latest_quote": "\"The latest version I'm aware of existing is somewhere in the 1.4–1.5 range. The most recent release whose contents I can actually describe with reasonable confidence is **1.3** (around July 2025). I have partial, low-confidence recollection of 1.4 (roughly November 2025).\"",
    "knowledge_stops_at_version": "1.3.0",
    "knowledge_stops_on": "2025-07-19",
    "knowledge_gap_starts_at_version": "1.4.0",
    "knowledge_gap_starts_on": "2025-11-22",
    "cutoff_lag_months": 11
  },
  "test": {
    "date": "2026-09-06",
    "battery": "better-auth/v7-d",
    "battery_spec": "prompts/better-auth.md",
    "prompt_file": "prompts/sent/better-auth-v7.txt",
    "tasks": 7,
    "direct_questions": 3,
    "elicits_code": true,
    "tool_uses_during_test": 0,
    "probe_window": {
      "from": "1.6.0",
      "to": "1.7.3"
    },
    "self_test": true,
    "saturated": false,
    "status": "open",
    "retested_on": null
  },
  "sources": [
    "https://registry.npmjs.org/better-auth",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.5.0.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.6.0.tgz",
    "https://registry.npmjs.org/better-auth/-/better-auth-1.7.3.tgz",
    "https://github.com/better-auth/better-auth/releases/tag/v1.6.0"
  ],
  "findings": [],
  "non_findings": [
    {
      "kind": "miss",
      "summary": "Task 1: \"yes\", reproducing its twin exactly, and consistent with its own task 5(a) answer of `updatedAt`. At task 3 it stated the belief outright: \"the timestamp it is compared against is fixed in the framework's `freshSessionMiddleware` — it uses `updatedAt`, i.e. the product team's semantics. Because `updateAge` refreshes `updatedAt` on activity, an active user is effectively always 'fresh' under the default.\" That is an accurate description of better-auth up to 1.5.0. Both Fable 5.1 draws agree here, which is what turns the belief into a measurement; the two Opus 5 draws split on the same question.",
      "api": "session.freshAge (measured from session.createdAt)",
      "introduced_in": "1.6.0",
      "chargeable_miss": true,
      "miss_class": "non_charging_arm",
      "charged_on": "better-auth--claude-fable-5-1--v7-c--2026-09-06",
      "why_not_a_finding": "Charged on the `-c` twin as F1. The `-d` draw of a duplicated arm carries the failure and no F-number."
    },
    {
      "kind": "miss",
      "summary": "Task 2: \"no\", with the absence claimed explicitly (\"Nothing among them says 'reuse the outstanding code on resend'\") after listing the 1.5.0 option set from memory. Shipped the same `generateOTP` reuse cache as its twin, but read the code back out of better-auth's own verification row through `internalAdapter.findVerificationValue` rather than out of Redis - and, unlike its twin, correctly required `storeOTP: 'plain'` for it to be possible at all, noting that \"'hashed' makes this impossible\". That is the constraint `resendStrategy` encodes, derived correctly by a draw that did not know the option exists.",
      "api": "emailOTP({ resendStrategy })",
      "introduced_in": "1.6.0",
      "chargeable_miss": true,
      "miss_class": "non_charging_arm",
      "charged_on": "better-auth--claude-fable-5-1--v7-c--2026-09-06",
      "why_not_a_finding": "Charged on the `-c` twin as F2."
    },
    {
      "kind": "miss",
      "summary": "Task 6: \"no\", with the same inverted history as `v7-c` and `v7-a`: \"Early 0.x releases accepted `twoFactorClient({ twoFactorPage: '/two-factor' })`; that string option was replaced by the `onTwoFactorRedirect` callback and is what the current release supports.\" This draw states it flatly rather than hedging it. Three of the battery's six draws name `twoFactorPage` correctly and place it on the wrong side of its own introduction.",
      "api": "twoFactorClient({ twoFactorPage })",
      "introduced_in": "1.6.0",
      "chargeable_miss": true,
      "miss_class": "non_charging_arm",
      "charged_on": "better-auth--claude-fable-5-1--v7-c--2026-09-06",
      "why_not_a_finding": "Charged on the `-c` twin as F3."
    },
    {
      "kind": "correct",
      "summary": "Task 3, the control: \"no\". Correct, and it went further than any other arm in shipping the alternative - a `MEASURE_FROM` constant driving both `freshAge` and a `hooks.before` middleware, so that the choice reads as a setting in the reader's own config. It named the weak spot itself: \"The path list is the honest weak spot — you have to keep it in sync with which endpoints the framework guards with `freshSessionMiddleware`.\"",
      "api": "session.freshAge anchor option (does not exist)",
      "introduced_in": null
    },
    {
      "kind": "correct",
      "summary": "Task 4, the floor probe. `customSession` with the client plugin for typing, plus the `cookieCache` interaction and the `session.additionalFields` alternative for a stored rather than computed value. Passed.",
      "api": "customSession",
      "introduced_in": "1.0.0"
    },
    {
      "kind": "context",
      "summary": "Task 7: \"I don't know of a release that does this, and I'd be suspicious of any answer that names one.\" Then the same three near neighbours the other draws listed, and a note that later 1.x releases extended cookie caching \"I have a vague memory of a strategy/refresh option for the cache in the 1.4–1.5 era\" - the right window for the wrong feature. Stateless session management is 1.4.0. Belief data, never scored.",
      "api": "stateless sessions",
      "introduced_in": "1.4.0"
    }
  ],
  "summary": "The blind twin, and it agreed with `v7-c` on every graded answer: task 1 \"yes\", task 5 `updatedAt`, both 1.6.0 options denied, the control held, the floor passed. That agreement is what makes the Fable 5.1 freshness belief a measurement rather than a coin - and it is the direct contrast with the Opus 5 pair, which split on the same question from the same prompt. Two details are its own. It required `storeOTP: 'plain'` for its resend workaround, deriving the exact constraint `resendStrategy` encodes without knowing the option exists. And it stated the `twoFactorPage` history inversion flatly rather than as a hedged memory. It charges nothing, by the duplicated-arm rule."
}
