The Full Grading Record

Regenerated from the grading run's own output, never hand-edited: the failing bands and the refusal to claim more are in it because the run wrote them there. Reproduce it with python -m engine.nflverse_backtest.

Confidence, re-measured on nflverse

Generated 2026-08-22 02:47 UTC. Method frozen in advance: reports/nflverse-backtest-method.md, committed before this harness existed. Reproduce with python -m engine.nflverse_backtest.

Grade C

no accuracy claim on any surface. The numeral prints as a recorded prediction only.

The grade comes from a rule written before any number was computed, and its clauses are required rather than weighted. This run cleared the error and resolution thresholds for a stronger grade and failed on the bucket count, so the stronger grade is not available. That is the rule working, not a technicality: the whole reason it was written first is that this is the moment it would otherwise be argued with.

Headline

Calls graded10041
Decided (ties excluded)9721
Ties320
Hit rate64.6%
Expected calibration error3.6%
Brier score0.2178 (0.25 = a constant 50% guess)
Resolution — bottom decile50.5%
Resolution — top decile86.1%
Resolution spread35.6 points
Judgeable buckets6
Calibrated2

Calibration

Intervals are a cluster bootstrap over (season, week), 2000 resamples. A per-call interval would assume calls are independent, and they are not — inside one roster-week the same benched player is the alternative at several slots, and across rosters one real game drives many outcomes. Wilson intervals are computed too, and a bucket where the two disagree is recorded as undecided and counts against the calibrated total.

StatedGradedDecidedTiesStated avgObserved95% intervalVerdict
50%–55%271426278752.4%51.6%50%–54%calibrated
55%–60%231122337857.4%58.9%57%–61%calibrated
60%–65%194618875962.5%66.3%64%–68%off
65%–70%140713575067.3%74.1%72%–77%off
70%–80%141713754274.1%82.3%80%–84%off
80%–90%244240482.9%91.2%88%–95%off
50%50%60%60%70%70%80%80%90%90%perfect calibrationstated 52.4% → observed 51.6% (2627 decided calls, 50%–55%)stated 57.4% → observed 58.9% (2233 decided calls, 55%–60%)stated 62.5% → observed 66.3% (1887 decided calls, 60%–65%)stated 67.3% → observed 74.1% (1357 decided calls, 65%–70%)stated 74.1% → observed 82.3% (1375 decided calls, 70%–80%)stated 82.9% → observed 91.2% (240 decided calls, 80%–90%)stated confidenceobserved hit rate
Each dot is a confidence bucket, sized by how many calls it holds; the bar is its 95% interval. On the diagonal, stated equals observed. Ours sit above the diagonal at 5 of 6 bands and below it at 1 — the largest gap either way is 8 points. What the report does about it is stated in the text above, not softened here.

The failure is one-directional. Every bucket above the lowest outperforms its stated number. The model is not noisy here — it is systematically underconfident, which is the correctable kind of wrong and the reason resolution is strong while calibration is not.

Per season

SeasonCalls
2014953
2015947
2016872
2017893
2018925
2019900
2020929
2021885
2022943
2023838
2024956

What this is not

It is not comparable to reports/backtest.md. That measured a different estimand on a different data stack: a human's actual starter against the model's best bench option, over one twelve-team league. There are no humans here, so this grades the model's first choice against its own second. The two numbers must never be placed side by side.

It says nothing about win probability, which stays gated. The published unit there is P(your total beats their set lineup), and the product no longer sees any rival's lineup, so no source exists to compute it live or to grade it.

Team defenses hold roster spots and produce zero calls — and the reason printed here was wrong. It said DST scoring "needs points and yards allowed, which this product does not compute". The product computes it: engine/scoring.py RULE S4 scores a defense from the team's own week plus the schedule's final score, and engine/subscriber.py calls it. What is true is narrower and worse: this HARNESS never merges the team rows in, so every DEF- id misses, scores as an absence, and is gated out before a call exists.

So the live product scored defenses, projected them and published a confidence on one — 0.627 on a real 2024 week-10 report — against zero graded DEF calls anywhere in this table. That is a published probability with no method behind it, which principle 1 forbids, so the product now withholds the numeral on any DEF slot (TEAM_DEFENSE_CONFIDENCE_CALIBRATED = False) while still showing the projection, exactly as it already does for win probability.

Folding defenses into the run above was not an option: §3 of the frozen method excludes them and says in terms that nothing in it may change once an output has been read. Grading them needs a new preregistration and a new commit. The data is there when that happens — stats_team_week_{season}.csv resolves for every season in this window, at about 0.2 MB each.

Excluded, and why

That's the record, failures included. Your reports get graded the same way, in public, every Monday.

Set up your team