hits-v1: built on the right structure, measured honestly, REFUTED

Hits was diagnosed as a family mismatch: 84% of hits rows trade at 0.5, so
the stat rides on P(0), and a negative binomial has unbounded support and no
notion of opportunity at all. hits-v1 models it as the bounded conversion it
is -- N ~ the player's empirical at-bat distribution, hits|N ~ Binomial(N,q),
with the multiplier scaling q (conversion) and never N (opportunity).

STEP 0 confirmed the inputs before the model existed: 30/30 real ledger
players, 100% combined-input coverage. Every read goes through knownRate --
a row with no atBats is dropped, never counted as a 0-at-bat game.

It FIRES: 158/159 hits props (99.4%) on the live production snapshot, through
the real attachProjection path. Scoping by book IDENTITY rather than price
shape kept 94 out-of-promotion-band props on the board, 93 of them modelled --
59% that a price rule would have deleted.

And it LOST. Point-in-time replay (game log truncated strictly before each
row's game_date, real grade-time multiplier), hits-only, direction-aligned,
n=242: resolution champion 0.195 / ladder 0.048 / hits-v1 0.026. Paired
bootstrap on the same rows: hits-v1 - ladder = -0.022, CI95 excluding zero.
Not promoted.

The value is in what it eliminates. The family was wrong AND the mean was not
the constraint -- hits-v1 moved the line-0.5 mean 0.554 -> 0.581 toward a
0.598 base rate while resolution fell. What is left is per-prop
discrimination: the ladder's inputs, not its distribution.

The pre-registered fallback is recorded as WRONG rather than deleted. It said
hits might be genuinely low-resolution for anyone; the champion scores 0.276
on the identical 189 rows, so there is real signal and the ceiling claim was
the comfortable reading, not the honest one. Its own control refuted it, and
that control was already in hand when the branch was written.

hits-v1 stays wired as a challenger writing its own ledger columns so the
forward accrual can confirm the backtest. Champion, ladder, ranking,
calibration, reference ruler and the four accruing verdicts are byte-identical
-- the diff has zero deleted lines.

Tests 4,156 green (332 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-02 19:04:08 -04:00
parent d103ecf4c3
commit 07626de3de
11 changed files with 1409 additions and 2 deletions
+38 -1
View File
@@ -1,7 +1,44 @@
# VYNDR — Build State
## Last Updated
2026-07-12
2026-08-02
## Session 76 (2026-08-02) — hits-v1: a challenger built, measured, and REFUTED ✅
Spec: `specs/hits-v1-binomial.md`. 4,156 tests / 332 suites green, web build exit 0.
Scope was hits only; champion, ladder, ranking, calibration, reference ruler and
the four accruing challenger verdicts are byte-identical (the diff has ZERO
deleted lines).
- **What was built.** `src/services/projection/binomialHits.js` — hits as a
bounded conversion: `N ~ the player's empirical at-bat distribution`,
`hits | N ~ Binomial(N, q)`. At the 0.5 line (84% of real hits rows) this
states `P(>=1) = 1 E[(1q)^N]` directly instead of inferring P(0) from a
count family. The multiplier scales `q` (conversion), never `N` (opportunity).
Wired in `projectionChallenger` as `proj_hits_p_over` / `proj_hits_meta`
(new ledger columns, migration applied).
- **STEP 0 first — inputs before model.** `scripts/hits-input-coverage.js`:
30/30 real ledger players, 100% combined-input coverage, mean 3.518 AB/G,
mean per-AB rate 0.248.
- **FIRING, on the real board.** `scripts/verify-hits-v1.js` runs the production
`attachProjection` over the live prod snapshot: 158/159 hits props (99.4%), one
honest abstention. 94 of 159 props sit OUTSIDE the promotion band and 93 were
modelled anyway — scoping by book identity kept 59% of the board a price-shape
rule would have deleted.
- **AND IT LOST.** Point-in-time replay (log truncated strictly before each row's
game_date, real grade-time multiplier), hits-only, direction-aligned, n=242:
resolution champion **0.195** / ladder **0.048** / hits-v1 **0.026**. Paired
bootstrap: hits-v1 ladder = 0.022, CI95 [0.046, 0.0003]. NOT PROMOTED.
- **The finding is what it eliminates.** Family was wrong AND mean was not the
constraint (hits-v1 moved the line-0.5 mean 0.554→0.581 toward a 0.598 base
rate while resolution FELL). The hits deficit is per-prop DISCRIMINATION — the
ladder's inputs, not its distribution.
- **A pre-registered branch recorded as WRONG.** The spec's fallback ("hits may
be genuinely low-resolution for anyone") is refuted by the champion scoring
0.276 on the identical 189 rows. Kept in the doc rather than deleted.
- **Next order is a DIAGNOSIS, not a model:** what does the champion's `p_win`
read on a hits prop that the projection ladder does not? Building another
projection variant first would repeat this session's mistake.
- Logged not fixed: local `.env` has a transposed Supabase ref — local scripts
need `SUPABASE_URL=` override; prod unaffected.
## Session S11 (a1 board, 2026-07-12) — Live Tracking: the read locked, the game watched ✅
Spec: `specs/LIVE-TRACKING.md` (+ ROW-GRAMMAR §2/§3 S11 amendment).