Files
vyndr/VYNDR-CANONICAL-STATE.md
T
builtbykev b85b351993 Grade-board sort: signed signal, takeable-gated p_win, missing sorts LAST
Display ORDERING only. No grade, ledger row, lock_line, scoring, or edge_pct
scale/display change. Push scoring untouched.

Two defects removed from selectTopGrades (wrong at ANY scale, independent of
edge_pct's separate retirement):
  1. edge: Math.abs(numOr(g.edge, -Infinity)) — abs() on an already-
     direction-signed value ranked the model's strongest DISAGREEMENTS level
     with its strongest agreements (177 public ledger rows carry a negative
     edge; positive = the model AGREES with the graded side).
  2. Math.abs(-Infinity) === Infinity, so a row with NO edge sorted FIRST —
     absent data presented as the top pick (the Number(null) class).

New key: grade -> confidence -> takeable-gated p_win (nulls LAST) -> SIGNED
edge (nulls LAST) -> input order. Scales are never mixed in one comparator.
Takeable band = web valueState.isTakeable, asserted byte-equal to the hero's
config/valueEngine.isTakeable (-160..+200) incl. strict-null.

Alt-line ladder (analyzeViaEngine1:506) no longer sorts by edge_pct: ordered
highest-p_win-first derived analytically at zero added compute — P(stat >= k)
is monotone non-increasing in k, so p_win-desc is line-ASC for an over and
line-DESC for an under. base stays marked; no consumer depends on
alt_lines[0]; deskShowcaseService.rungsOf already re-sorted by line.

THREE PREMISE BREAKS found report-first, before code:
  - /api/props/top-graded 404s in prod (absent from src/) so the dashboard
    board renders receipts/empty — the edge sort orders nothing there today.
    The prior order's "97.3% of rows tie" was a LEDGER measurement wrongly
    extrapolated to that board. Fix is correct-in-itself and lands when the
    feed is restored.
  - p_win cannot be a client-side key for all tiers: snapshotGating strips it
    for unentitled tiers ("shipping p_win is shipping the model price").
    Verified live: prod /api/snapshot carries p_win on 0/8 MLB, 0/25 WNBA.
  - Ladder rungs carry no per-rung price, so the takeable gate is inapplicable.

Verified on real data, both sports, both paths: unentitled — WNBA (n=25)
ordering CHANGED, MLB (n=8) unchanged, signed edge non-increasing in every
(grade,confidence) tie group (20 pairs, 0 violations); entitled — 40 real
ledger rows with p_win+locked_odds, p_win-descending, untakeable chalk NOT
promoted (Trea Turner .757 @-275 does not beat Rhyne Howard .745 @-120)
(36 pairs, 0 violations).

Hero consistency, stated honestly: same signal + same gate, different
precedence BY CONTRACT (board = grade-tier-first "top GRADES"; hero =
p_win-first "top read"). Identical within the leading tier (verified); across
tiers the board may lead with an A the hero doesn't pick. Not a contradiction.

Floor: 310 suites / 3864 tests green, web build exit 0. Dashboard + Desk
visuals are auth/feed-gated -> tagged for the Chrome audit, no visual faked.

Held: edge_pct rescale/display retirement (Order B); building the missing
/api/props/top-graded selector; exposing p_win to unentitled tiers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
2026-07-29 21:13:51 -04:00

291 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VYNDR — CANONICAL STATE FILE
Read-only ground-truth audit. Written 2026-07-26 by Claude Code (repo + deployed DB access).
Every item tagged **VERIFIED** (file/line or measured number), **CANNOT DETERMINE** (with reason),
or **BLOCKED** (with what unblocks it). Where a prior claim conflicts with code/data, the code/data wins.
Nothing was built, changed, deployed, or migrated by this audit.
---
## REVIEW ZERO — PREMISE
- **0.1 What I can read** — VERIFIED. The **repo** (full source), the **deployed Supabase DB**
(read-only via MCP, project `zmdnczhtdxcddsxzttub`), and the **deployed API** (`api.vyndr.app`).
- **0.2 Key reachability** — VERIFIED. `PROPLINE_API_KEY_*` are **NOT on the box** (`.env` absent);
they were pasted in-session earlier this conversation and are usable for probes. `ODDS_API_KEY`
is on the box but **exhausted (0/500)**. `ODDSPAPI_KEY` not on box. DB-dependent items were run
against prod directly, so nothing here is BLOCKED on PropLine keys.
---
## PHASE 0 — THE ROI RECONCILIATION (headline)
- **0.1 ROI computed in code?** — VERIFIED. **Not for the model ledger.** ROI exists only for
**user bet-tracking**: `performanceService.js:42` (`roi: stats.roi`) and `betService.js:172-174`
(`profit = payout - amount`). There is **no ROI computation over `ledger_entries`** (the model's
public record). That is why the model's ROI has been invisible.
- **0.2 Price-at-grade persisted?** — VERIFIED. **YES**`ledger_entries.locked_odds` (text,
American), written by `ledgerService.recordPipelineGrades`. Populated on essentially all settled
rows (5 nulls on B). **ROI IS computable for accrued history.**
- **0.3 11-point index persisted?** — VERIFIED (nuanced). **NOT in `ledger_entries`** — only the
collapsed letter (`grade`); `_grade_11` is explicitly `delete`d at `gradeSlateService.js:97`
before the ledger write. **BUT it IS retained in `model_snapshots.grade_11`** (migration
`025:51`, `retentionService.js:117`) — 2,888 of 4,600 snapshot rows carry it. So sub-tier is
**lost on the settled-outcome table but recoverable** by joining `model_snapshots` to
`ledger_entries` on (player, stat, line, date). STATE.md's "grade_11 is stored" and this audit's
"deleted before ledger write" are BOTH correct — different tables.
- **0.4 Price distribution by grade (American odds, settled)** — VERIFIED:
| sport | grade | n(decided) | median | q1 | q3 |
|---|---|---|---|---|---|
| MLB | B | 285 | 270 | 650 | 155 |
| MLB | C | 176 | 169 | 500 | +109 |
| WNBA | B | 208 | 124 | 145 | 110 |
| WNBA | C | 160 | 120 | 134 | 108 |
Blended B median 160, C median 130. Overall range 10000 … +600. MLB grades skew to **deep
favorites**; WNBA grades cluster tight around 120.
- **0.5 Flat-stake unit ROI by grade (1u/row at `locked_odds`, decided rows hit/miss, voids
excluded)** — VERIFIED:
| sport | grade | n | hit% | **ROI%** |
|---|---|---|---|---|
| MLB | B | 285 | 70.2 | **1.02** |
| MLB | C | 176 | 60.8 | **+4.57** |
| WNBA | B | 208 | 52.4 | **4.79** |
| WNBA | C | 160 | 51.9 | **5.26** |
| A / D / F | (all) | 1 / 2 / 4 | 0 / 0 / 0 | 100 each (negligible n) |
Including voids as net-0 (my first pass) gives blended B 2.31%, C 0.10%.
**CONCLUSION — the premise's binary is a false dichotomy; the truth decomposes:**
- MLB **B** = high-hit (70%) **break-even favorites** — the premise's "favorites at fair prices,
zero edge" hypothesis is CONFIRMED here.
- WNBA **B/C** = **losing** (~52% hit at ~120 where break-even is ~54.5%).
- MLB **C** = **genuinely +EV (+4.57% on 176 decided)** — a real edge the blended "no ROI" masked.
So: ROI is not uniformly zero-edge. It is **not computed in code**, and when computed here it shows
**one profitable segment (MLB-C) hidden under WNBA losses and break-even MLB-B**.
- **0.6 64/56 population** — VERIFIED. The displayed `/api/ledger/accuracy` figures are
`hits/(hits+misses)`, **excluding voids** (88 void rows, 8.8%) and unsettled (67). Distinct
`outcome` values are **{hit, miss, void, null}** — **no `push`** (pushes structurally impossible:
half-number lines). The ROI table above uses the SAME decided (hit/miss) population, so hit-rate
and ROI are comparable. Blended B hit is 63.1% on `hits/(hit+miss)` but 55.9% on all-settled
(the 64 void B rows are the gap).
- **0.7 Hit rate in UI/marketing?** — VERIFIED. Displayed on **≥10 surfaces**: `app/page.tsx`
(landing), `GradeCard`, `TopSignals`, `vyndr/TierRecord`, `vyndr/ModelRecord`, `vyndr/AccuracyBadge`,
`game/[id]/page`, `u/[handle]/portrait`, `ledger/page`, `scan/page`. **ROI / edge is displayed
NOWHERE.** Honesty gap: users see "63% / 70% HIT" with no indication MLB-B is 1% and WNBA is 5%.
- **0.8 CLV computed / closing persisted?** — VERIFIED (with a broken-ness caveat). `closing_odds`
on **962/996** rows, `clv` on **842/996** — so CLV IS computed and closing IS persisted. BUT
`closingCapture.js:7-8` documents it as **effectively broken**: `closing_line == locked_line on
92% of rows` (only ~56 rows show real movement), because `captureClosing` overwrites the field
with the current feed on every snapshot and most props leave the feed near their lock. CLV exists
but is **largely degenerate (≈0)**.
---
## PHASE 1 — THE GRADED LINE
- **1.9 Selector location** — VERIFIED. Two stages: (a) `gradeSlateService.dedupeProps`
(`gradeSlateService.js:34`, called `:144`); (b) the `snapshotService` dedup at
`snapshotService.js:425-434`.
- **1.10 Reads over which set?** — VERIFIED. **One provider at a time** — PropLine-normalized rows
(primary) via `oddsService``recordDownstream``gradeAndCacheSlate`. Falls back to
odds-api / oddspapi only if PropLine fails (`oddsService` fallback chain). Not all providers merged.
- **1.11 dedupeProps exists?** — VERIFIED. **YES, it EXISTS** (`gradeSlateService.js:34`). Logic:
Set on key `` `${player}::${stat_type}::${line}` `` — keeps the **FIRST** row per (player, stat,
line), discards later rows at the same (player, stat, line) (i.e. other books at the same line),
caps at `limit`. Written **Session 32** (`f0c8b4f`, "Grades pipeline + NFL/NHL wiring"). This
settles the asserted/un-asserted question: **it is real, not imagined.**
- **1.12 snapshotService 411-434** — VERIFIED. Confirmed: dedup by `` `${nameKey}|${stat_type}` ``,
keeps the row with the **highest `confidence`**. `confidence` derives from the **grade letter**
(band-midpoint from `grade_thresholds.json`, `confidence_basis:'grade_band'`) — so "highest
confidence" == "highest grade letter". It carries no information beyond the letter.
- **1.13 consensus vs first-book** — VERIFIED. **NO consensus rule exists in code.** The graded line
is chosen by **first-book-at-each-line (dedupeProps) then highest-grade-across-lines
(snapshotService)**. The "book-agnostic consensus rule" description is a prior order's *proposal*,
never built. The "first book" description is CLOSER to correct. **Code wins: no consensus.**
- **1.14 5 books at 3 lines → which line?** — VERIFIED (walked). `normalizeProps` emits one row per
book → `dedupeProps` keeps the first book at each of the 3 distinct lines (3 survivors) →
`gradeAndCacheSlate` grades both sides of each → `snapshotService` keeps the **highest-grade** of
the 3. **The graded line = whichever of the 3 lines grades highest** (a best-grade-for-us
selection, active now that the feed is 27% MLB / 82% WNBA multi-book).
- **1.15 Consumers assuming one book/prop** — VERIFIED (partial list): the grade path
(`dedupeProps` collapses book multiplicity), `analyzeViaEngine1` (`book_odds`/`fair_prob` use the
single surviving row's odds), the snapshot GameCard overlay, `detectBestBook` (no-ops <2 books).
- **1.16 Test pinning graded line vs book-set change?** — VERIFIED: **NONE.** `dedupeProps` and
`snapshotService` have unit tests for dedup mechanics, but **no test pins graded-line stability
against a change in the book set** (a multi-book different-line scenario).
- **1.17 Ledger flag for pre/post book-set change?** — VERIFIED. **No dedicated flag.** `model_version`
stamps the model era and challenger-version columns exist, but **nothing distinguishes grades by
book-set basis.** A silent book-set change would not be visible on the ledger.
---
## PHASE 2 — THE FEED
- **2.18 Books-per-prop TODAY (2026-07-26, live `/api/odds`)** — VERIFIED, and it **CORRECTS the
prior "137/140 single-book, DK129/FD14" measurement — the feed has broadened:**
- **MLB**: n=322 → `{1 book: 234 (73%), 2: 51, 3: 26, 4: 10, 5: 1}`. Books: **draftkings 273,
betmgm 105, betrivers 49, pinnacle 30, fanduel 2.** So 73% single-book (mostly DK), 27%
multi-book, **5 books now present** (not DK-only).
- **WNBA**: n=163 → `{1 book: 29 (18%), 2 books: 134 (82%)}`. Books: **fanduel 149, draftkings 148.**
**WNBA is graded off a genuine 2-book feed (DK + FD)** — better multi-book coverage than MLB.
- **2.19 ALLOWED_BOOKS** — VERIFIED. `oddsNormalizer.js:9` = 11 books (draftkings, fanduel, betmgm,
caesars, fanatics, bet365, hardrockbet, pointsbet, betrivers, pinnacle, thescore), applied at
`:110`/`:198`/`:235`. **Drops nothing today** (every book in the live feed is on the list).
- **2.20 PropLine request** — VERIFIED. `proplineAdapter.js:128` `buildUrl = ${BASE}/${sportKey}/odds`;
`:151-152` params = `{ apiKey, markets }`. **No `regions`/`bookmakers` param is sent.** The feed
broadening (2.18) happens on PropLine's DEFAULT response, not a param we added.
- **2.21 PropLine docs / multi-book param** — CANNOT DETERMINE (not re-tested this order). Prior
research (WebSearch): PropLine advertises "13 books + 5 exchanges; every payload includes a
bookmakers array" and is The-Odds-API-compatible (which uses `regions`/`bookmakers`). Whether a
param unlocks the full set on our tier is **unconfirmed** — would need a keyed test with the param.
---
## PHASE 3 — THE CHALLENGER LEDGER
- **3.22 Row counts** — VERIFIED:
| challenger | total | settled | first row | note |
|---|---|---|---|---|
| arch-v1 | 164 | 128 | 2026-07-21 | 94 with non-zero delta |
| contact-v1 | 122 | 86 | 2026-07-23 | 107 non-null `p_win_contact` (15 abstained) |
| proj-v1 + proj-v1.1 | 76 + 46 = 122 | 86 | 2026-07-23 | 119 with `proj_point` |
**All MLB** (challengers are statcast-gated → MLB only; the 407 WNBA rows carry none). Public
ledger total = **996 rows** (929 settled, 67 unsettled). **The earlier "ZERO settled p_win /
measurement not begun" is now STALE — 86-128 settled per challenger.**
- **3.23 Population rate** — VERIFIED. 872 rows in the last 14 days across **12 active days** (2 days
had no rows). Challengers populate a fraction of rows (arch 164/996) — the rest are pre-deploy or
WNBA.
- **3.24 Idempotent-lock lag** — VERIFIED, **still structural.** The ledger upsert is
`ignoreDuplicates:true`, so a challenger's fields land ONLY on rows first written AFTER that
challenger's code deployed (arch 07-21, contact/proj 07-23); re-running a snapshot never
backfills challenger fields onto an already-locked row. Any challenger added later inherits the
same gap. **Settlement itself is healthy** (`stale_unsettled = 0`).
- **3.25 Silent-failure / abstain path** — VERIFIED: **none producing fake accrual.** contact-v1's
nulls are honest abstentions (thin/absent Statcast); proj-v1 projects 119/122; arch-v1's
non-moved rows are byte-identical-to-champion (no distinctive axis). No caught-throw-writes-null
path masquerading as accrual.
- **3.26 Void/DNP rate** — VERIFIED. **88 void (8.8%)** + 67 unsettled of 996. Matches the ~9% premise.
- **3.27 model_snapshots / archetype / opp_rank** — VERIFIED (partial). `model_snapshots` is LIVE:
**4,600 rows, latest 2026-07-26 22:01**, `grade_11` on 2,888. Archetype + `opp_rank_stat` were
verified live per-sport (MLB + WNBA) in prior orders; **CANNOT re-confirm per-sport freshness here
without additional queries** (not run to keep this pass bounded).
---
## PHASE 4 — WHAT IS ACTUALLY WIRED
- **4.28 SportsGameOdds wired?** — VERIFIED. **NOTHING.** No SGO reference in `src/`, `web/src/`,
or `.env`. Not wired, configured, committed, or deployed. (The audit that qualified it as a source
was report-only.)
- **4.29 Design surfaces live vs designed** — VERIFIED:
- **S2 (book comparison / crown / disagreement):** `BookComparison.tsx` EXISTS in
`web/src/components/` but is **NOT imported/routed anywhere (dead component)**. `BookChip` +
`BookWordmark` exist and are used. `MovementStrip`, `CrownBadge` → **NOT FOUND** (never built).
(Corrects an earlier order that said "BookComparison doesn't exist" — it exists, just unrouted.)
- **THE WIRE:** = the **daily newsletter/content format** (see 4.30). Newsletter engine built
(`newsletterService`); send is unscheduled (internal endpoint only).
- **S3 article media:** **NOT FOUND** (no article-media generator; `mediaEngine` only has wire/
share text templates).
- **S-2 Offseason hub / SeasonBoard:** **NOT FOUND** (never built).
- **System / Intelligence:** design files present in `specs/design-reference/`; live components partial.
- **4.30 What is THE WIRE?** — VERIFIED. It is **VYNDR's daily newsletter / content voice**, not a UI
surface: `newsletterService.js:219-220` (`THE WIRE — {date}`) + `mediaEngine.js:6,121`
(`MORNING WIRE`, `SIGNAL` deterministic templates). It's the editorial format for the daily report.
- **4.31 detectBestBook + LineSparkline** — VERIFIED. `slateAdapter.detectBestBook` returns a book
only when ≥2 books post the SAME line at differing prices (no-ops on 1 book, never marks a lone
price "best"). `StatStrip.LineSparkline` (`StatStrip.tsx:130`) renders only at **≥3 history
points**, returns `null` below. Both degrade honest-absent on single-book/shallow data.
- **4.32 Line history / closing overwrite** — VERIFIED. `history` (`{t,line}`, line-deduped, cap 24)
is shallow — most props sit at 1-2 flat points (line moves are rare; books move odds, not the
half-point line). Two distinct closing mechanisms: the **`closing_captures` TABLE is INSERTED /
appended** (`closingCapture.js:234`), but the **`ledger_entries.closing_line/closing_odds` is
OVERWRITTEN every snapshot** (`ledgerService.js:18-21` doc; overwrite is why CLV is degenerate — 0.8).
- **4.33 Migration drift** — VERIFIED, **still present.** Repo `supabase/migrations/` has
**001-022, 025, 030, 031, 032**. **MISSING: 023, 024, 026, 027, 028, 029** — applied to prod but
untracked (they carry the challenger columns `p_win_challenger`/`challenger_*`, `model_version`,
`env_*`, `archetype_vector` — confirmed live in prod). The repo does NOT reflect prod schema; check
`information_schema.columns` via MCP, not the repo, before schema work.
- **4.34 WNBA grading live?** — VERIFIED. **YES.** 407 WNBA ledger rows, **376 settled**, graded off
a 2-book (DK+FD) feed. WNBA carries no challenger rows (statcast-gated MLB-only).
---
## PHASE 5 — DOCTRINE AND THE BOARD
- **5.35 CLAUDE.md (read in full)** — VERIFIED. Standing rules & findings:
- **NO CODE WITHOUT A SPEC**; 5 quality gates; WSL2 heredoc rule (python3 for >10-line files);
update BUILD-STATE.md / BLOCKERS.md.
- **Data Semantics Rule:** VYNDR never generates lines/odds — market values are REAL captured book
numbers; only model_value/grade/edge are model output. `Number(null)===0` is the recurring
fabrication bug; use strict null guards.
- **Grade internals:** grade is an additive integer index (`engine1`, NEUTRAL_INDEX 3), moved by
flat ±1.0/±0.5 deltas; A needs sum ≥+4.5, D ≤−1.51. `confidence` is NOT a probability (grade-band
midpoint). `p_win` is the real signal. **NEVER rescale thresholds to mint A's (permanent founder
ruling).** **A-RATED marketing on hold** until a prod fingerprint shows real A grades.
- **Three stat_type whitelists must stay in sync** (analyze.js, scan.js, validation.py).
- **Snapshot pipeline** is the product model (scheduled grade → lock to line → read from cache);
on-demand "Read" retired. SNAP_TTL 24h.
- **DO-NOT-WIRE / DO-NOT-TOUCH:** Tank01 player props = empty (do not wire); ParlayAPI host dead;
`gameLogService.getGameLogs` returns null for MLB (a trap — use `featureCache.getStatRows`);
`mlbGrader.js` is dead code; the legacy `--grade-a` token alias block kept until consumers migrate.
- **Three separate MLB stat maps on purpose** (featureCache / outcomeService / liveTrackingService) —
do not merge. Settlement is MLB-only (WNBA/NBA/soccer never settle via that path — but WNBA IS
grading + settling per 4.34, so confirm the settle path).
- **5.36 Running task list / open items** — VERIFIED. Primary: **`specs/STATE.md`** (1,886 lines,
"STATE OF THE WORLD," CURRENT STATUS + OPEN ITEMS block, last dated 2026-07-22). Also
`BUILD-STATE.md`, `BLOCKERS.md`, `DECISIONS.md`, `AUTONOMY.md`, `PROMISE-AUDIT.md`, `ROADMAP.md`.
**Open threads I have been tracking across recent orders that a strategist chat may not have:**
(a) challenger measurement now HAS settled rows (arch 128 / contact 86 / proj 86) — promotion is a
future per-prop-type ledger decision; (b) the design-migration arc (Landing hero migrated; scanner
S6/S7 blue-channel reskin shipped; S2/THE WIRE/Offseason/article-media are GAP/unbuilt);
(c) multi-book: SGO qualified (report-only), nothing wired; (d) **ROI is uncomputed in code and
decomposes to MLB-C +4.57% / MLB-B 1% / WNBA 5%** (this file).
- **5.37 Spec / roadmap files** — VERIFIED (in `specs/`): `STATE.md` (running state),
`VYNDR-NORTH-STAR.md`, `DESIGN-SPEC.md`, `ROW-GRAMMAR.md` (row grammar law), `LIVE-TRACKING.md`,
`VOICE.md`, `model-train.md`, `phase-0-kill-the-lies.md`, `phase-1-truth-infrastructure.md`,
`propline-audit.md`, `feature-1-1…4-1` + `a1-s3/s7/s9/s10` feature specs, `combat-intelligence.md`,
`design-reference/` (the design bundle). Root docs: `CLAUDE.md`, `DECISIONS.md`, `ARCHITECTURE.md`,
`AUTONOMY.md`, `BACKEND_HANDOFF.md`, `BUILD-STATE.md`, `BLOCKERS.md`, `PROMISE-AUDIT.md`.
- **5.38 Standing decisions a new session might contradict** — VERIFIED: `DECISIONS.md`
(DECISION-001+ architecture log); CLAUDE.md's permanent rulings (never mint A's; A-RATED hold;
data-semantics; do-not-wire list); STATE.md open items (A does not emit in prod; EV overconfident/
unvalidated — hero ranks on ev_pct and picks the most overconfident read; grade_11 stored in
model_snapshots). `AUTONOMY.md` = the zero-touch loop trace.
---
## CLAIMS I WAS ASKED ABOUT THAT TURNED OUT TO BE FALSE OR UNSUPPORTED
1. **"the ledger reports … 597 settled with 'no ROI'"** — the population is now **929 settled / 996
total** (597 was an earlier snapshot); and **ROI IS computable** (`locked_odds` persisted) — it is
simply **not computed in the ledger code**. "No ROI" = no computation, not incomputable.
2. **"Either ROI is not computed, or the model is selecting heavy favorites at fair prices"** — the
binary is false; **both are partially true and it decomposes by sport/grade**: MLB-B = high-hit
break-even favorites (the hypothesis), WNBA = losing, **MLB-C = genuinely +4.57% EV**. Not
uniformly zero-edge.
3. **"the graded line is chosen by a book-agnostic consensus rule"** — **no consensus rule exists in
code.** It is first-book-at-line (`dedupeProps`) + highest-grade-across-lines (`snapshotService`).
4. **Prior "137/140 single-book, draftkings 129 / fanduel 14" (~98% single-book)** — **corrected**:
today MLB is 73% single-book with **5 books present** (DK/BetMGM/BetRivers/Pinnacle/FanDuel), and
**WNBA is 82% two-book (DK+FD)**. The feed broadened.
5. **"the 11-point index is lost / unrecoverable"** — **it is retained in `model_snapshots.grade_11`**
(2,888 rows); lost only from `ledger_entries`. Recoverable via join.
6. **"ZERO settled p_win yet / challenger measurement not begun"** (from prior challenger orders) —
**stale**: arch-v1 128, contact-v1 86, proj-v1 86 rows are now settled.
7. **"BookComparison doesn't exist, only BookChip"** (an earlier order) — **BookComparison.tsx
EXISTS**, it is just unrouted/dead.
8. **CLV framed as "held / not computed"****CLV IS computed** (842 rows) and closing IS persisted
(962), but it is **largely degenerate** (closing==locked on 92%) due to the overwrite — a
different problem than "not computed."
9. **WNBA implicitly treated as not-really-grading** (settlement described as MLB-only in CLAUDE.md) —
**WNBA IS grading AND settling** (376 settled). The doctrine note about MLB-only settlement is
contradicted by the data; the WNBA settle path should be confirmed.
---
*End of canonical state. Regenerate the measured numbers before citing them in a later session —
they move as the ledger accrues. Structural facts (file/line, schema, wiring) are stable until code changes.*