Reality assessment: the forward model exists, wired to the wrong side of the pipe

READ-ONLY. src/ and web/ untouched.

Inventoried every forward-model component against the real objective -- a
forward matchup projection, not an edge number. The finding is that all of it
already exists and is already loaded in production, and 100% of it sits
DOWNSTREAM of the grade in challenger columns nothing serves. The served p_win
reads three features and a game log; it has never seen a pitcher.

Inputs are HAVE, not missing: statcast_aggregates carries 1,354 rows (750
pitchers, 604 batters) with exit velo, launch angle, barrel, hard-hit, whiff,
chase, pitch mix, GB/FB, arm angle, and handedness complete on every row. Real
gaps are team defense and catcher/umpire. So Stage A is a plumbing-and-
modelling job, not a data-acquisition job.

Found along the way: the Statcast nightly refresh is unreachable code. tick()
returns for any hour not in HOURS_UTC (14,19,22,1,3) and the refresh block
then tests h === 11, which that guard can never admit. The mechanism data has
been frozen at its 2026-07-21 backfill for 13 days, and the block's own
failure alert sits in the same dead branch -- the identical silently-guarded-
out shape as the settlement outage.

Design shows the counter: every factor label the SIGNAL BREAKDOWN renders is a
restatement of recent frequency (l5_hot_vs_line, l20_over_line, back_to_back,
home_game) plus several structurally-NBA labels (referees, coach pace,
starters out) inside a baseball product. Not one names a pitcher, pitch type,
handedness or park. The card's forward-read slots already exist and go
unfilled -- the surface needs feeding, not redesigning.

On what changes: the prior measurements were outcome-accuracy, not edge, so
the metric was right and the question was narrow. proj-v1.1 and hits-v1 stay
correctly refuted as DISTRIBUTION swaps on thin inputs -- neither tested a
matchup-fed projection. arch-v1 is a market-relative nudge by construction and
is the one component genuinely measured on the wrong axis. AT CEILING is
provisional: measured only against features the champion already reads.

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-03 00:53:56 -04:00
parent d8bf7765db
commit c551bf0340
3 changed files with 299 additions and 0 deletions
+38
View File
@@ -1224,6 +1224,44 @@ phased plan in the Session-57 conversation / BUILD-STATE Next section).
built for exactly this kind of replay was never settled, so ablations must join
outcomes from `ledger_entries` on (player_key, stat, line, side, game_date).
## Forward-model reality check (Session 79 — non-obvious)
- **`specs/forward-model-reality-assessment.md`.** THE OBJECTIVE is a FORWARD
matchup projection (hitter profile × pitcher stuff × park/conditions, read
through archetype), not a market-edge number. Every component that needs
exists AND is loaded in prod — and ALL of it sits DOWNSTREAM of the grade.
- **The served grade sees NONE of it.** `probabilityEstimator` reads exactly
three features (`opp_rank_stat`, `home_away`, `l10_stddev`/`l20_avg`) plus the
game log. statcast rows, arsenal, park, weather, platoon and archetype are all
loaded in `snapshotService` AFTER grading and written to CHALLENGER columns.
`mlbContext` (platoon/handedness) has ZERO consumers — dead code.
- **STATCAST NIGHTLY REFRESH IS UNREACHABLE CODE.** `snapshotScheduler.tick()`
returns at `if (!HOURS_UTC.includes(h)) return` (14,19,22,1,3); the statcast
block then tests `h === STATCAST_HOUR_UTC` (default **11**), which that guard
can never admit. Data frozen at its 2026-07-21 backfill; its failure alert is
inside the same dead branch so it can't warn. Same shape as the settlement
outage — guarded-out code that reports nothing. Set STATCAST_HOUR_UTC to one of
HOURS_UTC or move the block above the guard.
- **Inputs are HAVE, not missing** — `statcast_aggregates` 1,354 rows (750
pitchers / 604 batters): exit velo, launch, barrel, hard-hit, whiff, chase,
pitch_mix, GB/FB, arm angle, and **bats/throws complete on all 1,354**. Gaps
are team DEFENSE (only a coarse `opp_rank_stat`) and catcher framing/umpire.
PARTIAL: batter GB/FB land in the `metrics` JSONB not the typed columns;
lineup-slot tables (`player_role_profiles`, `lineup_role_profiles`) are 0 rows.
- **The card is designed for the forward read; the engine never fills it.** The
factor vocabulary the "SIGNAL BREAKDOWN" renders is entirely counter-restating
(`l5_hot_vs_line`, `l20_over_line`, `back_to_back`, `home_game`) with several
structurally-NBA labels (`ref_foul_high`, `coach_pace_delta`,
`opp_3plus_starters_out`). No signal names a pitcher, pitch type, handedness or
park. Surface needs FEEDING, not redesigning.
- **What the prior verdicts do and don't say.** Resolution = corr(forecast,
outcome) was never a market/edge test — the metric was right, the QUESTION was
narrow ("does challenger out-rank champion?"). proj-v1.1 and hits-v1 remain
correctly refuted AS DISTRIBUTION SWAPS on thin inputs; neither tested a
matchup-fed projection. arch-v1 IS market-relative by construction and is the
one component genuinely measured on the wrong axis — re-test its axes as
forward inputs. "AT CEILING" (runs/walks) is provisional: measured only against
features the champion already reads.
## Active Skills
- vyndr-voice (all user-facing output)
- prop-analysis (grading methodology)