diff --git a/specs/STATE.md b/specs/STATE.md index aadfe9b..cc990dc 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1,5 +1,5 @@ # VYNDR β€” STATE OF THE WORLD -### As of `f2da9dd` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** +### As of `927e867` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** --- @@ -131,6 +131,77 @@ 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 72 β€” TIER-1 MAPPINGS LIVE + TIER-2 HARNESS (2026-07-21) + +## πŸ”΄ PHASE 0 GATE β€” NO HISTORICAL OOS IS POSSIBLE (and why) +`statcast_aggregates` is **overwritten nightly by design** (Layer 1 = full +re-pull upsert) β†’ season-TO-DATE only, **no point-in-time history**. Classifying +a player for a Jul-15 game with today's aggregate feeds in Jul 15–21 β†’ +**look-ahead leakage**, making any "out-of-sample" verdict worthless. +**β†’ The harness reads the archetype vector RETAINED at grade time (S70 +instrument) and runs FORWARD-ACCRUAL.** Reported, not worked around. +*(If point-in-time archetypes are ever wanted, Layer 1 must retain a dated +snapshot per refresh β€” a real schema change, not a query.)* + +## Canonical names β€” asserted, not assumed +Every mapping references the axis keys the classifier **emits**; a test walks +both maps against `BATTER_AXES`/`PITCHER_AXES`. **A non-existent key would look +wired and never fire** β€” that's the failure this guards. + +## TIER 1 β€” LIVE (tautological, directional, MLB-only) +| Axis (canonical) | Labels | Stat | Dir | +|---|---|---|---| +| `strikeout` | PUNCHOUT / WHIFF | strikeouts | ↑ | +| `ground_ball` | SINKER / SEAM | home_runs_allowed | ↓ | +| `fly_ball` | FLY BALL / ELEVATOR | home_runs_allowed | ↑ | +| `control` | SURGEON ARM / PINPOINT | walks_allowed | ↓ | +| `power` | SLUGGER / BOMBER | total_bases, home_runs | ↑ | +| `contact` | TECHNICIAN / SURGEON | hits ↑, strikeouts ↓ | | +| `patience` | GRINDER / SNIPER | walks | ↑ | + +Average player untouched; UNDER mirrors OVER exactly. +**SPEED (BURNERβ†’SB) is HONESTLY ABSENT** β€” SB never reached the aggregate store, +so Layer 2 shelved the axis. `BATTER_MAP.stolen_bases = {}`, not invented. + +## TIER-2 HARNESS β€” `src/services/mappingHarness.js` +Three hurdles a plausible-but-worthless mapping fails: +1. **MARGINAL, not correlation** β€” scores the archetype against the + **projection's RESIDUAL** (`outcome βˆ’ p_win`). If the projection already + knows it β†’ `REJECT (redundant)`. **This is the double-counting catch.** +2. **OOS split by DATE, never random** β€” rows from one game share pitcher/park/ + lineup; a random split leaks. +3. **Direction validated from held-out data; a contradicted sign is REJECTED, + never silently flipped** (flipping = fitting noise). +Floors: `MIN_ROWS 200 Β· MIN_HELD_OUT 60 Β· MIN_EFFECT 0.02` β†’ else `INSUFFICIENT`. + +**Lifecycle encoded: `nominated β†’ live β†’ claimed`.** A survivor RUNS LIVE and is +measured; **only the quantified public claim waits.** Nothing sits dark. + +## Real candidate, end-to-end +`GROUND-BALL β†’ hits_allowed` β†’ **INSUFFICIENT β€” 0 / 200 settled rows**, because +no settled row carries `p_win` yet (S70 instrument starts at the next new lock). +Correct and expected verdict. + +## Gotcha worth keeping +My first synthetic fixture **aliased the carrier selector against the outcome +draw** and manufactured a 0.038 effect where zero was injected. The harness +rejected it correctly β€” but via the SIGN reason, not redundancy, which is how I +found the fixture bug. Draw now uses a coprime modulus. *Lesson: when a harness +gives the right verdict for the wrong reason, check the fixture.* + +## Still blocking everything downstream +**No settled row carries `p_win`/`p_win_challenger` yet.** Tier-1 adjusts real +projections now, but neither it nor any Tier-2 candidate can be evaluated until +volume accrues. Verify: +```sql +select count(p_win) champion, count(p_win_challenger) challenger, + count(*) filter (where challenger_delta <> 0) moved +from ledger_entries where user_id is null; +``` + + --- # 🟑 SESSION 71 β€” LAYER 3 STEP 2: CHALLENGER PROJECTION (2026-07-21)