# Wave 1 Integration Layer M1 Ship Summary

## Commit Ladder

| Step | Commit | Primary artifact |
| --- | --- | --- |
| Wave 1 Step 1 | `f76c9de` | Triple-confirmed signals data layer and `/analytics/triple-confirmed` |
| Wave 1 Step 2 | `0c56daf` | Consensus disagreement split view at `/betting/consensus?view=split` |
| Wave 1 Step 3 | `0ab5368` | Savant-style player percentile strips |
| Wave 1 Step 4 | `7384ac4` | Projection percentile bands on season and projection pages |
| Wave 1 Step 5 | This commit | Verification and this ship summary |

## Executive Summary

The audit recommended four Wave 1 integrations: triple-confirmed signals, a disagreement-first consensus view, Savant-style percentile strips, and BP-style projection bands. All four shipped. The key product change is that Mithrandir now intersects its four data layers rather than displaying them as separate islands: projections, live state, regression candidates, and three-lane betting consensus can now meet on a single player row.

## Feature 1: Triple-Confirmed Signals

Surface: `/analytics/triple-confirmed`

Data sources:

- `data/derived/live/deltas_player_hitter_<date>.parquet`
- `data/derived/live/regression_candidates_<date>.parquet`
- HR consensus artifacts under `outputs/betting/home_run_edges/<date>/`
- TB consensus artifacts when three-lane coverage exists

Computation:

- Buy/sell rows require projection pace delta, surface-vs-underlying regression gap, and a three-lane aligned consensus signal.
- Hitter buy/sell shipped first; pitcher version is deferred until K Valinor and pitcher consensus are production-ready.

Sample output from latest full live snapshot (`2026-05-07`):

| Player | Direction | Pace delta | Regression gap | Consensus |
| --- | --- | ---: | ---: | --- |
| Suarez, Eugenio | sell | `+16.8%` | `xwOBA-wOBA -0.109` | HR aligned below market, lane average `15.8%` vs market `22.0%` |

Rendering description:

- The page renders tiered buy/sell sections, stack chips for the three confirming signals, and honest empty states.
- The May 12 forced task wrote a clean empty artifact because May 12 HR consensus outputs are not present on disk yet; this is expected until the full daily betting build advances.

## Feature 2: Consensus Disagreement View

Surface: `/betting/consensus?view=split`

Data sources:

- Existing Phase 2 consensus loader and lane-contract columns from HR/K/TB/Hits/Outs/Futures outputs.

Computation:

- Adds `lane_dispersion = max(lane probabilities) - min(lane probabilities)` when all three lanes are present.
- Adds high-lane and low-lane labels for row-level explanation.
- Split mode sorts by highest dispersion first.

Sample row:

| Entity | Market | High lane | Low lane | Dispersion | Market fair |
| --- | --- | --- | --- | ---: | ---: |
| Lane Thomas | HR | Valinor `19.6%` | Palantir `4.1%` | `15.5 pts` | `12.6%` |

Rendering description:

- The default consensus view remains aligned picks.
- Split mode explains the disagreement in one templated sentence: Valinor sees `19.6%`; Palantir sees `4.1%`; gap `15.5 pts`; market fair `12.6%`.

## Feature 3: Player Percentile Strips

Surfaces:

- `/players/<mlbam_id>`
- `/projections/hitter/<mlbam_id>`
- `/projections/pitcher/<mlbam_id>`

Data sources:

- `data/historical/statcast_hitter_seasonal/hitter_seasonal_2026.parquet`
- `data/historical/statcast_pitcher_seasonal/pitcher_seasonal_2026.parquet`
- `data/derived/projections/hitter_season_2026.parquet`
- `data/derived/projections/pitcher_season_2026.parquet`

Computation:

- Actual percentile ranks are computed against the current-season population.
- Projection ticks show where the preseason projection would rank inside the current population when a matching projection metric exists.
- Negative metrics such as hitter K% and pitcher BB% are inverted so `100th = best in MLB`.

Sample output:

| Player | Metric | Actual | Percentile | Projection tick |
| --- | --- | ---: | ---: | ---: |
| Aaron Judge | Hard-hit% | `56.2%` | `97.0` | `97.6` |
| Aaron Judge | Barrel% | `14.5%` | `99.5` | `98.4` |
| Trent Grisham | Hard-hit% | `53.4%` | `94.6` | `64.4` |
| Andrew Abbott | K% | `16.4%` | `16.3` | `40.6` |

Public sanity check:

- Aaron Judge's strip is directionally consistent with public Statcast-style references. [Baseball Scope](https://baseballscope.com/players/aaron-judge-592450.html) lists Judge around `57.6%` hard-hit, `94.2 mph` average exit velocity, and 96th-97th percentile ranges; Mithrandir's current pull shows `56.2%`, `94.1 mph`, and `97.0` hard-hit percentile.

Rendering description:

- The strip uses the Signal-to-Warn design ramp, never red/green.
- Tooltip text should be expanded in a later polish pass to state `100th = best in MLB`, especially for inverted metrics.

## Feature 4: Projection Percentile Bands

Surfaces:

- `/projections/hitter-season`
- `/projections/pitcher-season`
- `/projections/team-season`
- `/projections/standings`
- `/projections/leaders`

Data sources:

- Existing p10/p50/p90 player intervals in projection parquets.
- `data/derived/projections/team_wins_sim_2026.parquet` for empirical team projected-win quantiles.
- Season projection JSON outputs for display rows that exist outside the richer parquet overlap.

Computation:

- New sidecar: `data/derived/live/projection_quantile_bands_<date>.parquet`.
- Existing p10/p50/p90 intervals are reused where available; p25/p75 are midpoint interpolation.
- Team standings bands use empirical p10/p25/p50/p75/p90 from `team_wins_sim_2026`.
- HR and K/9 display rows use conservative parametric display bands when richer source quantiles are unavailable.

Sample output (`2026-05-12`):

| Entity | Metric | p10 | p25 | p50 | p75 | p90 |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| Aaron Judge | HR | `35.4` | `40.4` | `46.0` | `51.6` | `56.6` |
| Shohei Ohtani | HR | `36.2` | `41.3` | `47.0` | `52.7` | `57.8` |
| Andrew McCutchen | HR | `7.0` | `8.4` | `10.0` | `11.6` | `13.0` |
| LAD | Team-season wins | `86.4` | `90.8` | `95.6` | `100.4` | `104.8` |

Rendering description:

- Each row shows p10/p25/p50/p75/p90 in mono numerals with a faint CI-band style track beneath.
- Tied source rows remain tied in the display. Collapsed bands are transparency, not a bug.

## Scheduler Tasks

| Task | Cadence | Health signal |
| --- | --- | --- |
| `compute_triple_confirmed_signals` | Daily `05:40` | File exists; empty is valid because the query is intentionally high precision |
| `compute_player_percentiles` | Daily `05:45` | Non-empty percentile output |
| `compute_projection_quantile_bands` | Daily `05:50` | Non-empty quantile-band output |

Forced targeted scheduler cycle on May 12:

| Task | Status | Metric |
| --- | --- | ---: |
| `compute_triple_confirmed_signals` | success | `1.0` health metric, `0` current rows |
| `compute_player_percentiles` | success | `4,725` rows |
| `compute_projection_quantile_bands` | success | `5,548` rows |

## Verification

Route checks:

| Route | Result |
| --- | --- |
| `/analytics/triple-confirmed` | `200` |
| `/betting/consensus` | `200` |
| `/betting/consensus?view=split` | `200` |
| `/players/592450` | `200` |
| `/players/663757` | `200` |
| `/projections/pitcher/671096` | `200` |
| `/projections/hitter-season` | `200`, bands rendered |
| `/projections/pitcher-season` | `200`, bands rendered |
| `/projections/team-season` | `200`, bands rendered |
| `/projections/standings` | `200`, bands rendered |
| `/projections/leaders` | `200`, bands rendered |

Tests:

- `python -m unittest discover tests`: `57` tests passed.

Chalk discipline:

- Template grep still finds chalk only in `src/pitcher_card_engine/web/templates/daily.html`.

Data sanity:

- Top latest triple-confirmed row genuinely satisfies all three conditions: Suarez is above projected pace, has a negative xwOBA-wOBA gap, and has an HR three-lane aligned below-market signal.
- Top split consensus row matches the explainer text: Lane Thomas has Valinor `19.6%`, Palantir `4.1%`, dispersion `15.5 pts`, market fair `12.6%`.
- Judge percentile strip aligns with public Statcast-style values within normal refresh-window differences.
- Judge/Ohtani/McCutchen HR bands and LAD team-season wins bands are monotone and plausible.

## Honest Caveats

- WAR bands are not surfaced because there is no validated per-player WAR quantile source in the season projection parquets.
- xwOBA is used where the prompt asked for wOBA because xwOBA is the validated hitter rate metric in the current projection stack.
- Some projection leader rows have tied p10/p50/p90 from source; Wave 1 preserves those collapsed bands rather than inventing fake variance.
- TB-side triple-confirmed is currently empty because TB does not have three-lane coverage yet. It will fill as Phase 3 expands lanes.
- May 12 targeted scheduler outputs do not advance the global live `latest.json` pointer. Public routes still use the latest full live-state snapshot until the full live cycle advances.

## Carried Debts

- Pitcher version of triple-confirmed is deferred to Wave 4 because it depends on K Valinor and pitcher consensus coverage.
- Wave 2 calibration drift moat has not started.
- Wave 4 K Valinor plus bat-tracking and ABS integrations remain blocked on K Valinor resumption.
- Methods page write-ups remain stubs per user direction.
- Mobile polish pass remains deferred per the integration audit recommendation.
- Percentile-strip hover copy should explicitly say `100th = best in MLB`.

## Explicit Non-Goals

- No new ML training.
- No K-specific signals.
- No calibration drift banner.
- No methodology page content.

## Audit Delivery Check

| Audit recommendation | Shipped? |
| --- | --- |
| Triple-confirmed signals | Yes |
| Consensus disagreement view | Yes |
| Player percentile strips | Yes |
| Projection percentile bands | Yes |

The audit identified triple-confirmed signals as the highest impact-to-effort Wave 1 feature. It is now live, with the first real signal class represented by the Suarez sell row.
