diff --git a/specs/STATE.md b/specs/STATE.md index 87c3681..2501268 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1,5 +1,5 @@ # VYNDR — STATE OF THE WORLD -### As of `9f60ceb` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** +### As of `6dd6f59` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** --- @@ -131,6 +131,73 @@ exist locally; harmless, the data restores completely. `specs/snapshot-retention.md` (Phase 2 report + schema) · `specs/audit-data/grade-collapse.md` · `specs/audit-data/gate-simulation.md`. +--- + +# 🟡 SESSION 76 — PLATOON SPLITS (regressed) (2026-07-21) +**Built + tested + deployed. Not reaching live props** (same plumbing gap). + +## Gates — both clear; one was ALREADY closed +1. **Splits** = statsapi pull, 1 call/hitter: + `people/{id}/stats?stats=statSplits&sitCodes=vl,vr&group=hitting`. +2. **🔴 CORRECTION — batter handedness is NOT pending.** S69's Tier-A join did + it: `statcast_aggregates.bats` = **604/604** (210 L · 327 R · 67 S). The S69 + STATE note said pending; the data says done. +3. **Point-in-time holds** if fetched before first pitch (a split queried this + afternoon can't contain tonight). A HISTORICAL backtest would use + season-final and leak → **clean measurement is forward-accruing.** + +## 🔑 THE SPINE — regression IS the feature +`regressed = (PA·observed + K·prior) / (PA + K)`, **K = 600 PA**, prior = the +hitter's **OWN** blended rate (not the league's). The question is *"is he +DIFFERENT vs this hand than he normally is"* → his own line is the null, so a +hitter with no evidence gets **nothing**. +Weights: `30 PA → 4.8% · 130 → 17% · 400 → 40% · 1000 → 63%`. + +### The make-or-break test (both halves, tested) +| Same .310 vs LHP | weight | multiplier | δ | +|---|---|---|---| +| **30 PA** | 4.8% | 1.011 | **0.003** | +| **400 PA** | 40% | 1.096 | **0.023** | +**8× apart.** A test asserts the ratio stays **> 5** — if the regression ever +breaks, the suite fails instead of projections quietly drifting onto noise. + +## Real data — most hitters get NOTHING, and that's correct +| Player | vs L | vs R | vs LHP mult | +|---|---|---|---| +| Josh Bell (S) | .259 / 126 PA | .248 / 265 PA | **1.005** (17% wt) | +| Aaron Judge (R) | .246 / 76 PA | .248 / 185 PA | **0.999** (11% wt) | +Bell *looks* like a platoon splitter until the sample speaks. Neither is +material. **Expect most hitters to get ~nothing — honest output, not failure.** + +## Honest-absent — 5 distinct routes, all exactly 1.0 +`batter_hand_absent` · `pitcher_hand_absent` · `splits_absent` · +`not_applicable` (stat) · missing SIDE → falls back to prior (weight 0), never 0. + +## INDEPENDENT of the environment — deliberate +Park+weather compose into ONE coefficient (both describe the stadium). Platoon +rides its **own** `matchup` slot with its own label — entangling them would make +both **harder to attribute** when the instrument scores them. Mirrored on under, +capped ±15%, **inverted for strikeouts** (higher K rate → higher K prop). + +## 🔴 THE PLUMBING GAP — now FOUR orders old +`snapshotService` still doesn't attach **venue + first-pitch + opposing-pitcher +hand** per prop and pass `environment` / `matchup` into `attachChallenger`. +**Park, weather AND platoon all adjust NOTHING live.** Everything else is ready. + +## 🔴 And still: no settled row carries `p_win` +All seven adjusters remain unmeasurable. +```sql +select count(p_win) champion, count(p_win_challenger) challenger +from ledger_entries where user_id is null; +``` + +## Recommendation (repeated, now urgent) +**Stop adding adjusters.** The next order should be ONLY: (a) attach venue + +first-pitch + opposing SP hand to props, (b) pass environment/matchup through, +(c) confirm `p_win` lands on a settled row. The measurement stack is now far +deeper than the plumbing feeding it. + + --- # 🟡 SESSION 75 — WEATHER MODULATION (2026-07-21)