Calibrate hits point-in-time: partial pass, and an honest ceiling of 0.667

Fitted the isotonic map on game_date < 2026-08-02 (n=589) and evaluated it on
everything from that date forward (n=383). The map never saw the evaluation
rows, which is the only thing that makes the result mean anything -- fitting
and evaluating on the same rows always looks perfectly calibrated, because the
map is reciting the answers it was built from.

It works, on most of the distribution. Held-out after correction: 0.477 comes
back 0.506, 0.587 comes back 0.580, 0.667 comes back 0.603 -- against raw
errors of +0.191, +0.279 and +0.246 in the same bins. Ordering survived, and
that was verified pairwise rather than assumed, because a broken map would
silently destroy the one thing this model does well.

Two findings matter more than the pass.

First, the honest ceiling is 0.667. Once the numbers are truthful this model
has no 80%-plus hit reads at all -- the top of its range was miscalibration,
not confidence. A four-leg ticket at the ceiling is 0.198, where the raw
numbers implied 0.686. The high-floor parlay is a two-thirds-per-leg
proposition, and that is the number to say out loud.

Second, calibration is certified BY BAND rather than by a blanket flag.
Held-out error was -0.029 and +0.007 through the middle but -0.167 at the
bottom and +0.063 at the top: the model is trustworthy over most of its mass
and untrustworthy at both edges. A single true/false would either throw away
the 72% that works or ship the edges that do not. Only a probability inside a
certified band is marked stackable, and that flag is what chainAcross requires
before it will compound anything. The certified band is 0.40 to 0.60, n=276.

A methodological catch on the way: my first pass condition demanded honest bins
at 0.70 and above -- but honest calibration REMOVES those bins, since the
ceiling drops to 0.667. The gate would have failed the repair for succeeding.
It now tests the highest remaining band instead of a fixed threshold.

Wired forward with the same discipline: calibrationService fits strictly before
today, splits by time rather than at random, and returns null on thin history
so that "no calibrator" means nothing is stackable rather than "trust the raw
numbers". p_win is never mutated -- the calibrated value rides beside it as
p_win_calibrated, because a calibration map is a correction to a forecast, not
a different forecast, and the counter stays byte-identical.

4,275 tests green (339 suites); web build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1sivYNqY2TS5ftykmHBU9
This commit is contained in:
Kev
2026-08-04 17:51:15 -04:00
parent 9c5b968351
commit 4d1803f6d7
7 changed files with 477 additions and 0 deletions
+31
View File
@@ -1624,6 +1624,37 @@ phased plan in the Session-57 conversation / BUILD-STATE Next section).
barely and a 4-obs atom a lot. That gap is the difference between learning and
noise-chasing.
## Hits calibration + the parlay unblock (Session 91 — non-obvious)
- **PARTIAL PASS: hits are calibrated and stackable ONLY in 0.400.60.** Fit on
game_date < 2026-08-02 (n=589), evaluated on >= (n=383) — the map never saw the
evaluation rows. Held-out after correction: **0.477→0.506 (0.029, n=83),
0.587→0.580 (+0.007, n=193), 0.667→0.603 (+0.063, n=63)**, vs raw errors of
+0.191/+0.279/+0.246. Ordering preserved (verified pairwise, not assumed).
- **THE HONEST CEILING IS 0.667.** Once the numbers are truthful this model has
NO 80%+ hit reads at all. A 4-leg ticket at the ceiling is **0.198**, not the
0.686 the raw numbers implied. The "high-floor parlay" is a ~0.67-per-leg
proposition — say that plainly rather than selling the old number.
- **CERTIFY BY BAND, never a blanket flag.** Held-out error was 0.029/+0.007
through the middle but 0.167 at the bottom and +0.063 at the top. A single
true/false would either discard the 72% that works or ship the edges that
don't. `calibration.certifyBands` + `inCertifiedBand`; only in-band atoms get
`calibrated: true`, which is what `chainAcross` requires.
- **A PASS CONDITION CAN FAIL A MAP FOR SUCCEEDING.** My first gate demanded
honest bins ≥0.70 — but honest calibration REMOVES those bins (ceiling 0.667),
so it failed the repair for working. Test the highest REMAINING band, not a
fixed threshold.
- **`calibrationService.fromLedger` fits STRICTLY before today** and splits by
TIME, not at random — certifying on rows the map was fitted on always looks
perfect, and a random split leaks the future. No calibrator ⇒ NOTHING is
stackable, never "pass raw numbers through".
- **`p_win` is never mutated.** Calibration rides beside it as
`p_win_calibrated` + `calibrated` on hits grades, so the counter stays
byte-identical — a calibration map is a correction TO a forecast, not a
different forecast.
- **Synthetic-data trap in the tests:** front-loading wins makes outcome
correlate with date, so a time-split trains on wins and certifies on losses —
the generator creating the exact leakage the split prevents. Interleave.
## Active Skills
- vyndr-voice (all user-facing output)
- prop-analysis (grading methodology)