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:
@@ -139,6 +139,11 @@ scorer, pipeline, or real feature touched. Updated HONEST cells:
|
||||
|
||||
## 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.
|
||||
@@ -191,3 +196,160 @@ Gates the champion's over-CLV signal (partial r=0.375, p≈0.003, n=62 takeable
|
||||
# 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.
|
||||
|
||||
Reference in New Issue
Block a user