# `opp_nine_k_prior` — killed by quant gate, live 2026-07-17 to 2026-07-19 > **Dates corrected 2026-07-20.** This document previously said the > feature "had been live since 7/17" and was killed on 7/18, and > described the revert as not yet deployed. All three were wrong, and > the site's disclosure inherited the error. > > The feature was live in **production** from **2026-07-17 19:20 UTC** > (commit `7506da3`, when it entered `FEATURES`) until **2026-07-19 > 16:11 UTC** — the mtime of `data/k_model.joblib`, when the droplet > actually retrained without it. The revert was *committed* 2026-07-18 > 12:19 UTC (`babd80f`), but a commit is not a deployment: production > served the withdrawn model for a further **28 hours** after it. > > Measured effect: **147 of 252 graded strikeout line-results (58%), > across 51 starts.** Both ends of the window fall mid-slate — 7/17 is > affected for 2 of its 30 starts, 7/19 for 20 of 31 — so no > whole-days description of this era can be correct. The site computes > the figure from these two timestamps at build time rather than > restating it in prose. **Verdict: DO NOT SHIP. Reverted from the model's feature list the same day it was reviewed. It had been live since 7/17.** ## What the feature was Mean strictly-prior strikeout rate (vs the starter's throwing hand) across the nine batters the starter would face — the idea being that a lineup of free swingers is a different strikeout matchup than a contact lineup, and that a team-level rate blurs exactly that. ## Why it was killed **1. The sign inverts on the serve path it actually uses.** Training used `game_nines()` — the nine the pitcher *actually faced*, recovered from the game's own play-by-play. Serving uses the posted lineup when available and a projection when not. Rebuilding the feature from the projected nine: | | Δ log-loss vs no feature | |---|---| | Trained on actual nine, served actual nine (the published claim) | −0.00343 | | Trained on actual nine, served **projected** nine | **+0.00334** | The projected nine overlaps the actual nine 75.6% of the time (6.8 of 9; an exact match in only 2.7% of starts). On the fallback path the feature made the model **worse than not having it at all** — and nothing in the code distinguished the two paths at inference time. **2. The gain was never distinguishable from zero.** Paired bootstrap (B=20,000) over the 1,035 holdout starts: | Metric | Δ | 95% CI | P(wrong sign) | |---|---|---|---| | Log-loss | −0.0035 | [−0.0146, +0.0078] | 0.27 | | Brier 4.5 | −0.0010 | [−0.0028, +0.0009] | 0.16 | | Brier 5.5 | −0.0008 | [−0.0027, +0.0011] | 0.21 | | Brier 6.5 | −0.0012 | [−0.0029, +0.0006] | 0.09 | Effect ÷ bootstrap SE = 0.60 (significance needs ~1.96). The CI is four times wider than the effect. "All three line Briers improved" was true and meaningless: the three lines are nested thresholds of one outcome, so they are one piece of evidence, not three. ## What this changes about a public claim The gate also corrected a claim on our own goal table, in the direction of being *unfairly harsh on ourselves*. The K model's log-loss deficit versus the persistence baseline is +0.0135, but that gap's 95% CI is **[−0.0103, +0.0371]** — it crosses zero. The honest statement is therefore: > We cannot distinguish the K model from a shrunk-Poisson persistence > baseline on log-loss. not "persistence beats us." Goal K-1's wording is corrected accordingly. Being wrong in the self-critical direction is still being wrong. ## The salvage path (not attempted yet) Train the feature **from a projected nine** so training and serving see the same construction, then re-measure. If it survives that, it ships. The feature-computation functions are retained in `pipeline/features.py` for exactly this; only the entry in `FEATURES` was removed. ## Deployment state - Live from 7/17 until 7/18. Predictions published in that window were built with it; they are immutable and stay on the record as published, which is why this document exists rather than a silent rollback. - The revert is staged on branch `k-feature-revert`. It **must** ship together with a droplet retrain: the deployed model artifact expects the feature, so code-without-retrain would fail on dimension mismatch. - Measured impact of the revert on the holdout: log-loss 2.2552 → 2.2587, line Briers 0.2289 / 0.2130 / 0.1715. Both figures are inside the noise band described above; nothing measurable was lost.
Source: K-FEATURE-VERDICT.md — the original, unrendered, for anyone who wants to diff it.