Files
vyndr/outputs/VYNDR-COMPLETION-MATRIX.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

356 lines
33 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 — PRODUCT COMPLETION MATRIX
Canonical board. Supersedes STATE.md's narrative. Re-derived from repo (`b0a51c8`),
prod (`api.vyndr.app` / `vyndr.app`, checked 2026-07-27), and the design bundle
(`specs/design-reference/*`). Every cell cites a file, a route, or a prod check.
**DONE = all five YES:** DESIGNED (spec exists) · BUILT (code exists) · WIRED (a user
can reach it) · LIVE (serving in prod now) · HONEST (real data, not fabricated/inflated/
placeholder). A built-but-unrouted component is **WIRED: NO** regardless of code quality.
> **HONESTY PASS applied 2026-07-27 (6bc18d8):** every KNOWN live fabrication removed/hidden
> — see the "HONESTY PASS" section at the end. HONEST cells for rows 19/20/24 updated (✓).
## SURFACE MATRIX — 16 of ~26 fully done (Book Comparison wired 2026-07-29)
| # | Surface | DESIGNED | BUILT | WIRED | LIVE | HONEST | DONE | Evidence / what's missing |
|---|---|---|---|---|---|---|---|---|
| 1 | Landing / hero | YES | YES | YES | YES | YES | ✅ | `Landing.dc.html`; `app/page.tsx`; nav logo; prod `/`→200; Hero/TopSignals/ClaimMeter/ModelRecord all fetch real + self-hide |
| 2 | Dashboard / Slate | YES | YES | YES | YES | YES | ✅ | `Mobile.dc.html`; `Slate.tsx`+`vyndr/GameCard`; Nav "Slate"; prod `/dashboard`→200; snapshot grades real (wnba 25/mlb 5 today) |
| 3 | Scan flow (S6/S7) | YES | YES | YES | YES | YES | ✅ | `Scanner States.dc.html`; `scan/page.tsx`+`ProcessingGrade`; Nav "Read"; prod 200; real grade, honest `NoMarketState` |
| 4 | Grade result card | YES | YES | YES | YES | YES | ✅ | `System.dc.html` reveal; `GradeResultCard.tsx` (3 importers); `/scan`; real grade. Sub-sections (alt ladder, books) gated/absent — see #17/#18 |
| 5 | Grade display / badge | YES | YES | YES | YES | YES | ✅ | `HANDOFF.md:41`; `GradeBadge.tsx`; dashboard/scan/many; prod grades B/C real, no fake A |
| 6 | Ledger | YES | YES | YES | YES | YES | ✅ | `Intelligence.dc.html` S4; `ledger/page.tsx`+`ClvBadge`; Nav "Ledger"; prod 200; n≥20 gate honored |
| 7 | Tier-record | YES | YES | YES | YES | YES* | ✅ | `HANDOFF.md:33`; `TierRecord.tsx` (6 routes); n≥20 gate. *A-tier "edge glow" is ranking-as-credibility, not proven ROI (honest-but-thin) |
| 8 | Pricing | YES | YES | YES | YES | YES | ✅ | `Mobile.dc.html`; `Pricing.tsx`+`ClaimMeter`+`DeskShowcase`; Nav/Footer; prod 200; Stripe real, mocks removed |
| 9 | Public profile `/u` | YES | YES | YES | YES | YES | ✅ | `System.dc.html` /u; `PublicProfile.tsx`; OPEN_ROUTE share link; prod `/api/profiles/test`→404 (privacy honest) |
| 10 | Player profile | YES | YES | YES | YES | YES | ✅ | `Mobile.dc.html` PLAYER; `player/[name]`; reachable via `playerHref` links; prod `/api/stats/player`→found:true (archetype/propDNA/season) |
| 11 | Team hub | YES | YES | YES | YES | YES* | ✅ | `Mobile.dc.html` TEAM HUB; `TeamHub.tsx`; `TeamLink` on cards; prod `/api/team/NYY`→26 roster. *MLB real; NBA/WNBA partial roster |
| 12 | Explore | YES | YES | YES | YES | YES | ✅ | `Intelligence.dc.html`; `ExploreHub.tsx`+`FuturesBoard`/`NewsWire`; Nav; prod 200; real leaders/futures, "TRACKED·NOT GRADED" label |
| 13 | WIRE / ticker | YES | YES | YES | YES | YES | ✅ | `System.dc.html` WIRE; `vyndr/Ticker.tsx`; root layout; prod `/api/ticker`→30 real items |
| 14 | Streaks / hot list | YES | YES | YES | YES | YES | ✅ | `System.dc.html`; landing panels + Explore; prod `/api/streaks/mlb`→240 (computed), self-hide honest |
| 15 | Live tracking | PARTIAL | YES | YES | YES | YES | ⚠️ | `liveTrackingService`+`StatStrip.LiveTracker`; Slate polls `/api/live`; prod hasLive:false now (valid empty). DESIGNED: no dedicated bundle artboard |
| 16 | Parlay lab | PARTIAL | YES | YES | YES | YES | ⚠️ | `System.dc.html` PARLAY BUILDER (not named "Lab"); `parlay/page.tsx`+`ParlayPanel`; `#parlay` drawer; prod `/api/parlay/grade`→real A- |
| 17 | Alt-line ladder | PARTIAL | YES | PARTIAL | YES | YES | ❌ | Champion `alt_lines` real (prod: 5 rungs w/ edge_pct) but **Desk-tier only** (`tierGating.js:55`); proj-v1 `proj_ladder` ledger-only. DESIGNED: only Offseason grouping, no prop alt-stack |
| 18 | **Book comparison (S2)** | YES | YES | **YES✓** | YES | YES | ✅ | ✓WIRED 2026-07-29: `BookComparisonPanel` (self-fetches `/api/books`) on the GradeResultCard; renders per-book lines (books differ), single-book honest state, crown OFF (`BOOK_CROWN_ENABLED=false`, no best claim). Push-to-book + movement strip deliberately HELD |
| 19 | Price triplet | YES | YES | YES | YES | YES✓ | ❌ | ✓HONESTY PASS: null model/EV now NO_MODEL (honest-absent), no false "poisoned" copy. Still ❌: EV layer doesn't *produce* model_odds/ev (separate build) |
| 20 | **Compare (H2H)** | PARTIAL | in-dev | NO | YES | YES✓ | ❌ | ✓HONESTY PASS: fabrication removed → honest in-development state, pulled from Nav+BottomTabBar. Real two-player build pending (awaiting real build) |
| 21 | Newsletter | YES | YES | YES | PARTIAL | YES | ❌ | `NewsletterCapture.tsx` on `/`,`/welcome`; subscribe validates live; send internal-only. LIVE: Listmonk env config `CANNOT DETERMINE` from prod |
| 22 | Slip reader | PARTIAL | YES | PARTIAL | YES | YES | ❌ | `slip/page.tsx`; prod 200; **0 nav links → orphan** (deep-link only). DESIGNED: `a1-s9` spec, not the design bundle |
| 23 | Article media / share (S3) | YES | PARTIAL | NO | PARTIAL | YES | ❌ | `Intelligence.dc.html` ARTICLE MEDIA; `ShareCard.tsx` **0 real importers = DEAD**; OG `opengraph-image.tsx` IS live. In-article archetype figures not built |
| 24 | Calibration / edge board | YES | YES | NO | NO | YES✓ | ❌ | ✓HONESTY PASS: placeholder-edge% `MobileEdgeBoard` REMOVED from the Slate (phones show real cards). Component kept as dead code until a real edge feed exists |
| 25 | System / Intelligence terminal | YES | YES | **NO** | PARTIAL | YES | ❌ | `System.dc.html`/`Intelligence.dc.html`; `/terminal`→redirect to `/dashboard`; `/intelligence` REAL but **orphan (0 nav links)**; `/system` no page (prod 404) |
| 26 | Offseason hub (S-2) | YES | PARTIAL | NO | NO | — | ❌ | `Offseason.dc.html` full spec; **no `/offseason` page** (prod 404); logic only inline in `FuturesBoard`/`NewsWire` on Explore |
Prod endpoints confirmed LIVE + real: `/api/snapshot/{mlb,wnba}`, `/api/accuracy` (n=763),
`/api/ledger/accuracy`, `/api/ticker`, `/api/books/mlb`, `/api/streaks`, `/api/hotlist`,
`/api/live`, `/api/schedule`, `/api/stats/{player,leaders}`, `/api/team`, `/api/parlay/grade`.
## MODEL MATRIX — champion serves; every challenger is ledger-only
| Component | EXISTS | PROVEN | PROMOTED (serving) | USED (a surface reads it) | Evidence |
|---|---|---|---|---|---|
| Champion (engine1) | YES | PARTIAL→**promising** | YES | YES | `analyzeViaEngine1.js`; `enriched``snapshot`/`grades`. SKEW AUDIT 2026-07-29: on takeable MLB overs (n=62) champion p_win→CLV **partial r=0.375, SIG p≈0.003**; SURVIVES the mechanical baseline (no-edge CLV +1.5pt n=20 vs high-edge +8.6pt n=37 → **+7.1pt marginal**). De-vig clean (same-book pairing); close well-defined (DK/MGM r=0.92). PROMISING, NOT confirmed (thin n; lock-staleness check BLOCKED; 1 sig result among many) |
| arch-v1 | YES | NO | NO | NO | `challengerProjection.js`; rides `withChallenger`→**ledger only** (`:693`). "measured, never served" (`ledgerService.js:251`) |
| contact-v1 | YES | NO | NO | NO | `contactChallenger.js`; ledger col `p_win_contact` only |
| proj-v1 / v1.1 | YES | **NO (tested 2026-07-29)** | NO | NO | `projectionChallenger.js` (MLB-batting only). PROOF ORDER verdict: **NOT PROVEN** on n=45 takeable MLB overs — edge-CLV partial-r (controlling price) = 0.245 (n.s.); ~half the raw signal is the shared fair_prob_lock term (mechanical); and the CHAMPION out-predicts it (champ partial-CLV 0.380 sig, champ-edge→hit 0.25 vs proj 0.12). Ledger-only |
| Champion alt-ladder | YES | NO | YES | Desk-only | `analyzeViaEngine1.js:486`; real re-grades ±1 line; `gradeAdapter.js:100` maps to card **Desk-gated** |
| proj-v1 `proj_ladder` | YES | NO | NO | NO | `distribution.js:100`; ledger-only, reaches no card |
| Price gate / EV | YES | PARTIAL | PARTIAL | PARTIAL | fields on `enriched`; hero gates on `isTakeable`+`ev_pct` (`heroPropService.js:78`). **But prod grades show ev_pct/p_win/model_odds/value/takeable = NULL** — built, served-schema, not producing |
### Ladder question (Phase 2.6) — VERIFIED: proj-v1.1 DOES compute rungs above the line
`projection/distribution.js:100` `ladder()` computes `P(stat ≥ k)` for a fixed rung set
`k = 1..LADDER_MAX` (default 4), **independent of the listed line** — so for a line of 1.5
(tradedRung=2) it emits rungs 1 (below), 2 (at), **3 and 4 (ABOVE)**, each a real
negative-binomial survival prob (`projectionChallenger.js:183,200`). "Ladder-up **works**"
— sole cap is `LADDER_MAX=4`. **BUT `proj_ladder` is ledger-only and reaches no user
surface.** So "ladder up from the listed line to find value" is *computed and never wired*.
## HONEST-STATE SUMMARY
### 3.7 — What a PAYING USER sees right now that is not true
1. **`/compare` — fabricated grades, nav-linked + public.** Hardcoded Jokić A+ / Wembanyama A + fake "VYNDR VERDICT" (`compare/page.tsx:10-62`). No data behind it. **The single worst live lie.**
2. **FAQ — phantom processor.** "We use NexaPay" (`FAQ.tsx:28`); every legal/pricing page says Stripe. Also founder price inconsistency ($24.99 vs $19.99).
3. **FAQ + Features — Brier/CLV over-claim.** "Brier score and CLV… published / from day one · Public accuracy by tier" (`FAQ.tsx:498`, `Features.tsx:323`). No Brier surfaced anywhere; CLV held. `CANNOT DETERMINE` a live Brier surface — because none exists.
4. **Mobile edge board — placeholder edge%.** `MobileEdgeBoard.tsx:44` renders a miscalibrated edge feed, masking >40% to "—". The numbers ≤40% still come from a placeholder pipeline. Live on mobile Slate.
5. **Price triplet / EV markers advertised-and-absent.** Grade schema carries `ev_pct`/`p_win`/`model_odds`/`value`/`takeable`; all **NULL on live grades** — the "model price" leg and VALUE marker don't render though the design promises them.
Not inflated (verified honest): grades are B/C only with A/D/F below n≥20 → `pct:null` everywhere; `AccuracyBadge`/`ModelRecord`/`TierRecord`/ledger all honor the n≥20 gate and self-hide. Hit-rate (59%, n=763) shows **without ROI/CLV** (CLV frequently null) — thin, not false.
### 3.8 — The graveyard (built, no user can reach it)
1. **`BookComparison.tsx`** — 0 importers. Backend `/api/books` is live + honest; nothing renders it. (S2 headliner.)
2. **`ShareCard.tsx`** — 0 real importers (S3 share cards).
3. **`components/GameCard.tsx` (legacy)** — type-only import; superseded by `vyndr/GameCard`.
4. **proj-v1 `proj_ladder`** — the above-line probability ladder; computed, ledger-only, never served.
5. **arch-v1 + contact-v1** — challengers, ledger-only, never served ("measured, never served").
6. **`TerminalTemplates.tsx`** — all SAMPLE data; `/terminal` redirects → effectively unrouted.
7. **`DemoScan.tsx`** — defined, never rendered.
8. **`/intelligence`** — REAL signals feed, but 0 nav links (orphan; reachable only by typing the URL).
9. **`/soccer`, `/marketplace`** — REAL pages, 0 nav links (orphans).
10. **`/notifications`** — `RouteStub`, unreachable.
11. **Offseason hub** — full design spec, no page built (prod `/offseason`→404).
## FULLY DONE (all five YES) — 15
Landing · Dashboard/Slate · Scan flow · Grade result card · Grade badge · Ledger ·
Tier-record · Pricing · Public profile `/u` · Player profile · Team hub · Explore ·
WIRE/Ticker · Streaks/Hot list · Live tracking.
## NEAREST TO DONE (one column from YES) — the shortlist a single order could finish
| Surface | The one gap | Finish move |
|---|---|---|
| **Book comparison (S2)** | WIRED: NO | Route `BookComparison.tsx` onto the card from the live `/api/books` store (crown stays off — measured flat). *Backend already shipped.* |
| **Parlay lab** | DESIGNED: PARTIAL | Accept the System "PARLAY BUILDER" spec as the lab spec (functionally live) — a doc call, not a build. |
| **Compare (H2H)** | HONEST: NO | Replace the hardcoded SAMPLE with a real two-player fetch, or pull it from nav until real. |
| **Price triplet** | HONEST: PARTIAL | Make the price-aware EV layer actually produce `p_win`/`ev_pct`/`model_odds` on served grades (built, not firing). |
| **Newsletter** | LIVE: PARTIAL | Confirm/enable Listmonk env (config `CANNOT DETERMINE` from outside). |
| **Slip reader** | WIRED: PARTIAL | Add one nav/More-sheet link (page is live + honest). |
| **Calibration / edge board** | HONEST: NO | Fix the placeholder edge% feed (backend), or hide the board until real. |
Two-plus columns out (bigger builds): **Alt-line ladder** (design + wire the served
probability ladder), **System/Intelligence terminal** (wire the orphan `/intelligence`),
**Article media/S3** (build in-article figures; `ShareCard` dead), **Offseason hub**
(no page at all).
*Tags: all cells VERIFIED against repo/prod/bundle except — Newsletter LIVE (Listmonk env)
and a live Brier surface = CANNOT DETERMINE (none found). Nothing BLOCKED.*
---
# HONESTY PASS — applied 2026-07-27 (commit 6bc18d8, deployed)
Removed/hid every KNOWN live fabrication. REMOVE/HIDE only — no grade, snapshot,
scorer, pipeline, or real feature touched. Updated HONEST cells:
| Item | Was | Now |
|---|---|---|
| /compare (row 20) | HONEST: NO — hardcoded Jokić A+/Wembanyama A + fake VERDICT, nav-linked | Honest **in-development** state; removed from Nav + BottomTabBar. Real two-player build **pulled, awaiting real build**. |
| Pricing (founder copy) | $34.99 desk / struck $19.99 / FAQ $24.99 — wrong | Founder **Desk $44.99** (matches lib/checkout.js), **Analyst $14.99**; struck "regular" numbers removed; DeskShowcase $34.99→$44.99. First-100 counter is REAL (ClaimMeter→Stripe). No "first 50" desk claim (no such counter). |
| FAQ processor | "NexaPay" | **Stripe** (verified live: Next→Express→checkout.stripe.com). `nexapay.ts` + its webhook route were **PURGED 2026-07-27** (NexaPay Purge order) — cross-project contamination, never a real VYNDR path. Provider-side env/keys + the orphaned `user_profiles.nexapay_customer_id` column flagged for Kev. |
| FAQ + Features "Brier/CLV published from day one" | INFLATED (not surfaced) | Removed. Returns when Brier/CLV are actually surfaced. Backend Brier compute untouched. |
| Calibration / edge board (row 24) | HONEST: NO — MobileEdgeBoard placeholder edge% (masked >40%) | **Removed** from the Slate; phones show the real game cards. Component kept as dead code (hidden, not deleted) until a real edge feed exists. |
| Price triplet (row 19) | HONEST: PARTIAL — null model/EV rendered "MODEL READ WITHHELD · poisoned" (false quarantine) | New **NO_MODEL** honest-absent state: MODEL "—" / "NOT PRICED", no verdict. Fixes grade card + LiveHeroProp. EV layer still doesn't *produce* values (separate build). |
## KEPT ON THE BOARD (real work to finish — NOT cut)
- **Article media / S3 (row 23)** — real feature + free SEO/distribution. Finish, don't delete. Only the false "Brier/CLV" claim about it was corrected.
- **Newsletter / THE WIRE (row 21, 13)** — real. Capture live, honest.
## FUTURE MODEL INPUT (logged only — not built this order)
- **News / line-movement signal** — injuries, scratches, lineups, weather move props before books reprice. Wire later as a model input. This is **ADDITIVE** to the media surfaces, not a replacement for them.
## KNOWN HONESTY GAPS (not fixed this order — logged, not fabrication)
- **Hit rate 59% (n=763) shown without ROI/CLV** — thin, not false. ROI/CLV surfacing is a later build.
- **"0 pushes = mis-scoring" — RETIRED 2026-07-29 as a false alarm** (premise re-verified, report-only).
The displayed hit/miss denominators are NOT corrupted by a hidden push bug: the feed is still 100%
half-numbers (0 whole lines in 117,970 captured market lines / 6,050 snapshots / 1,141 ledger rows /
173 lock_lines), all 992 settled actuals are integers, and the smallest actual-vs-line gap in the
whole ledger is 0.5. Expected pushes = exactly 0. See the verdict block below.
- **CLV instrument REPAIRED 2026-07-28** (commit 6552281). Was: 59 usable closing_prob. Now: **406** (MLB 248, WNBA 158) — the collapse was `attachClosingProb`'s `.limit(50000)`/no-ORDER-BY read + write-once `market_unavailable`, NOT capture (95% per-prop coverage) or the join (0 key mismatches). **CLV finding, straight: MLB unders lag the close (mean 9.1 prob-pts, 74% lose); MLB overs +2.0; WNBA flat.** → the +4.57% MLB-C and over/under asymmetry are substantially stale-line artifacts. This UNBLOCKS the proof order (proj-v1.1), which gates promoting p_win/ev to served grades.
**Honest state after this order: "no KNOWN live fabrications" — not "provably none."** The audit was thorough (repo + prod), but absence of a claim of falsehood is not a proof of universal truth.
---
# proj-v1.1 TAKEABLE-EDGE PROOF — verdict 2026-07-29 (report-only, read-only)
**N-gate PASSED: overlap = 45** (settled ∩ proj-v1.1 ∩ MLB over ∩ CLV close ∩ fair_prob_lock ∩ takeable 160..+200). The CLV repair is what made n≥30 reachable. Edge basis = `proj_p_over_line proj_book_implied` (de-vigged fair, VERIFIED not raw book).
**VERDICT: NOT PROVEN.** proj-v1.1's takeable-edge does NOT beat the champion or clearly beat the close on MLB overs.
- Phase 1 buckets (descriptive; only the neg bucket clears ≥15): 6%+ edge (n=11) shows hit 70% / fair-ROI +0.57 / CLV +12.95pts — but even negative-edge rows show +2.7pt CLV (whole over-side is elevated = the stale-high concern).
- Phase 2 partial correlation (the real test): raw r(edge,CLV)=0.455 → **partial r(edge,CLV | price)=0.245, n.s.** at n=45 (t≈1.64, p≈0.11). ~half the raw signal is the shared fair_prob_lock term (mechanical). proj predicts the close itself only weakly (partial r(proj,close|lock)=0.281, n.s.).
- Phase 2.6 champion comparison (same 45 rows): **the CHAMPION out-predicts proj-v1.1** — champ-edge→hit r=0.250 vs proj 0.120; champ partial-CLV=0.380 (SIGNIFICANT, p≈0.01) vs proj 0.245 (n.s.). Positive-edge fair-ROI comparable (proj +0.39 n=19, champ +0.34 n=26).
- Shown, not judged: MLB unders (n=20) CLV 9.34pts, r(edge,CLV)=0.03 (contaminated, zero signal); WNBA — proj-v1.1 does not run (MLB-batting only), N/A.
**Conditional dependency (moot):** the verdict was to be conditional on the under-capture audit clearing the over-side. It's moot — proj-v1.1 fails Phase 2.6 (loses to the champion) BEFORE the audit applies, so NOT PROVEN regardless. The stale-high concern is corroborated (whole-over-side baseline +CLV; half of proj's signal mechanical).
**Notable:** the one statistically-defensible edge signal here is the **CHAMPION's** p_win predicting CLV on takeable MLB overs (partial 0.380, p≈0.01) — NOT proj-v1.1. That champion signal is itself still audit-gated (if MLB overs are whole-side stale-high, even it is suspect).
**What accrues a re-test:** ~20-25 more settled takeable MLB-over rows (to test proj's residual ~0.245 partial against zero), AND proj-v1.1 must demonstrate it beats the champion — which it currently does not. Promotion stays HELD.
---
# OVER-SIDE SKEW AUDIT — verdict 2026-07-29 (report-only, read-only)
Gates the champion's over-CLV signal (partial r=0.375, p≈0.003, n=62 takeable MLB overs — 0.2 CONFIRMED).
**THE THREE NUMBERS (Phase 4), n at each:**
- Mechanical baseline CLV (champ-edge ≤ 0, "no edge"): **+1.51 pts** (n=20)
- Champion high-edge CLV (champ-edge ≥ 0.05): **+8.64 pts** (n=37)
- **DIFFERENCE = the real edge: +7.14 pts**
**VERDICT: SURVIVES BASELINE.** The marginal (+7.1) is ~5× the mechanical floor (+1.5) and the price-controlled partial correlation stays significant. The skew is essentially ONE-SIDED (unders lag 7.0 overall; overs carry only a small +1.5 floor, NOT the +7 a symmetric two-sided over-skew would show). De-vig is CLEAN (`analyzeViaEngine1.js:539` pairs over+under from the same book/fetch — no fresh/stale pairing). Close is well-defined (draftkings vs betmgm over-prob r=0.921, n=151).
**→ GREENLIGHTS building the takeable-edge grade ON THE CHAMPION (engine1 p_win), NOT proj-v1.1** (which lost the proof). This is the project's first edge signal to survive an adversarial audit.
**FLAGGED — promising, NOT confirmed:**
- Thin n (62 overs / 37 high-edge / 20 baseline).
- Phase 2 lock-staleness check is **BLOCKED**: multi-book lines AT LOCK are not retained (`bookprices` is Redis current-only), so we cannot fully rule out that part of the baseline is lock-time staleness. The de-vig being clean + the small baseline make a large hidden skew unlikely, but it's not excluded.
- Sharp (pinnacle) reference covers only **8** props (sharp-CLV +3.17pt, directional hint only).
- This is one significant result among many computed this session — do not overstate.
**What would strengthen it:** retain multi-book at lock (enables the sharp/consensus lock-staleness check), and accrue more settled takeable MLB-over rows. Held: no promotion, no served p_win/ev, no capture fix — diagnosis only.
**UPDATE 2026-07-29 (commit c7067c8): the lock-multi-book gap is now CLOSED.** New `lock_lines` table (migration 033, applied+tracked) persists each graded prop's per-book lines at the lock moment (`lockLineCapture` in `snapshotService`, fenced RLS-service-role-only, grade byte-identical proven). This unblocks the staleness audit for FUTURE rows — it does NOT retroactively fix the existing 62. Confirmation still needs weeks of accrued lock+close+outcome. Populates from the next snapshot tick.
---
# HERO RANKING FIX — 2026-07-29 (commit 41b86e3, deployed)
The landing/hero (matrix row 1) selection was silently broken: it ranked on `ev_pct`, which is NULL on served grades, and **`Number(null) === 0`** made every prop tie at EV 0 → the "top read" was the FIRST takeable A/B prop in cache order — **arbitrary, dressed as ranked** (prod served Kelsey Mitchell, the #6 read by p_win). FIXED: rank by the **champion's p_win** (the only promising edge signal) among A/B **takeable-priced** reads (`isTakeable` 160..+200, same band as the proof/audit); strict-null guard; takeable filter excludes chalk; **no backfill** → honest empty state when nothing qualifies. p_win is ranking-only (never exposed; the route strips it). Display-only — reads caches, writes to nothing. No proven-edge/+EV/best-bet claim, no CLV/ROI/edge number. This makes the champion's p_win a real (display) consumer for the first time. Fingerprint VERIFIED: hero is the max-p_win read across sports (WNBA A), not old code's first-in-order MLB pick (Schanuel 135); untakeable chalk excluded. Visual auth-gated → data fingerprint.
---
# PUSH-SCORING PREMISE VERIFY — verdict 2026-07-29 (report-only, read-only)
Tested the standing ruling "push scoring is correct — do not touch." That ruling rested on
"100% half-number lines → pushes structurally impossible," which was true for the data it was
made on. If whole-number lines had entered the feed since, 0 pushes across settled rows would be
a real mis-scoring bug the ruling was shielding. **The premise HOLDS — the ruling stands.**
**Phase 1 — feed distribution, 4 independent populations, per sport AND per market (never blended):**
| population | what it covers | rows with a line | whole-number lines |
|---|---|---|---|
| `closing_captures` | raw captured market lines, 5 books, `book`+`sharp`, Jul 20-29 continuous | **117,970** | **0** |
| `model_snapshots` | every graded prop **incl. grader refusals** (not survivorship-filtered) | 6,050 | **0** |
| `ledger_entries` (public) | the settled public record, 11 markets | 1,141 | **0** |
| `lock_lines` | TODAY's lock-time per-book lines (freshest feed, migration 033) | 173 | **0** |
Per-market: MLB hits / doubles / rbi / total_bases / stolen_bases / runs / strikeouts / home_runs /
walks / earned_runs / outs / hits_allowed and WNBA points / rebounds / assists / threes — **every
market's min AND max line ends in `.5`** (e.g. MLB strikeouts 2.5-8.5, WNBA points 5.5-26.5, MLB
outs 3.5-19.5). No whole-number market is hiding inside a blended fraction.
**Phase 2 — the push branch would fire.** `outcomeService.js:151` `if (a === l) return 'push'`,
reached **after** `Number()` + `Number.isFinite` guards on both operands — a sound numeric compare,
not the `Number(null) === 0` string-vs-number class that hit the hero. It is the **single scoring
chokepoint** (`ledgerService.js:31` imports `settleResult`; no parallel hit/miss derivation exists
in `src/`), it is **unit-tested live** (`outcomeService.test.js:38`, `nbaSettlement.test.js:104`),
and both `ledger_entries.outcome` and `outcomes.result` CHECK constraints **include `'push'`** — a
real push would score, write, and persist end-to-end.
**Phase 2.6 — the decisive number.** Across 992 settled rows carrying an actual: **0 exact ties, 0
fractional actuals, and the smallest actual-vs-line gap is 0.5** — the arithmetic minimum between an
integer result and a half-number line.
**VERDICT: RULING HOLDS.** Expected push rate is **exactly 0 (P = 0), not "low"** — 0/992 is
*forced*, not chance. The "implausible" flag mistook an arithmetic impossibility for a suspicious
absence; the row closes honestly. Stale n corrected: the flag said 470 settled, it is now **1,097**
(593 hit / 399 miss / 105 void / 44 unsettled-today). Nothing modified — no scoring, settlement,
re-settle, or backfill.
**No latent bug either.** Because the branch is correct and covered, a whole-number market entering
later (NFL/NHL are code-wired but out of season; whole-number strikeout props exist at some books)
would be scored as a push automatically. The residual is a **monitoring** gap, not a scoring gap:
nothing alerts on the first whole-number line to enter the feed. Logged, not built.
---
# EDGE_PCT SCALE DIAGNOSIS — 2026-07-29 (report-only, read-only). Fork REPORTED, not chosen.
**What it is (0.1).** `analyzeViaEngine1.js:265-270``edge_pct = ((projection line) / line) × 100`,
signed by direction, where `projection = l5_avg ?? l20_avg ?? {stat}_per_90 ?? xg_per_90`.
**Independent of `p_win`** (so NOT tainted by the overconfidence that damns `ev_pct`) but it takes
**no price input at all**, so it cannot express a betting edge. **Arithmetically correct, MISLABELLED:**
honest as "% the projection differs from the line," **a lie at any scale as "EDGE."** Two independent
implementations — backend `edgePctFor` and `web/src/lib/gradeAdapter.js:25-31 computeEdge`; the grade
card renders the WEB one, so a backend-only fix would miss it.
**The cap (0.2).** `SANE_EDGE_MAX = 40` (`deskShowcaseService.js:31`: *"beyond this the (model-line)/line
value isn't a market edge"*), mirrored in `slateAdapter.js:613` and `MobileEdgeBoard.tsx:45`. A
self-declared plausibility bound from an earlier order, not a derived statistical one.
**Mechanism (0.3) = SMALL-DENOMINATOR EXPLOSION** — not units, not inversion, not a missing ×100.
`line` is the denominator and **86% of MLB rows (562/655) sit at line 0.5**. Max 620 = a ~3.6 projection
on a 0.5 line.
| population | n | >cap 40 | >100 | median | p95 | max | min |
|---|---|---|---|---|---|---|---|
| **MLB** | 655 | 65.8% (>50) | 13.6% | **60** | 180 | **620** | 86.7 |
| **WNBA** | 486 | 4.9% (>50) | **0%** | 12 | 49 | 77.8 | 51.7 |
| blended | 1,141 | **44.0% (502)** | 7.8% | — | — | 620 | — |
Per line (the proof): MLB 0.5 → 73.0% over cap, max 620 · MLB 1.5 → 43.8%, max 153 · WNBA 12.5 → 6.7%
· **WNBA 26.5 → max 1.9.** Matrix figures re-verified: **51.5% is stale → 44.0%; worst 620 is exact.**
**Shape: structurally broken for MLB, sane for WNBA** — and the scale is a *function of line size*, so
the metric is incomparable across markets **by construction**. No rescaling fixes that.
**Surfaces (Phase 2) — the "~13" count is NOT confirmed. Three surfaces RENDER it:**
| surface | live | access | role | user sees at 620 |
|---|---|---|---|---|
| `GradeResultCard.tsx:182,216,325` | YES (3 importers) | **auth-gated `/scan`****TAGGED FOR CHROME AUDIT** | display | **"+620% edge", raw + GREEN** |
| `DeskShowcase.tsx:40` | YES | **PUBLIC** `/pricing` | display | **"—"** (already honest) |
| `SoccerGradeResult.tsx:229` | YES | orphan `/soccer` (0 nav links, public by URL) | display | raw uncapped `X.X% edge` |
| `MobileEdgeBoard.tsx:47` | **DEAD** (0 importers) | — | sort+display | "—" (pulled in honesty pass) |
| `PropRow:45`, `GradeCard:32`, `ledger/page:40` | live components | — | **type-only, never rendered** | nothing |
| `contentTemplateService.js:164` | public `/api/content` | API | string | uncapped — **no page fetches it** |
**🔴 IT DRIVES TWO LIVE SORTS (the fork's load-bearing answer).**
1. `slateAdapter.selectTopGrades:469-471``grade → confidence → |edge| desc` → **dashboard TOP GRADES
top-10** (`dashboard/page.tsx:419`). **97.3% of rows (1110/1141) sit in a (date,sport,grade,confidence)
tie group of ≥2** (biggest 56), so |edge| is operative for essentially the whole slate — the **de facto
ordering** of that leaderboard.
2. `analyzeViaEngine1.js:506` — the Desk **alt-line ladder** is sorted by `edge_pct` desc.
**Two scale-INDEPENDENT defects inside that sort** (`Math.abs(numOr(g.edge, -Infinity))`): **(i) abs()**
on an already-direction-signed value ranks the model's strongest *disagreements* equal to its strongest
agreements (**177 negative-edge rows**: 58 B / 118 C / 1 F, worst 86.7); **(ii)** `Math.abs(-Infinity)
= Infinity` → a **missing edge sorts FIRST**. The `Number(null)` fabrication class again, new costume.
**Phase 4 correction — "nothing renders `ev_pct`" is WRONG.** `PriceTriplet.tsx:60,67,76` renders
`${pct(ev)} EV`, live and wired (scan → `gradeAdapter:143`). It shows nothing only because `ev_pct` is
NULL on served grades → `valueState.js:121` falls to **NO_MODEL honest-absent**. The right metric already
has a live honest render site, **starved of data, not unwired** — and the card's "EDGE" row sits exactly
where a price-aware number belongs.
**THE FORK (reported, not chosen).**
- **FIX** — dishonest: no rescaling turns a price-free projection gap into an edge (renaming, not fixing);
it silently re-ranks the dashboard top-10 (the hero-class bug just fixed); needs BOTH implementations.
- **HIDE** — cheap: 3 render sites, each already has a null branch (no layout breaks), and DeskShowcase
already proves the honest "—" pattern in-product. Not load-bearing for layout anywhere.
- **RECOMMENDED: HIDE the number and re-point the sort at `p_win`** — the hero order established p_win is
on 100% of recent ledger rows and is the only signal that survived an adversarial audit. Repairing a key
that is a 0.5-line artifact is not worth it. End state: **p_win ranks · ev_pct displays · edge_pct retires.**
The abs()/null-first sort defects deserve their own small order either way.
*Nothing changed: no edge_pct, scale, surface, sort, grade, ledger, or accruing edge touched.*
---
# GRADE-BOARD SORT FIX — 2026-07-29 (spec `specs/grade-board-sort.md`, shipped)
Display ORDERING only. Fixes two defects that were wrong at ANY scale, independent of edge_pct's
separate retirement (Order B, still held).
**Defects removed.** `selectTopGrades` ranked on `Math.abs(numOr(g.edge, -Infinity))`:
`abs()` on an already-direction-signed value ranked the model's strongest **disagreements** level with
its agreements (177 public ledger rows carry a negative edge); and `Math.abs(-Infinity) === Infinity`
made a **missing** signal sort **FIRST** — absent data as the top pick. Now: `grade → confidence →
takeable-gated p_win (nulls LAST) → SIGNED edge (nulls LAST) → input order`, scales never mixed.
The alt-line ladder (`analyzeViaEngine1:506`) no longer sorts by `edge_pct`; it is ordered
highest-p_win-first via the monotonic line rule (line-ASC for an over, line-DESC for an under) at zero
added compute.
**Three premise breaks found report-first.** (1) **`/api/props/top-graded` 404s in prod** — the
dashboard board's feed does not exist, so that board renders receipts/empty and the sort orders nothing
there today; the prior order's "97.3% of rows tie → the edge key decides the board" was a ledger
measurement wrongly extrapolated to it. (2) **p_win is stripped for unentitled tiers by design**
(`snapshotGating`, Session 67 — "shipping p_win is shipping the model price"); verified live, prod
`/api/snapshot` carries p_win on **0/8 MLB and 0/25 WNBA** grades, so the browser path uses the signed
edge and only entitled callers rank on p_win. (3) **Ladder rungs carry no per-rung price**, so the
hero's takeable gate is inapplicable there.
**Verified on real data, both sports, both paths.** Unentitled: WNBA (n=25) ordering CHANGED, MLB (n=8)
unchanged; signed edge non-increasing within 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, 36 pairs, 0 violations.
**Hero consistency, honestly:** same signal + same gate, different precedence by contract (board =
grade-tier-first "top GRADES"; hero = p_win-first "top read"). They agree exactly **within** the
leading tier (verified); across tiers the board may lead with an A the hero doesn't pick. Not a
contradiction — do not "fix" it by making the board ignore grade.
**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, building
the missing `/api/props/top-graded` selector, exposing p_win to unentitled tiers.