diff --git a/specs/STATE.md b/specs/STATE.md index cc990dc..c2fdc2b 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1,5 +1,5 @@ # VYNDR β€” STATE OF THE WORLD -### As of `927e867` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** +### As of `3ac91c3` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** --- @@ -131,6 +131,80 @@ 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 73 β€” PARK FACTORS (derived, composable) (2026-07-21) +**Derivation + composition BUILT and proven. PIPELINE WIRING INCOMPLETE β€” see +the gap.** Layer 3 Step 4. + +## πŸ”΄ PHASE 0 β€” A STATIC PARK TABLE ALREADY EXISTED (nearly built a duplicate) +`src/data/parkFactors.js` β€” **static FanGraphs table, Session 15**, consumed by +`computeFeatures.js:374`. I almost created a second source of truth. +- **NEW derived service = `src/services/parkFactors.js`** (distinct path). +- **Checked:** the static table reaches `computeFeatures` but **NOT + `probabilityEstimator`** β†’ it does **not** currently touch `p_win`. +- **This reframes the order:** if the champion ever does see park, adding it to + the challenger is DOUBLE-COUNTING β€” precisely what the S72 harness catches. + Park therefore ships as a **nominated challenger to be TESTED**, not an + assumed win. + +## Derivation (ours, from real games) +statsapi `schedule?hydrate=linescore,venue,scoringplays` β€” **one call per date +range**. Every HR scores β‰₯ the batter, so **HR totals are fully recoverable from +scoringPlays**. Derived from **5,055 real games, 2022–2025**: + +| Park | HR base | RUN base | +|---|---|---| +| Dodger Stadium | **1.106** | 1.023 | +| Coors Field | 1.029 | **1.099** | +| Oracle Park | **0.923** | 0.969 | +| PNC Park | **0.917** | 0.994 | + +**18 parks present Β· 18 honestly absent** (below the 150-game regime floor). +*(Sample was a partial pull β€” month-chunked with `-31` end dates drops days β€” so +absences are inflated. Re-derive with proper date ranges before trusting counts.)* + +## COMPOSABLE β€” the architectural point +Emits a **multiplier ~1.0**, never an additive nudge, because weather must +modulate it next: `effective = park_base Γ— weather_mod Γ— …`. Additive terms +don't compose (5% park + 8% wind = 1.05Γ—1.08, **not** +13%). The challenger +converts via `Math.log(env)` into the same log-odds space the archetype nudges +use. A test multiplies a placeholder weather term on to prove the shape composes +**with no rearchitecting**. + +## Honest states (three, distinct) +`absent` (thin β†’ adjust nothing) Β· `present` (adjust) Β· **`weather_na`** (dome β€” +**the park factor STILL APPLIES**; the flag exists so next order's weather does +nothing there). **N/A β‰  absent** β€” conflating them either drops a valid park +factor or applies wind indoors. + +## Directional by prop-owner +`home_runs` and `home_runs_allowed` both key off `hr_base` **in the same +direction** β€” the sign lives in the STAT, not the park. + +## Structural breaks + shrinkage +A deviating season starts a new regime **only if the NEXT season confirms** (one +odd year = noise). Only post-break seasons used. Factors **regress toward +neutral by sample size**; fine conditioning (parkΓ—hand, parkΓ—batted-ball) stays +**unavailable** until its own larger floor (400 games). + +## πŸ”΄ THE GAP β€” park is NOT yet reaching live props +`adjust()` accepts `environment`, and `parkMultiplier` produces it β€” but +**nothing in `snapshotService` looks up a venue per prop and passes it**. Props +carry `game_time` (via gameBinder) but **no venue**. Remaining wire-up: +1. Attach venue to each prop (statsapi schedule already returns it). +2. Persist derived factors (a table or cached blob) + a refresh job. +3. Pass `environment` into `attachChallenger`. +**Until then park adjusts nothing live** β€” the derivation and composition are +proven, the plumbing is not. + +## Next +1. Wire venue β†’ prop β†’ park lookup (the gap above). +2. **Weather modulation** β€” composes by multiplying `park_base`; domes already + flagged `weather_na`. +3. Still blocking all measurement: **no settled row carries `p_win` yet**. + + --- # 🟒 SESSION 72 β€” TIER-1 MAPPINGS LIVE + TIER-2 HARNESS (2026-07-21)