Commit Graph

5 Commits

Author SHA1 Message Date
builtbykev 77e8937a56 P2-9: leaderboard stat labels (SB/ER/TB) + FLAG the grade-degradation root cause
DISPLAY FIX (shipped): the league leaderboard rendered raw snake_case
("stolen_bases U0.5", "earned_runs U2.5"). New canonical short-label lib
web/src/lib/statAbbrev.js (one source, CommonJS + unit-tested) maps stat_type
to SB/ER/TB/HR/K/PTS/… and ExploreHub routes through it. Unknown ids upper-case
their words so raw snake_case can never leak again.

FLAG (reported, NOT silently changed — per the audit's instruction): the "B at
45% confidence" is a BACKEND grading issue, diagnosed against live snapshot:
- 25/25 grades mismatch their own confidence vs grade_thresholds.json (B shown
  at conf 55 = the B- band; a systematic one-sub-tier gap on every prop). The
  surfaced `confidence` is not the probability that derived the letter (likely
  the data-sufficiency penalty applied to display-only).
- 9/25 have projection=0 — the MLB feature path feeds 0 instead of refusing
  (S58 insufficient_data), which also produces the P1-7 broken edge_pct.
Full write-up + do-not list: specs/audit-data/mlb-grade-degradation.md. NOT
re-lettering or shifting thresholds on the frontend — that would hide the bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 01:42:03 -04:00
builtbykev 55f5cdc57d P0-3a: dedupe the Explore leaderboard — one row per player+market + per-market cap
Phone audit: leaderboard flooded with 9 consecutive identical 'stolen_bases U0.5
45% B' rows. New shared lib/playerGrouping (dedupeLeaders + groupIntoLadders,
name-key aware, 7 unit tests): ONE row per (player, market family) keeping the
best-ranked, then a per-market cap (4) so no single prop type floods the board.
Applied to ExploreHub. Ledger cards + Consensus grouping follow in P0-3b/c using
the same lib.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 00:59:08 -04:00
builtbykev 1664e1b3d5 Wave 2B: Offseason never-dark hub — NewsWire + FuturesBoard on /explore
Extend /explore (ExploreHub) into the 365-day never-dark hub. Two new
self-hiding sections feed REAL always-available data into the offseason:

- NewsWire (components/vyndr/NewsWire.tsx): real ESPN headlines from
  /api/news/:sport (newest-first, mono timestamps, type chips, player/team
  links) + real injuries from /api/schedule/:sport/injuries (OUT/GTD chips,
  token colors). Reuses the retired TerminalTemplates INJURY_WIRE layout but
  never routes its sample constants. Self-hides when both feeds are empty.
- FuturesBoard (components/vyndr/FuturesBoard.tsx): real futures from
  /api/futures/:sport — championship/win-total/award markets, mono tabular
  prices + movement colored by the contract (shortening=green / drifting=amber
  / flat=dim, NEVER red; move shown ONLY when the backend supplies one).
  Carries the honest "TRACKED · NOT GRADED" label — no fabricated grades on
  futures. Self-hides when markets:[].
- ExploreHub is offseason-aware (via emptyState OFF_SEASON month check): the
  hub LEADS with futures + wire when the board is dark, COMPLEMENTS the live
  board in-season. Sport selector kept; each section self-hides independently.
- Testable pure helpers: lib/futuresMove.js (move→color, never red) +
  lib/newsFormat.js (timeAgo mono-stamp, ESPN type labels).

Contracts consumed (Wave 2A owns the proxy/service files); code self-hides on
fetch failure if a proxy isn't present yet.

Tests: tests/unit/newsWire.test.js + futuresBoard.test.js (26 new). Full suite
259 suites / 3144 green; web build exit 0. vyndrParityQA stays green
(mono data, no glitch on data surfaces).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 23:27:19 -04:00
builtbykev 7a35c96781 Session H (night2): design pass — §2 locks + honest waiting sweep
- QA.20: one meaning per color, locked — STEAM amber (caution), VALUE
  green (edge), red only at STALE/miss; revisions render the original
  grade struck through on slate AND ledger.
- QA.21: every new data surface (PriorReads, PlayerStreaks, ModelRecord)
  renders data in mono (tabular-nums via the .mono class).
- QA.22: waiting copy derives from the real cron schedule on every
  surface (StatStrip, dashboard, Explore) — no passive mystery anywhere.
- Explore empty state joined the honest-waiting lib.
- Sweep result: no raw brand hexes and no sans-serif data in tonight's
  components (sans only on prose/captions/hero letter — sanctioned);
  glow confined to A-tier badges + reveal chrome; 390px containment
  holds (wrapping chips, scroll-contained tables from Phase 3 rules).

Backend 2352 -> 2396 tests (205 suites), web build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 02:30:41 -04:00
builtbykev 80481d6f6a Session C (night2): the aggregator mounted — Explore hub, lens panels, one filter
- StreaksPanel renders THE LENS (interpreted read + tonight difficulty) +
  snapshot grade letters; streaks are FREE for every tier per the product
  definition (the picture is free, the read is paid) — only hot lists keep
  the free top-3 gate.
- One stat selection now filters ALL layers: card props narrow together
  with streaks + hot lists (Slate activeStat → slateGameToCardData).
- /explore = server SEO shell (daily-indexable: 'MLB Hit Streaks, Hot
  Hitters & Stat Leaders') + ExploreHub client: leaders + full streaks +
  hot lists, real-tier gated, defaults to the in-season sport.
- Landing teaser fixed: it pointed at NBA (off-season → self-hid forever);
  now MLB top-3 through the lens — the anonymous-visitor hook actually
  shows.
- Player dossier: ACTIVE STREAKS block (free, lens reads) via new
  ?player= filter on /api/streaks/:sport.
- Schedule layer already the Slate base (mergeSlate) — verified, no change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 00:47:01 -04:00