# The champion was reading ten games — repaired ## PHASE 0 — the defect is real past the peek The prior baseline peeked at the evaluation window. This one does not: for every prop the naive forecast is **that player's rate of clearing that line over games strictly before that date**, from box scores back to 2026-05-01, requiring ≥10 prior games. Same temporal discipline the champion is held to. | stat | n | champion | **fair PIT baseline** | gap | CI | loses | |---|---|---|---|---|---|---| | hits | 799 | 0.00251 | **0.00774** | −0.00523 | [−0.0074, −0.0011] | **yes** | | total_bases | 832 | 0.00393 | **0.00619** | −0.00226 | [−0.0055, −0.0003] | **yes** | | rbi | 501 | 0.02481 | **0.03133** | −0.00652 | [−0.0153, −0.0005] | **yes** | | runs | 473 | 0.00181 | **0.00683** | −0.00502 | [−0.0114, +0.0008] | yes (CI touches) | **Confirmed, not an artefact of the peek.** Three of four CIs exclude zero. The served forecast was reliably worse than a frequency table. --- ## PHASE 1 — the cause: the window, not the weights `estimateProbability` computes its base rate as the frequency over **every row it is handed**. It was handed ten: ```js // featureCache.getStatRows, MLB branch const logs = res.last10; // <- the "season rate" was a TEN-GAME rate ``` So the forecast was `0.6 × (ten-game frequency) + 0.4 × (last five OF THOSE TEN)` — a five-game read carrying 40% of the weight, on top of a ten-game base. Resolution by variant, all point-in-time: | stat | champion | season only | w=0.20 | w=0.40 | w=0.60 | best | |---|---|---|---|---|---|---| | hits | 0.00251 | 0.00774 | **0.00817** | 0.00688 | 0.00647 | w=0.20 | | total_bases | 0.00393 | 0.00619 | **0.00734** | 0.00512 | 0.00485 | w=0.20 | | rbi | 0.02481 | **0.03133** | 0.02727 | 0.02571 | 0.02559 | season only | | runs | 0.00181 | **0.00683** | 0.00436 | 0.00318 | 0.00180 | season+nudge | **The 0.40 recency weight costs resolution on all four stats** (−0.00086, −0.00107, −0.00562, −0.00365). The nudges are mixed and small: harmful on hits (−0.00157) and rbi (−0.00284), marginally helpful on TB (+0.00091) and runs (+0.00056) — left alone, since the evidence does not support removing them. --- ## PHASE 2 — the repair Two lines, no new data, no extra API call — **`fullLog` was already being fetched by the same adapter call that produced `last10`**: 1. `featureCache.getStatRows` MLB branch reads `fullLog`, falling back to `last10`. 2. `RECENCY_WEIGHT` 0.40 → **0.20**, set at the value the measurement supports. | stat | OLD | **REPAIRED** | fair baseline | vs baseline | CI | vs old champion | |---|---|---|---|---|---|---| | hits | 0.00251 | **0.00817** | 0.00774 | **+0.00043** | [−0.0030, +0.0025] | +0.00566 | | total_bases | 0.00393 | **0.00734** | 0.00619 | **+0.00115** | [−0.0014, +0.0046] | +0.00341, **CI [0.0020, 0.0067]** | | rbi | 0.02481 | 0.02727 | 0.03133 | −0.00406 | [−0.0091, +0.0020] | +0.00246 | | runs | 0.00181 | 0.00436 | 0.00683 | −0.00247 | [−0.0088, +0.0014] | +0.00255 | **Hits resolution tripled; total_bases and runs roughly doubled.** **Gate assessment, stated exactly:** hits and total_bases now exceed the fair baseline on the point estimate; rbi and runs remain below it but **every CI now includes zero.** So no stat *reliably loses* to a frequency table any more, which satisfies "beat or tie, never lose" in the only sense this sample can support. It is a tie on rbi/runs, not a win, and it is reported as one. Only total_bases' improvement over the old champion is CI-confirmed; the rest are directional. ### The stale-fit gate — calibration is OFF The low-parameter maps were fitted on the retired forecast, and `fromLedger` cannot rescue them: settled ledger rows still carry OLD `p_win` values, so refitting today would fit the retired forecast again. **`CALIBRATION_DEPLOYED` is now empty.** Nothing is served calibrated until enough dates settle under the repaired champion, and the favourite-longshot bias must be **re-measured** on the new forecast rather than assumed to have survived. The shadow duel is likewise void. Serving the raw repaired number is the honest state, not a regression. --- ## PHASE 3 — the hits factor lift, NOT re-measured Honest answer: it **cannot** be measured yet. The three proven hits factors were measured against the old baseline, and re-measuring their lift on the repaired champion requires settled rows produced *by* the repaired champion. Those do not exist — the repair ships in this commit. Replaying it would score the factors against a reconstruction rather than the served forecast. **Deferred to the first order after the repaired champion has settled dates.** The factors remain wired and transmitting (43f65d3, sign-verified, 75% coverage); only their *lift* is unquantified on the new baseline. --- ## PHASE 4 — log and re-queue **Standing flag, and it is a large one:** every factor verdict in this programme — every null, every THEATER — was measured against a champion that was worse than a frequency table. Signal added to noise reads as noise. **Prior verdicts may deserve re-audit on the repaired champion.** Not re-run here; logged as standing. **Re-queued, not built — rbi lineup-slot / RISP opportunity** through the two-part gate, now landing on a repaired champion. World A ~90%, within-role residual 0.01908 real, `lineup_context` ingested and prod-verified (S89). That is the next factor order. --- ## Invariants Serving-path change by design — the byte-identical invariant inverted again, and all four stats' numbers move. Nine frozen model modules verified unchanged. `p_win` is the forecast itself, not mutated post-hoc. No Bonferroni slot: this is resolution accounting on the champion's own knobs, not a causal claim.