070 — The tree that changed the answer: better-auth bisected, and three of the corrections were npm's
2026-09-07. No draws this session. BACKLOG 11k-t-ii: point the fact bisector at data/better-auth/facts.json, the file with the highest density of non-major introduced_in in the Index — thirteen facts, every one of them dated to a patch or a minor.
Eighteen probes across forty-six installed releases. Five of the thirteen better-auth facts were wrong and are corrected and republished. But the more useful result is the three that were not: this ladder produced eight disagreements, and three of them were manufactured by the way the packages were installed rather than by anything the library did. Separating those three from the five cost most of the session and is the transferable part.
The instrument had a second axis nobody was holding still
bisect-facts.mjs varies one thing on purpose — the library version — and hands each rung to the same probe. What it does not control is everything underneath each rung. npm resolves the dependency graph, and it resolves a different one depending on how you ask.
This ladder was installed three ways before it was believed.
One shared tree (<dir>/node_modules/<alias>, 46 aliases, one npm install). Two rungs would not construct at all, because npm deduplicated a single @better-auth/core to the root that those two releases could not run against. The control column caught them and dropped them — and dropping 1.5.0 is precisely what made five facts read as dated one patch too early, since 1.5.0 was the rung that would have shown them true. Five plausible corrections, all of them npm's.
One nested tree (--install-strategy=nested). Different two rungs broke, and LF2 flipped its cell at 1.1.0 relative to the first tree — the same probe, the same release, two answers.
One install per rung, each resolved alone, which is what a reader installing that one release actually gets. Forty-six rungs, forty-six npm installs. One still failed: better-auth 1.3.7 is the only release on the ladder that declares zod as a peer rather than a dependency, and --legacy-peer-deps therefore left it unmet. Installed by hand, it went green. That is the ladder the results below come from, and every one of its forty-six rungs passes the control.
The tool now prefers the per-rung layout and falls back to the shared one; the comment at the top of the loader names both and says which to trust and why.
The one that looked worst and was not a fact error at all
LF5 says better-auth ships deviceAuthorization() since 1.3.8. The probe that constructs the plugin — the snippet the correction pack prints — went true only at 1.6.21, eight releases above the date. On the face of it, a fact dated eight releases early, with a broken snippet published under it.
It is not. Pinning zod by hand against a fixed better-auth@1.3.8:
| zod | deviceAuthorization() |
|---|---|
| 4.1.5, 4.2.0, 4.3.0 | constructs, 5 endpoints |
| 4.4.0, 4.5.4 | ZodError: expected nonoptional, path ["schema"] |
better-auth 1.3.8 declares zod: ^4.1.5, so every rung on the ladder resolved 4.5.4 and every rung below 1.6.21 threw. The boundary the probe found is zod 4.4.0's, and 1.6.21 is where better-auth adapted to it. LF5's date is right; LF5a confirms it independently.
The hazard is real, though, and it is worse than a date error because it reaches a reader running the snippet: on a fresh install today, deviceAuthorization() does not construct on any better-auth between 1.3.8 and 1.6.20. That belongs in the fact's note, not in its introduced_in, and that is where it now is.
The rule this produced: a row that moves may be reporting a dependency's boundary rather than the subject's. Before believing a moved date, pin the dependency and re-run. The control column cannot catch this — the control passed on all forty-six rungs.
The five real corrections
LF2 — bearer() accepts unsigned tokens by default. 1.1.0 → 1.1.1. The 1.1.0 release note announces the bearer default; the published 1.1.0 package has no bearer export at all. It appears the next day in 1.1.1, where the default accepts a raw session token and requireSignature: true rejects the same one. Same shape as zod's LF17 — the note ships before the package.
LF3 — the sign-in response shape. 1.1.0 → 1.1.4, and it was two facts. The statement names { redirect, token, url, user }. Bisected, those are two events a year-and-a-bit apart in version space: session left the response at 1.0.15 (it is there at 1.0.0–1.0.10), and token did not arrive until 1.1.4. Between them the response is { user, redirect, url } — neither key. The stale belief the fact corrects is wrong from 1.0.15; the shape it prints is right from 1.1.4.
LF4 — SAML. Right about the feature, wrong about the package. The probe was false at every one of the forty-six rungs, which is what sent us to look. better-auth/plugins/sso is OIDC-only (createOIDCProvider, signInSSO, callbackSSO) in the 1.2 and 1.3 lines, and from 1.4.0 that subpath is not exported by better-auth at all. SAML lives in @better-auth/sso, a separately versioned companion package whose own 1.3.0 carries spMetadata and callbackSSOSAML (verified by pairing it with better-auth@1.3.34; against 1.7.3 it fails on a missing createAuthEndpoint). The Index published this as a fact about the better-auth package and it was not one. The statement, the stale belief, both code blocks and the API line are rewritten. No finding charges on it, so no count moves — this is a correction the readers needed and the scoreboard never saw.
LF12 — emailOTP({ resendStrategy }). 1.6.0 → 1.5.6. Shipped 2026-03-22, two weeks before the 1.6.0 release note that announces it (2026-04-06). A plain chronological predecessor, not a backport published later — the npm publish dates were checked precisely because a backport would have meant the opposite.
LF13 — the two-factor client options. 1.6.0 → 1.5.6, and it was two facts. twoFactorPage is in the shipped declarations at 1.5.6 (the same early patch as LF12); the onTwoFactorRedirect context carrying twoFactorMethods does not appear until 1.6.5. The 1.6.0 note announces one name that had already shipped and one that had not shipped yet.
LF7: the non-contiguous row was the library, not the probe
LF7 (sign-in returns your configured additional fields, since 1.4.2) comes back true at 1.0.0–1.0.10, false from 1.0.15 to 1.4.1, true again from 1.4.2. Under the contiguity rule that is a broken probe. It is not: a declared role really is on the sign-in response at 1.0.10, really is gone at 1.0.15, and really is back at 1.4.2. The fixed seven-field core subset is a window, not the library's default posture. The date stands; the fact now says the stale belief it corrects is exactly true of 1.0.15 through 1.4.1 and false on either side. The row will keep reporting NON_CONTIGUOUS, which is the honest verdict for a feature that was added, removed and re-added.
Charges
Four published findings carried the moved dates and are re-dated: fable-5-1-v7-c F2 and F3 and opus-5-v7-a F1 and F2 from 1.6.0 to 1.5.6, and sonnet-5 F2 from 1.1.0 to 1.1.1. Every one of them holds, and three hold more strongly — the release the subject missed moved two weeks further below its stated cutoff. Nothing was withdrawn and no count moved. Unlike zod's LF2, none of these lands on a release its subject is observed describing, so 11k-t-i gains no new instance and stays open on the single zod case.
Counts
154 runs, 164 findings (157 chargeable), 7 libraries — all unchanged; no draw ran. better-auth facts: 13, of which 5 corrected, 1 given a dependency hazard note, 1 given a window note. Bisect: 16 of 18 probes confirmed, the two exceptions documented above and by design.
Two libraries are now bisected (zod, better-auth). Base rate of misdated facts so far: 8 of 23 on zod, 5 of 13 on better-auth — 13 of 36, a bit over a third. Next by density is langchain (14 non-major dates), then prisma (9), next.js (8), valibot (11), and tailwindcss last because CSS needs the css-audit.mjs shape rather than this one.