diff --git a/specs/STATE.md b/specs/STATE.md index de83e66..aadfe9b 100644 --- a/specs/STATE.md +++ b/specs/STATE.md @@ -1,5 +1,5 @@ # VYNDR β€” STATE OF THE WORLD -### As of `474ebc5` (main, DEPLOYED + fingerprinted live), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** +### As of `f2da9dd` (main, DEPLOYED), 2026-07-21. This file opens every future session. **Start with the CURRENT STATUS + OPEN ITEMS block below.** --- @@ -131,6 +131,72 @@ 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 71 β€” LAYER 3 STEP 2: CHALLENGER PROJECTION (2026-07-21) +**Code-complete + deployed + unit-proven. NOT yet observed writing a live row β€” +see the verification gap below.** Champion serves users UNCHANGED. + +## What it is +`src/services/challengerProjection.js` β€” the champion's `p_win` **adjusted** by +the Layer-2 axis vector in **log-odds space** (a nudge can't cross 0/1 and means +the same at p=.5 and p=.9). Total capped at **0.45 log-odds** β€” a lean, not a +re-forecast. Every deviation is attributable: `challenger_adjustments` stores the +named axis + signed nudge. Only mechanically obvious stat↔axis pairs are mapped. + +**Identical to champion by construction** where there's no signal β€” unremarkable +player, thin sample, unmapped stat, or no classification all return the champion +value byte-for-byte with a stated `reason`. The A/B therefore differs ONLY where +archetype-awareness could help or hurt. + +**Isolation:** `adjust()` is pure; the champion field is read, never written; the +served snapshot payload is still `enriched` (untouched); a challenger throw is +caught so it can never break the pipeline it's measured inside. Statcast loads +ONCE per snapshot run β€” grade-time I/O stays at zero. + +## Induced on real players (local, real data) +| Prop | Champion β†’ Challenger | Via | +|---|---|---| +| Judge HR **over** | 0.42 β†’ **0.447** | BOMBER +.22, WHIFF RISK βˆ’.11 | +| Judge HR **under** | 0.58 β†’ **0.553** | exact mirror | +| Judge strikeouts | 0.55 β†’ **0.55 (Ξ΄=0)** | WHIFF RISK +.11 / GRINDER βˆ’.11 **cancel** | +| Skubal Ks over | 0.60 β†’ **0.702** | WHIFF, TRAPDOOR, CANNON aligned | +| Skubal hits-allowed | 0.50 β†’ **0.392** | a K arm allows fewer hits | +| Bell / 12-PA sample | **unchanged** | `no_distinctive_axis` / `thin` | + +## πŸ”΄ VERIFICATION GAP β€” read before assuming this is recording +**Zero live rows carry `p_win` or `p_win_challenger`** (`latest_lock` still +`2026-07-21 02:58:45`). Three compounding reasons: +1. **Immutability refuses to backfill.** Every prop on the current slate was + locked BEFORE these columns existed; `ignoreDuplicates` correctly skips them. + **First live rows arrive on the next NEW lock** (new game date / new line). +2. **`ledgerWritten` OVER-REPORTS.** It counts `chunk.length`, not actual + inserts β€” 14 induced snapshots all reported "ledger 25" while inserting + **zero**. This misled my own verification; worth a one-line fix. +3. **`statcast_aggregates` is MLB-ONLY (1,354 rows; 0 wnba/nba).** I induced on + WNBA, where the challenger correctly no-ops for lack of mechanism data. + **The challenger currently applies to MLB only.** + +**Verify with:** +```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; +``` +Expect first non-zero after the next MLB slate locks. + +## HELD +**Promotion** (challenger β†’ serving) and **any superiority claim** stay held +until the ledger proves it on segmented volume. Baseline volume: blended nβ‰ˆ500 +β‰ˆ 5 days; per-segment nβ‰ˆ200 β‰ˆ 2–4 weeks. + +## Next +1. Confirm first live champion+challenger rows after the next MLB lock. +2. Fix `ledgerWritten` to report real inserts. +3. Extend mechanism data beyond MLB (or accept MLB-only challenger). +4. Let volume accrue; then compare Brier/calibration segmented. + + --- # 🟒 SESSION 70 β€” LAYER 3 STEP 0+1: THE MEASUREMENT INSTRUMENT (2026-07-21)