diff --git a/specs/STATE.md b/specs/STATE.md index f15ddae..87c3681 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1,5 +1,5 @@ # VYNDR — STATE OF THE WORLD -### As of `f33091d` (main, DEPLOYED + fingerprinted live), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** +### As of `9f60ceb` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** --- @@ -131,6 +131,70 @@ 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 75 — WEATHER MODULATION (2026-07-21) +**Built + tested + deployed. NOT reaching live props** (same venue gap as park). +`effective_environment = park_base × weather_mod`. + +## Phase 0 — both feeds free + keyless +statsapi `/venues?hydrate=location` → lat/lon for every park, ONE call. +Open-Meteo hourly (temp / wind speed / wind direction), **available hours before +first pitch** = when we project. Verified live. + +## 🔑 THE SPINE — two weather values, NEVER crossed +| Value | Purpose | Lands in | +|---|---|---| +| **FORECAST** at projection time | **drives** the live adjustment **AND is what the instrument measures** | `ledger_entries.wx_forecast` + `env_*` | +| **ACTUAL** at game time | future SELF-DERIVED weather factors ONLY | `game_context.wx_*` | +Using the actual to measure tonight = **scoring ourselves on information we +didn't have**. The actual comes from Open-Meteo's **ARCHIVE** endpoint — asking +the *forecast* endpoint after the fact returns a re-forecast, not what happened. + +## 🔴 WIND SIGN — the easiest error in the file +`wind_direction` is METEOROLOGICAL (where it comes FROM). Blowing OUT to centre += arriving FROM the opposite bearing → **`(from + 180) % 360`** before comparing +to the CF bearing. **Getting this backwards inverts every wind adjustment.** +Pinned by a test on straight-out / straight-in / crosswind. +`CF_BEARING` (30 parks) is **public geometry**, same class as the dome list. A +park missing from it gets **no wind effect** (keeps temperature). + +## Three honest do-nothing states — all 1.0, distinct +- **`dome_na`** — weather doesn't apply; **the PARK factor STILL does** (verified: + domed venue keeps its sub-1.0 base while weather stands down). +- **`forecast_absent`** — none available for this park/time. +- **`sub_threshold`** — real forecast below a meaningful bar (a 0.3% nudge on a + light breeze is false precision). +Plus `not_applicable` for stats weather says nothing about (strikeouts). + +## Conservative + ledger-tunable +Every magnitude is an env var (`WX_WIND_PER_MPH` .006 · `WX_TEMP_PER_DEG` .003 · +thresholds 5 mph / 8°F), total **capped ±12%**. Nothing asserted — a nominated +challenger that earns its place or is cut. + +## Induced (Wrigley, CF bearing 32°) +`wind FROM 212° @15mph → 15mph straight OUT · wx 1.12 × park 1.06 = 1.187 · +δ +0.043` · under mirrors · **pitcher HR-allowed moves WITH hitter HR** (both are +P(over) on a ball leaving the park) · wind IN → 0.955 · calm/dome/no-forecast all +→ 1.0 by three different routes, park base still applying. + +## ⚠️ ORDER CORRECTION (recorded) +The order described wind-out as helping the hitter and **hurting** the pitcher's +HR-allowed as "opposite sides." In PROP terms both go the **same** way — the +HR-allowed OVER is also more likely. **The sign lives in the STAT**, as already +established for park. Implementation follows that, not the phrasing. + +## 🔴 STILL OPEN (unchanged) +1. **Neither park nor weather reaches live props** — `snapshotService` still + doesn't attach venue+first-pitch per prop and pass `environment` into + `attachChallenger`. Everything else is ready; this one link is missing. +2. **No settled row carries `p_win`** — all six adjusters remain unmeasurable. +```sql +select count(p_win) champion, count(p_win_challenger) challenger +from ledger_entries where user_id is null; +``` + + --- # 🟢 SESSION 74 — PARK BASE (public, pluggable) + GAME-LEVEL CAPTURE (2026-07-21)