The north-star internal metric is weekly ranking quality: within each (position × week) slate, rank players by a projected score, and measure the Spearman rank correlation against the realized ordering, alongside Top-N hit rate and start/sit accuracy. The score is deliberately touchdown-free:
QB score = pass_yd × 0.04 + rush_yd × 0.10
RB score = (rush_yd + rec_yd) × 0.10 + receptions
WR score = rec_yd × 0.10 + receptions
TE score = rec_yd × 0.10 + receptions
Per-position slate sizes (Top-N): QB 12 · RB 24 · WR 36 · TE 12
Three design choices, each load-bearing:
A result "counts" only under the robustness contract of snapcore/eval/ranking_eval.py:
beats the recency baseline in ≥3 of 4 seasons (2021–2024), beats a flat constant, and survives a
label-shuffle placebo.
Because the market is judged in fantasy points, a parallel scoreboard
(snapcore/eval/fantasy_points_eval.py) mirrors the same position-pure slate-Spearman
machinery on actual fantasy points — half-PPR primary, standard and full-PPR secondary
(interceptions/fumbles/2-pt conversions excluded from truth: no predictor in the stack projects them,
documented). How much do TDs reorder reality? Spearman between TD-free truth and fantasy truth:
| Position | Truth-vs-truth ρ | Top-N membership overlap |
|---|---|---|
| QB | 0.851 | 0.736 |
| RB | 0.946 | 0.781 |
| WR | 0.952 | 0.796 |
| TE | 0.923 | 0.672 |
So a TD-blind predictor concedes real headroom (most at QB and TE) on fantasy truth — which is exactly what the prospective E[TD] layer (§7) is measured against. The internal modeling metric stays TD-free; the fantasy-points scoreboard is the candidate public yardstick.
RotoWire (via Sleeper's public API, company="rotowire" on the wire) and ESPN's weekly
projections are scored as benchmarks only. They are structurally fenced in code
(BENCHMARK_ONLY flag, separate registry, an assert_not_benchmark() guard on
every feature path): no third-party projection is ever an input, feature, anchor, or blend component of
our predictions. The same rule holds for betting prop lines (eval-only since 2026-06-08). Game-level
environment (spread/total from schedules) is the one market signal that is feature-legal — and it tested
dead for within-position ranking anyway (§6).
All ingestion comes from the nflverse open-data ecosystem via nflreadpy (play-by-play,
weekly box scores, rosters, depth charts, schedules with pregame lines, NGS weekly aggregates, draft,
contracts, trades). Raw pulls are schema-validated (drift detection against expected schemas) before any
feature build. No paid data feeds are in the model today (§5.3).
Features are built in eight composable layers, each contributing named columns; the canonical entry point composes them and then passes every column list through the anti-leakage gate:
| Layer | Family |
|---|---|
| 0 | Identifiers and keys |
| 1 | Lagged player box-score aggregates |
| 2 | EPA / CPOE efficiency context |
| 3 | NGS weekly aggregates (pregame-usable) |
| 4 | Game context (schedules, environment) |
| 5 | Player attributes and draft |
| 6 | Injury status |
| 7 | Injury intelligence (practice-report refinement) |
The validator (snapcore/eval/leakage.py) is mandatory on every live feature build and
fail-closed. It block-lists five surfaces:
participation.parquet is blocked — including identifier keys, so the surface has zero
silent-leak area.was_pressure, route, defense_coverage_type).
Blocked at both surfaces.temp,
wind, series_result, fixed_drive_result. (Weather
forecasts are a separate, allowed feed.)epa, wpa, yards_gained,
complete_pass — the targets themselves or trivially derived from them.snap_share_lag3) — the rename forces the lag to be visible
and auditable.The doctrine is precise to the column name: per-play time_to_throw (participation,
post-game charted) is blocked, while avg_time_to_throw (NGS weekly aggregate, released
within ~24h of games and available pregame the following week) is allowed — and there is a unit test
asserting exactly that distinction.
The production ranking model decomposes each player-week into projected volume × shrunken efficiency
(snapcore/models/opportunity_model.py). Per position, the supervised volume targets and
feature sets are:
Volume targets QB: pass att + rush att RB: carries + targets WR: targets TE: targets
CANONICAL_FEATURES QB: lag_att, lag_car, lag_snap_share, depth_chart_order
RB: lag_car, lag_car_share, lag_tgt, lag_tgt_share, lag_snap_share, depth_chart_order
WR: lag_tgt, lag_tgt_share, lag_snap_share, depth_chart_order, lag_airyds
TE: lag_tgt, lag_tgt_share, lag_snap_share, depth_chart_order, lag_airyds
Every lagged feature is an exponentially-decayed trailing window computed strictly on prior weeks:
shift(1).ewm_mean(half_life=2.5) within (player, season) — the shift(1) guarantees week W
reads only weeks < W. The training panel spans seasons 2018–2024; evaluation seasons are 2021–2024.
Each volume is projected by its own walk-forward regression. For the ranking lane the estimator is a
standardized RidgeCV (alphas 0.1, 1, 10, 100, 1000) — the feature set is small and collinear
by design, so a regularized linear model selects among proxies rather than hand-stacking them. Missing
feature cells are imputed with training-set medians (computed on pre-eval rows only). The training mask
for a season-S, weeks-9–18 target is:
train: (season < S) OR (season == S AND week ≤ 8) — with week ≥ 2 and a non-null lag guard
predict: (season == S) AND (week ∈ eval_weeks)
Projected volumes are recombined through the position's TD-free efficiency decomposition
(yards-per-attempt / per-carry / per-target and catch rate), producing proj_score. An oracle
variant that keeps actual volumes measures the achievable ceiling (§5.2).
For the displayed yards median (the number on the board, not the ranking), the estimator is a
gradient-boosted quantile median — HistGradientBoostingRegressor(max_iter=300, max_depth=3,
learning_rate=0.05, l2_regularization=1.0, random_state=0, loss="quantile", quantile=0.5) — a
frozen configuration, adopted after paired MAE wins over the ridge base (RB −1.19 / WR −0.94 / TE −0.21
displayed yards, pooled sign-test p ≈ 2e-20 over 120 cells) with interval coverage unchanged. This is an
L1-versus-L2 structural improvement, not a modeling breakthrough, and we quote it as such.
On top of the model score, the ranking lane applies a walk-forward blend with the recency baseline
(models/recency_blend.py, RB/WR/TE only; blend weight re-selected per position and season on
strictly prior data). Current canonical pooled Spearman (2021–2024, W9–18, own population):
| Pos | Model only | Ranking lane (blend) | RotoWire benchmark | Gap | Seasons held |
|---|---|---|---|---|---|
| RB | .698 | .700 | .741 | −.041 | 4/4 |
| WR | .677 | .680 | .690 | −.010 (statistically unresolved) | 4/4 |
| TE | .623 | .623 | .637 | −.014 | 4/4 |
Honest headline: the gap to the market narrowed and WR is statistically unresolved — that is parity-adjacent, not a win. QB is absent from this table by design (§3.3).
Trailing per-opportunity efficiency mean-reverts; carrying a hot streak forward hurts ranking. Each player's trailing efficiency is therefore shrunk toward a position prior:
shrunk_eff = w · trailing_eff + (1 − w) · prior w = n / (n + k)
where n is the trailing opportunity count and k is chosen by argmin reconstruction-MAE on an out-of-sample calibration cohort (each season's weeks 1–8), then frozen before scoring weeks 9–18. Low-n players (noisy ratios) are pulled hard toward the prior; high-n players keep their own signal. Shrinkage is applied to RB/WR/TE only: it measurably hurts QB ranking, so QB efficiency stays raw — a finding from the Phase-2 validation, kept as a hard rule.
Every QB ranking model built to date fails the season-consistency gate (0/4 seasons vs the recency
baseline for the promoted-model family; one later side-flag candidate resolved NULL under paired testing
against the stronger ewma5 incumbent — mean lift fails Bonferroni, 0/4 seasons, unstable weights). QB
therefore serves recency_ewma5 — an exponentially weighted average of prior TD-free scores —
and the projection grid leaves the QB model score deliberately NULL. We publish QB numbers under this
baseline and do not represent them as model output.
The W5+ production training mask is illegal for W1–4 targets — it would train on the very weeks being predicted. The early lane therefore uses a strict mask, and Week 1 gets its own construction:
recency_ewma5; RB/WR/TE =
last_season_mean_post, the player's prior-season mean TD-free score including playoff
games (playoffs happened before W1 and are invisible to the regular recency windows — including them
is free, legal information). Backtest: the book scores ALL 0.571 vs 0.550 for the serving-family
recency baseline (+0.021, matched population, 16 position-season cells, 2021–2024). Five research
iterations (§6) tried to beat it with models; all failed the gate; the book is what we serve.model_early. The full opportunity pipeline under the strict
(season < S) mask — no same-season rows at all. Backtest (bucket ALL): 0.545 / 0.594 /
0.588 at W2/W3/W4 vs 0.540 / 0.579 / 0.577 for the recency baseline. Honest caveat, recorded at
promotion: at W2 the prior-season book scores 0.558 on its own population, but no W2-specific
candidate cleared the gate — the promoted construction mirrors the harness's validated book-to-model
transition exactly rather than cherry-picking a per-week argmax.The promotion also closed a latent leakage path: before it, an early-week request could reach the W9+ production mask. The W1 numbers were independently hand-traced to raw stats (agreement to 1e-9) during adversarial verification. Known caveat, recorded: the W1–4 boom/bust and interval columns are not yet early-week-validated.
TDs are never regressed as counts (symmetric-percentage error on 0-vs-0.4 outcomes is a math artifact,
not signal). Instead, multinomial XGBoost (multi:softprob) predicts a distribution over
discrete outcomes, binned per target from the empirical distribution:
passing TDs: classes [0, 1, 2, 3+] E[Y] values [0, 1, 2, 3.3]
rushing TDs: classes [0, 1, 2+] E[Y] values [0, 1, 2.2]
receiving TDs: classes [0, 1, 2+] E[Y] values [0, 1, 2.2]
E[TD] = Σ class_value × P(class)
Two calibration fixes were merged 2026-07-03 after a three-fold bootstrap verification (all folds p < .001, coefficients re-derived independently to 4 decimal places):
| Metric | Before | After |
|---|---|---|
| any-TD precision | .4915 | .5872 |
| any-TD recall | .2985 | .4734 |
| multi-TD precision | .2097 | .2452 |
| multi-TD recall | .1960 | .7035 |
| any-TD ECE | .0529 | .0308 |
Recorded caveats: the QB any-TD fraction .75 is grid-capped below its .82 base rate; the multi-TD board expands 186 → 571 predicted rows (QB-dominated); 2022 any-TD precision is .535 on the thinnest calibration fold.
Every displayed projection is a 70%-target central interval on yards
(snapcore/serving/projection_intervals.py):
median = walk-forward gbm_q50 projected yards (§3.1)
raw_lo = median − σ raw_hi = median + σ σ = lagged trailing SD of the player's yards
floor = clip(raw_lo − q̂_lo, 0) ceiling = raw_hi + q̂_hi
The asymmetric offsets (q̂_lo, q̂_hi) are conformal corrections calibrated walk-forward on prior-season out-of-sample residuals (each median is itself an out-of-sample projection, so no eval rows leak into calibration). Target coverage 0.70 (α = 0.30); measured coverage post-merge:
| Pos | Coverage | Band width (yds) | In target band [0.65, 0.75] |
|---|---|---|---|
| RB | 0.699 | 59.7 | yes |
| WR | 0.712 | 53.0 | yes |
| TE | 0.708 | 38.5 | yes |
The per-player width is the honest product form of the boom/bust research: a steady every-down role gets a tight band, a volatile role a wide one.
Per player-week, calibrated P(boom) and P(bust) on the TD-free score
(snapcore/models/boom_bust.py): boom = deviation from the pregame expectation in the top
tercile of the per-position residual distribution, bust = bottom tercile (terciles, not quintiles — TE
slates carry only ~12 players, and a ~33% base rate keeps calibration bins populated). Tercile thresholds
are fit only on calibration seasons, never eval rows. The classifier is a standardized logistic
regression on lagged volatility/role features (air yards, target share, snap share, depth-chart order,
trailing residual volatility, trailing score CV); its output is calibrated by a Platt / isotonic / beta
calibrator selected by a pre-declared 0.6·Brier + 0.4·ECE criterion, fit on out-of-fold predictions,
leave-one-season-out over 2021–2024. Scoring follows a reliability contract (AUC ≥ 0.60, ECE ≤ 0.05,
BSS > 0) against climatology and a naive-volatility floor. Positioning is explicit: this is a
confidence layer, not an accuracy edge — an adversarial re-check showed much of the raw bust
signal is mean-reversion a naive variance ranker also captures.
One cell (RB receptions spike/bust probabilities) is served by a negative-binomial count head: a Poisson-loss gradient-boosted mean on the RB feature set, NB2 dispersion via method-of-moments, spike probability from the NB CDF, shipped only behind a strict-prior isotonic calibration map (verified ECE 0.021). Gate evidence: paired lift over the incumbent provider in 3/3 seasons (p = .0215 / .0025 / .009; pooled 0.6206 vs 0.5105 spike AUC, Δ +0.110, p = 0.0015). Adoption conditions are recorded and enforceable: the incumbent runs in parallel through 2026 with auto-fallback on any trailing-8-week regression, and the head is scoped to this single cell — it was tested for TE receptions and made things significantly worse (−0.152 spike AUC), which is exactly why cell-scoping is a rule and not a vibe.
The only valid evaluation shape for this domain
(snapcore/eval/harness.py::walk_forward_splits): to score predictions at (season S, week W),
train on (season < S) OR (season == S AND week < W), predict on
(season == S AND week == W), iterate chronologically. Random k-fold is invalid — it trains
on future weeks of the same season. Feature columns are validated against the leakage block-list before
the first fold yields.
Before any promotion, an independent verification pass — instructed to refute, not confirm — re-derives the numbers and attacks the construction. This step has teeth; its record includes:
The public market scoreboard (market_scoreboard.json, rendered on the 2024 replay page) grades us against RotoWire and ESPN on identical player populations — a player-week counts only if truth, our board, and every provider cover it. 2024 W9–18, 40 graded cells:
| Pos | Snapcount | RotoWire | ESPN | Best | Gap | Our cells W–L vs best | sign_p / wilcoxon_p |
|---|---|---|---|---|---|---|---|
| QB | 0.416 | 0.546 | 0.516 | RotoWire | −0.130 | 1–9 | 0.022 / 0.010 |
| RB | 0.730 | 0.771 | 0.767 | RotoWire | −0.042 | 2–8 | 0.109 / 0.037 |
| WR | 0.715 | 0.729 | 0.745 | ESPN | −0.030 | 1–9 | 0.022 / 0.014 |
| TE | 0.643 | 0.666 | 0.682 | ESPN | −0.038 | 3–7 | 0.344 / 0.065 |
| ALL | 0.626 | 0.678 | 0.678 | — | −0.052 | — | — |
Readings, verbatim from the internal tracker: we are #1 at zero positions today. QB is a statistically resolved loss and 3–4× the size of any other gap. TE is the closest to unresolved (sign_p 0.344; we already take 3–4 of 10 weekly slates). These all-common numbers run higher than the own-population numbers elsewhere in this document because the common population drops market-uncovered fringe players — compare only within one table, never across scoreboards. The claim rule is encoded in the artifact itself: "best at a position" requires beating every provider on the identical population, with paired tests agreeing, over consecutive graded periods. By that rule we claim nothing today.
Headroom map (pooled Spearman, recency → our model → opportunity oracle, W9–18): RB .675 → .688 → .932; WR .643 → .666 → .876; TE .574 → .609 → .861; QB .474 → .457 → .751 (the model loses to recency at QB, hence §3.3). Against the corrected, honest recency anchor (ewma5), our model's edge is ~+0.006–0.008 Spearman at RB/WR/TE — roughly 3% of the oracle headroom, not the 5–12% an earlier weak-anchor comparison suggested. The ceiling was stress-tested: a richer gradient-boosted configuration plus every additional free lagged signal we could construct (team pace, pass rate, red-zone shares) added approximately nothing, replicated per position. Free public data is exhausted for this problem on both the ranking and boom/bust axes; the honest description of our current edge is "beats naive baselines, trails paid-analyst market projections."
The oracle gap decomposes mostly into this-week volume information we cannot see in free data. The #1 scoped paid lever is in-season route participation (charted, e.g. FTN/PFF-class feeds), with an evidence bar attached: a Stage-A partial-correlation probe grades it +.251 (TE) / +.186 (RB) against residuals, and the expected gain is +0.03–0.08 Spearman on WR/TE. The spend rule is pre-registered: a paid sample must beat the free RotoWire benchmark on WR/TE (> 0.687 / > 0.636 on the own-population yardstick) in ≥3 of 4 seasons — beating our own recency baseline is not enough — and a $0 public-tracking-data pre-validation is mandatory before any vendor contract is signed.
Every research iteration appends a verdict — with numbers — to a ledger; NULL verdicts are recorded with the same detail as promotions, and "never re-quote as support" flags are attached to near-misses. Excerpts from the Week-1 2026 program (five iterations, 2026-07-11 → 07-12) and the preceding loop campaign:
Made the model emit at W1 by carrying prior-season volume and EB-shrunk efficiency forward. The standalone W1 model scored 0.513 — worse than both dumb baselines (recency 0.550, prior-season mean 0.554). Adversarial read: the blend's apparent edge decomposed ~80% into a QB-substitution effect containing zero model information. Real product: the harness and a sharpened weakness map.
W1: nothing there — the in-model variant lost to the model-free book (−0.023 paired), and the tilt selector chose zero tilt where it had data. W4: a market-attributable window (beats the plain model 4/4 seasons at season level, paired sign_p 0.004 vs recency) — carried forward as a hint, not promoted.
Gate: 2/4 seasons — candidates rejected. But the same-pipeline probe shows the features genuinely move the model (+0.009, 3/4 seasons), and the subgroup analysis produced the program's key insight: the "movers break the model" thesis was partially a phantom — prior-season aggregates carry across team changes far better than assumed; the real mover weakness belongs to recency predictors.
Hand-curated OC/DC table, 32 teams × 2015–2026, per-row citations, 15/15 adversarial spot-audit. W1 model: 1/4, lost to the book by −0.016 paired. Conclusion: coaching-change information does not move W1 within-position ranking on free features. Durable assets: the cited coordinator table and a third consecutive family showing a W4 window.
The slow-start prior (F5) is a construct-level NULL: across 127 same-staff consecutive-season pairs, early-vs-late form anti-persists (Pearson −0.217) — the "slow-start team" trait does not exist at season-mean granularity in free data, and we retired it. New-contract recency (F7) is the program's strongest in-model W1 signal (+0.017, 4/4 seasons) but both A5 candidates failed the charter gate; the model-free book keeps the W1 seat.
The surviving incumbent — the model-free book (+0.021 vs the recency family at W1) plus the strict prior-seasons-only model at W2–4 — was promoted into week-aware serving, with W5+ byte-identity re-proven independently and the W1 numbers hand-traced to raw stats.
Selected reverts from the wider campaign, kept on the record so they stay dead: ranking-loss objectives (significantly worse than the squared-loss default); removal of an "obviously wrong" /3.0 divisor (significantly negative — it was beneficial implicit shrinkage); RB share pooling (significantly negative, Wilcoxon .017); the TE transfer of the RB count head (−0.152 spike AUC); player-level red-zone share features for TDs (null beyond lagged TD rate + volume); and four consecutive linear book-tilt grids (A2 tilt, A3 discount, A4 delta, A5 z-tilt — all ≈ net zero; the idea is retired). The QB side-flag that briefly suggested a promotable QB model resolved NULL under paired re-testing and is quarantined.