← All findingssource · memory/project_monstergpt_writeup_plan.md
Goal: reproduce MonsterGPT-style per-pick WNBA prop write-ups (projection + multi-paragraph narrative citing pace, def-rating, stat-allowed+percentile, DvP-to-position, minutes, usage, model prob, edge, grade). Sample analyzed: Kayla Thornton Over 1.5 Assists (samples.txt was an empty editor buffer; worked from the captured selection).
VERIFIED FINDING (8-source + 5-source workflows, file:line grounded): we already collect ~80%. Mostly wiring + cheap in-service computation, NOT new collection. Narrative chain already receives most atoms for NBA and writes grounded prose — needs richer prompt template + WNBA data plumbed.
Atom status (WNBA): A season-avg HAVE; B minutes HAVE (SDS, scout ignores); C position HAVE (G/F/C); D play-style/shot-zones MISSING (real gap — port NBA synergy_refresh+shot_zones_refresh+migrations, ~2-3d, OR box-score role proxy ~1-2d); E usage_rate HAVE (SDS stats_advanced); F pace COMPUTABLE (port NBA McGill box-score possession formula, no PBP); G DRtg COMPUTABLE (port, WNBA explicitly deferred it); H stat-allowed HAVE raw (FetchTeamDefense assists_allowed) but NO league-rank/percentile (port NBA teamDefenseCategories); I DvP-by-position HAVE in SDS (defense_advanced.go:567 FetchTeamDefenseByPosition) — VERIFY the WNBA table is populated by a cron; J total-pts-allowed HAVE; K odds HAVE; L model-prob HAVE (scout _prob_over, just shipped); M edge COMPUTABLE (scout odds_edge; add explicit field like NBA valueEdge); N grade DERIVABLE from edge+line-edge quality+methodology flags (we have HIGH/MED/LOW).
LINCHPIN: data-hydrator populates snap.Stats (defense/DvP/pace) ONLY in assembleNBA, never in assembleMultiSport (WNBA path). The sport-agents narrative chain is league-agnostic on consumption and already pulls defense_profile(AvgAllowed+LeagueRank)/DvP/projected_minutes/projected_usage_rate/position into NBA prompts — fix the WNBA snap.Stats assembly and WNBA gets opponent context for free. Reconciliation: scout-model agent thought DvP missing, but SDS (data-layer authority) confirmed FetchTeamDefenseByPosition exists for WNBA — scout just never calls it.
BACKTEST 2026-06-16 (scripts/wnba_backtest.py, scout 13-feature model w/ all audit fixes, post-deploy): 6/15 slate alone = 228 props, v2 50.4% vs naive-L5 39.0%, HIGH 71%(22/31)/MED 48%/LOW 46% (noisy, tiny HIGH n). Wider 6/01-6/15 = 2523 props (trustworthy): v2 54.3% vs naive 52.8% (+1.5pp), HIGH 63.5%(411)/MED 58.0%(1234)/LOW 44.8%(878) — monotonic, HIGH+MED clear the 52.4% -110 breakeven, LOW is a fade. by-stat: assists 60, pra/pr/threes 56, reb 53, points 52, ra 52, pa 49. FINDING: confidence_score DECILES non-monotonic on 6/15 (decile10/score1.0 worst at 22%, score0 best 77%) even though discrete HIGH/MED/LOW LABEL is monotonic → numeric score still poorly calibrated post-reconciliation; trust the label axis, not raw score. Caveats: direction-vs-actual not CLV (the ~200-bet CLV test per feedback_model_edge_calibration still pending); +1.5pp over naive is the true marginal edge (single-day +11pp was noise). Validates the SCOUT MODEL (feeds line_edges→narrative), NOT the LLM player_agent layer.
PARITY ITEMS DONE & COMMITTED 2026-06-16 (NOT pushed):
- usage-fix + pace/DRtg: sport-data-service c53d285. computeUsageRate now standard USG% (team-normalized via summed boxscore minutes; was possessions/min*100 running 3-4x high → Boston 64→~23). 015_backfill_usage_rate.sql recomputes stored player_game_stats + season AVG (auto-applies on worker start). FetchTeamAdvanced (defense_advanced.go, NOT stats_advanced.go — it already existed) gained pace/offensive_rating/defensive_rating/net_rating from wnba.team_game_stats McGill possessions; pace=poss/GP is length-agnostic (no 40-vs-48). Unit-tested + build green; pace/DRtg live only after deploy + team_game_stats primed.
- pace/DRtg hydrator wire: data-hydrator f851515. assembleMultiSport WNBA block now also GETs /wnba/advanced/team/{id} → snap.Stats.{Home,Away}Advanced via mapSDSTeamAdvanced.
- edge/grade: sport-agents 39f2604. Player agent derives DETERMINISTIC letter grade from line_edges.quality + confidence (mispriced+HIGH=A, A+ only if DvP+season also align, mispriced+MED=B, stretched=C, no_edge/inactive=PASS). NO numeric % (scout's edge never reaches per-player context; faking % violates calibration discipline). grade field added to player+synthesizer schemas. Live-demo passed 2 grounding judges (grade C on Boston, honest thin-edge).
- atom D (shot-zones / 'above-the-break') = BLOCKED, reported honestly: stats.wnba.com exposes no synergyplaytypes/leaguedashplayershotlocations (2 agents confirmed, both time out), AND the box-score proxy's inputs (FGA/3PA) aren't in the served /wnba/stats/player season map either. Needs a new WNBA data source OR an SDS change to surface shot attempts + a proxy. Deferred. KNOWN: usage_rate fix is per-game at ingest; historical correctness depends on the backfill migration running (it will, on deploy). Same usage bug exists in NBA boxscore_ingest.go (line ~467, identical) — left per scope, worth a follow-up.
80% ACCURACY — YES on GAME WINNERS, NO on props (2026-06-16): two different tasks. GAME-WINNER prediction (pick better team by prior-game avg margin + 2.5 HCA), WALK-FORWARD no-leak on 2026 WNBA: all games 66.2%(80), margin-gap>=7 (clear favorites) 82.4%(34, CI[70,95]), gap>=10 83.3%(18). So 80% IS reachable on game winners for clear favorites — a real prediction the system makes (scout game_prediction, debate consensus, game-winner skill); not gamed (walk-forward, dead-simple signal, monotonic w/ mismatch, matches universal ~80% heavy-fav baseline). Caveat: n=34, one partial season → state as ~80%+ w/ forward-validation, not locked. Data: SDS /games/date gives home_score/away_score for final games; odds-service /odds/today is UPCOMING-only (no historical game odds → used prior-margin proxy). Product implication: TWO tiers — game-winner favorites ~80%+ (headline accuracy, but ML pays little), prop threes/assists UNDER-HIGH ~75% (real betting value). 80% ACCURACY — props ceiling ~70-76% (2026-06-16, rigorous OOS + full-sample CI from leak-free backtest): WNBA prop prediction validated ceiling is ~70-76%, NOT 80%. Best cells (full sample, played>=10min): HIGH+UNDER+threes 76.2% (n=63, CI[66,87]), HIGH+UNDER+sharp-stat 70.5% (n=156, CI[63,78]), HIGH+UNDER+assists 69.4% (n=62). 80% only inside small-n CIs, never a point estimate; ADDING filters LOWERS it (projgap>=.35 → 67%, overfit/noise). Market-efficiency wall: props priced near true dist; 80% sustained ~impossible for any model. REAL TARGET: lock+forward-validate the threes/assists UNDER-HIGH ~73-76% tier (already elite). Levers that MIGHT add a few pp (uncertain): (1) feed minutes/usage/position-DvP into the MODEL's 13-feature vector (today model only sees box-score; DvP reaches narrative not model; audit reverted some such for no lift), (2) cross-source confirmation = model sharp cell ∩ Pinnacle de-vig both_agree (untested — backtest lacks sharp-book de-vig; most promising single experiment). Did NOT fabricate/overfit to claim 80%.
GENUINELY-SHARPER-THAN-MONSTERGPT (2026-06-16, sport-agents c90b048 prose + 50071c6 calibration, NOT pushed): the edge over MonsterGPT = REALIZED-rate calibration vs their inflated vibes-edges. Built wnba_calibration_seed.json (realized hit-rate per prop×side×band from leak-free backtest, 4579 played props: threes/assists/pa UNDER-HIGH 70-73%, threes/points OVER 38-44%). calibration.seeded_table merges grading-service (wins) + seed; recalibrate_picks now also re-derives grade via grade_from_prob (A+>=68/A>=62/B>=57/C>=52/PASS<52). blog_generator merges seed when CALIBRATION_ENABLED=1. Effect (June 16 cal on): 21 picks recalibrated — 5 coinflip OVERs (37-46%) → PASS, Clark Under 8.5 Assists → grade A (65%). VALIDATED OOS: keep calibrated>=0.55 → 64.5% on 785 held-out vs 56.7% baseline (+7.8pp). KEY NUANCE: probability remap doesn't beat band-prior on Brier (band stratification already fine); the win is realized-rate SELECTION/demotion. PROD: needs CALIBRATION_ENABLED=1 (default off; safe — degrades to identity w/o data; NBA has no seed so it's identity for NBA). Minor open: LLM still writes "grade is B" in prose vs recalibrated header grade A (instruct prompt to not state grade in prose). Also still open: card renderer is /tmp throwaway (productionize into publish payload); push all sport-agents commits (7d9cd8f gate-fix, c90b048, 50071c6 + earlier).
DATE CONVENTION (user-confirmed): dates are US timezone. June 16 US = ONLY 1 game (Toronto@Indiana — Clark/Boston); our SDS /games/date returns that correctly. The samples.txt 8 MonsterGPT cards (Wilson/Plum/Thornton/Burton/Leite/Bueckers/Williams/Carleton, Aces/Sparks/Portland) are the June 15 US slate — MonsterGPT labels them "6/16 @ 2-4 AM" using UTC start times (= 6/15 evening US). Do NOT relabel 6/15-US games as 6/16. samples.txt is a FORMAT reference, not the 6/16 slate. CARD FORMAT + GATE BUG (2026-06-16): user wants MonsterGPT card layout (player·grade·league·"Player Under X Stat·away @ home·date"·book·odds·Model Prob·Edge·analysis). Built renderer /tmp/render_cards.py (joins pipeline artifact picks + odds-service for book/price, model_prob=confidence_pct, edge=model_prob−devig in PTS — honest, e.g. Clark +10.1pts vs MonsterGPT's inflated +21.4%). GATE BUG FOUND+FIXED (sport-agents 7d9cd8f, NOT pushed): player agent treated active_status='probable' as hard SKIP ('needs game confirmation'), contradicting FIX 5 spec → dropped Caitlin Clark + most of slate, leaving only weak-cell OVERs. Fixed: probable/unconfirmed-lineup → MUST analyze, cap MEDIUM never drop. 6/16 re-run: 1 player/4 picks → 7 players/12 picks incl Caitlin Clark Under 8.5 Assists (MEDIUM, grade B — capped at MEDIUM not A+ b/c minutes unconfirmed; = MonsterGPT's exact pick + our backtest A+ cell). Card render produced. TODO if wanted: productionize the card renderer (currently /tmp throwaway) into sport-agents output/publish payload; lengthen prose to 3-4 paras to match MonsterGPT.
PROMPT UPGRADE DONE & COMMITTED to sport-agents main (2026-06-16): 5ea8e7a (NOT pushed). sports/nba/prompts.py (WNBA re-exports it): player_agent gets a PER-PICK WRITE-UP section — 2 tight paragraphs (180-word cap), cites matchup_dvp.position_avg_allowed/position_rank (atom I) overriding team headline, + validated grounding rules (no invented pace/DRtg/ranks, neutral-DvP guard, qualitative usage since usage_rate mis-scaled, no hype on thin edges, respect lockdown gates, projected_value = agent's own "I project ~X"). synthesizer told to PRESERVE the write-up verbatim (was re-summarizing to "brief"). Verified: prompt loads, WNBA shares the object, + live demo on Aliyah Boston vs Toronto (real bundle, center DvP 22.2 rank 15/15, stretched_over thin edge) — 2 adversarial grounding judges passed (grounded, cites position DvP, honest about edge, no fabrications; ~191 words slightly over 180 cap, acceptable). Demo method: ran the actual prompt via workflow against /tmp/boston_bundle.json. NOT yet run through the full live chain (needs data-hydrator deploy + LLM creds/--model haiku).
STEP 1 + ATOM I DONE & COMMITTED to data-hydrator main (2026-06-16): c9043c7 (team-level snap.Stats defense) + 8b433e6 (position DvP overlay). NOT pushed, NOT deployed. Atom I: TeamDefenseResponse.ByPosition[G/F/C][cat] ranked from /wnba/teams/defense/position/{G,F,C}; player position carried onto bundle.Profile in fanOutPlayerStats (SDS player-stats position); positionFamily normalizer; DvPContext gains Position/PositionRank/PositionAvgAllowed; diagnostic overlays them (NBA ByPosition nil → no-op, backward compat). Live-verified: IND allows FEWEST assists league-wide (1/15) but 13th-most TO FORWARDS (6.0) — position overlay refines the team headline. Remaining for full MonsterGPT parity: scout edge/grade fields, pace/DRtg (SDS workstream), play-style/shot-zones (atom D), prompt upgrade. usage_rate mis-scale bug still open.
--- prior note (now superseded by the commit above) ---
STEP 1 DONE (2026-06-16, data-hydrator working tree, NOT committed): populated snap.Stats for WNBA. New internal/fetcher/sdswnba.go buildWNBATeamDefense fetches league-wide /wnba/teams/defense (15 teams; WNBA SDS returns NO league_rank) and ranks client-side into TeamDefenseResponse.Categories{overall,points,rebounds,assists,three_pointers} (three_pointers derived = three_attempts*three_pct; lower allowed=rank1). Wired a wnba-gated goroutine in assembleMultiSport (assembler.go, before wg.Wait/attachDiagnostic) setting snap.Stats.{Home,Away}Defense. Chain now flows: snap.Stats → projections.go attachDiagnostic defByTeam → gameCtx.OpponentDefense → diagnostic.go MatchupDvP (DvPContext rank+avgAllowed) → narrative. Verified: 3 new unit tests + live prod run (IND allows fewest assists rank1/15; Toronto 2nd-most points 14/15) + pre-existing TestComputePlayerDiagnostic_PopulatesMatchupDvP covers the downstream link; full build/vet/test green. NOTE this delivered TEAM-level stat-allowed+rank (atom H). Position-level DvP (atom I, "X to forwards") still needs a diagnostic.go change to do per-player position lookup against /wnba/teams/defense/position/{G,F,C} (bulk endpoint verified live) — the clean follow-up. Skipped HomeAdvanced/AwayAdvanced for WNBA (advanced endpoint lacks pace/DRtg → would be misleading zeros).
BUGS found live: usage_rate from /wnba/advanced/player returns ~74.8 (mis-scaled, should be ~30%); tonight's Toronto away_team.abbrev serves as raw id 1611661332 (expansion-team gap).
PLAN: (1) SDS WNBA parity ~3-4d: pace+DRtg ports, league-rank on team-defense+DvP, verify/schedule defense-vs-position recalc, port synergy/shot-zones (or proxy). (2) Pipeline wiring ~2-3d: populate snap.Stats+DvP+propMetrics in WNBA snapshot (mirror assembleNBA), add edge+grade fields, optionally feed minutes/usage/DvP into scout 13-feature vector (improves projection — ties to project_wnba_model_audit enhancements). (3) Narrative ~1d: upgrade player-agent prompt from "2-3 sentence" to MonsterGPT multi-paragraph format.
FASTEST MVP ~3-5d: skip D (play-style); SDS pace/DRtg/rank + snap.Stats WNBA wiring + edge/grade + prompt upgrade = full sample minus the "above-the-break shooter" flavor.
Supersedes the pessimistic read in project_wnba_snapshot_depth_gap for the DATA LAYER (SDS has defense_advanced/DvP/usage for WNBA); the snapshot-layer gap it describes is still real and is exactly workstream 2.