docs: /api/books contract + crown gate (Book Comparison order)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VsztNChZ7vEvSR61AuMhD1
This commit is contained in:
Kev
2026-07-27 00:49:10 -04:00
parent e81c9b8c51
commit b0a51c8a0d
+25
View File
@@ -296,3 +296,28 @@ Every graded read (snapshot `grades:{sport}`, `/api/snapshot/:sport`, `/api/hero
DESIGN: the triplet + VALUE marker + refusal copy are Session-2 design surfaces
(reveal card, board row, hero). Backend ships the fields; rendering is Design's.
## Book Comparison — `/api/books` (per-book price display, snapshot-locked)
Data-only layer for the (still-unrouted) `BookComparison.tsx` grid. Never touches
the grade path.
- `GET /api/books/:sport/:player/:stat?side=over|under` → the honest per-prop
grid: `{ player, stat, line, side, books:[{book,line,over_odds,under_odds,
isBest}], bestBook, bestOdds, bookCount, savings, crowned }`. HONEST-ABSENT:
a single-book prop returns that one book with `isBest:false, bestBook:null,
crowned:false` — never a placeholder second row. `:player` is matched by
`nameKey` (accent/dot/nickname-folded).
- `GET /api/books/:sport?side=&limit=` → "best lines tonight" (`bestLines`, sorted
by savings). This is a CROWN claim, so it returns `[]` while the crown is gated
off. Response `source` = `bookprices` (snapshot-locked store serving) or
`odds-cache` (fallback before first snapshot) — also the deploy fingerprint.
- **The crown is threshold-gated + OFF by default** (`BOOK_CROWN_ENABLED=1` to
enable). Pre-registered gate: median same-line spread ≥8¢ OR ≥2 implied-prob
pts (`scripts/measure-book-spread.js`). Measured 2026-07-27 on the live feed:
MLB median 5¢/0.95pp, WNBA 7¢/1.38pp → NOT met → crown does NOT ship. Render
books WITHOUT crowning one until a future measurement clears the bar.
- Source store: `bookprices:{sport}` (Redis, SNAP_TTL), written by
`snapshotService` from the pre-dedup multi-book `props`. Grouped
`{player, stat_type, books:[...]}`, normalized-name-keyed. DISPLAY-ONLY —
fenced from grade/selector/challenger/ledger.