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
This commit is contained in:
Kev
2026-07-29 21:13:51 -04:00
parent 9b5235cf99
commit b85b351993
9 changed files with 886 additions and 11 deletions
+290
View File
@@ -0,0 +1,290 @@
# 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.*