# Player Prop Expansion Audit

Date: 2026-04-01
Scope: player prop coverage, ranking, generation, persistence, grading, and operational gaps

## Executive Summary

The player prop stack is no longer the old MLB-only special case. The core contract is now shared across ranking, generation, persistence, stale refresh, CLV, and reporting. The biggest remaining problems are no longer identity drift or one-sided live slates. They are the structural gaps the new audits now expose clearly: roster support gaps in some seeded leagues and partial The Odds prop-market coverage in soccer.

In plain English:

- The registry architecture exists and is working.
- Multi-source candidate pairing exists and is working.
- CLV-first ranking exists and is working.
- Grading identity cleanup exists and is working.
- MLB has the deepest end-to-end path.
- Non-MLB leagues still depend on thinner source-backed paths.
- Some leagues are fully active now, but a few still lack roster or market-completion depth.

## What Is Done

### 1. Shared Prop Identity Is Real

The same canonical prop identity now drives:

- market normalization
- source-backed candidate matching
- persistence payloads
- stale refresh comparison
- CLV resolution
- stats and reporting output

Primary files:

- [player-prop-market-registry.ts](/var/www/html/rainmaker/backend/src/services/player-prop-market-registry.ts)
- [forecast.ts](/var/www/html/rainmaker/backend/src/routes/forecast.ts)
- [weather-report.ts](/var/www/html/rainmaker/backend/src/workers/weather-report.ts)
- [odds-refresh.ts](/var/www/html/rainmaker/backend/src/workers/odds-refresh.ts)
- [clv-resolver.ts](/var/www/html/rainmaker/backend/src/workers/clv-resolver.ts)
- [stats.ts](/var/www/html/rainmaker/backend/src/routes/stats.ts)

Result:

- legacy aliases like `homeruns`, `points`, `outs`, `saves`, `three_pointers_made` stop drifting between systems
- stale source-backed props compare on canonical identity instead of raw strings
- reporting and CLV output use normalized prop stat names

### 2. Top Board Is CLV-First

The public board no longer treats CLV as a side note.

Primary files:

- [top-props.ts](/var/www/html/rainmaker/backend/src/services/top-props.ts)
- [top-picks.ts](/var/www/html/rainmaker/backend/src/services/top-picks.ts)
- [player-prop-signals.ts](/var/www/html/rainmaker/backend/src/services/player-prop-signals.ts)
- [player-prop-clv-profile.ts](/var/www/html/rainmaker/backend/src/services/player-prop-clv-profile.ts)
- [TopPropsOfDay.tsx](/var/www/html/rainmaker/src/components/TopPropsOfDay.tsx)

Result:

- CLV fit is a first-order ranking signal
- top-prop cards surface CLV fit directly
- mixed boards can rank props ahead of game markets when CLV quality is better

### 3. Multi-Source Candidate Pairing Exists

The source-backed candidate path is no longer FanDuel-only garbage.

Primary files:

- [team-prop-market-candidates.ts](/var/www/html/rainmaker/backend/src/services/team-prop-market-candidates.ts)
- [the-odds.ts](/var/www/html/rainmaker/backend/src/services/the-odds.ts)
- [grok.ts](/var/www/html/rainmaker/backend/src/services/grok.ts)

Result:

- candidate lines can be completed across books when one side is missing
- completeness metadata is tracked
- low-information one-sided junk gets suppressed
- registry-backed stat labels drive validation and fallback generation

### 4. MLB Has The Most Mature Pipeline

MLB is the strongest implementation, not because the code is prettier, but because it has deeper input and remediation paths.

Primary files:

- [mlb-buildout-todo.md](/var/www/html/rainmaker/backend/docs/mlb-buildout-todo.md)
- [mlb-team-props-repair.ts](/var/www/html/rainmaker/backend/src/workers/mlb-team-props-repair.ts)
- [weather-report.ts](/var/www/html/rainmaker/backend/src/workers/weather-report.ts)

Current MLB strengths:

- direct `PlayerPropLine` ingestion
- phased starter/bullpen/context scoring
- MLB-specific publish suppression
- late refresh handling
- zero-candidate audit worker already exists

## What Is Partially Done

### 1. Registry Coverage Is Ahead Of Full End-To-End Support

The registry currently supports:

- `nba`
- `ncaab`
- `wnba`
- `mlb`
- `nhl`
- `epl`
- `la_liga`
- `bundesliga`
- `serie_a`
- `ligue_1`
- `champions_league`

But the daily event seeder in [seed-all-sports.ts](/var/www/html/rainmaker/backend/src/workers/seed-all-sports.ts) currently activates:

- `nba`
- `nhl`
- `ncaab`
- `epl`
- `la_liga`
- `bundesliga`
- `serie_a`
- `ligue_1`
- `champions_league`
- `mlb`
- `mma`

Update after coverage audit:

- `wnba` is active in [seed-all-sports.ts](/var/www/html/rainmaker/backend/src/workers/seed-all-sports.ts); the old “WNBA is dormant” assumption was stale.
- `ncaab` now has ESPN roster coverage in [espn-roster.ts](/var/www/html/rainmaker/backend/src/services/espn-roster.ts) and is included in the default [roster-reconciler.ts](/var/www/html/rainmaker/backend/src/workers/roster-reconciler.ts) run.
- The remaining real roster mismatch is `champions_league`.

### 2. Soccer Coverage Is Structurally Thinner Than Basketball/MLB

Soccer leagues are supported in the registry, but the market set is narrow:

- Shots
- Shots on Target
- Assists
- Goals

Practical limitation:

- `goals` has no The Odds market key in the registry right now
- that means direct The Odds overlay/verification cannot help on goals the same way it helps on shots or assists

This does not mean goals can never exist. It means they are not getting the same verified source-backed support path.

### 3. Champions League Is Not Fully Roster-Aware

The registry includes `champions_league`, and The Odds mapping includes it.

But [espn-roster.ts](/var/www/html/rainmaker/backend/src/services/espn-roster.ts) does not define `champions_league`.

That creates a structural mismatch:

- candidate and odds layers say the league exists
- roster and roster-dependent validation do not fully agree

This needs an explicit decision:

- add a roster source for Champions League
- or stop pretending it has the same roster validation guarantees as league play

### 4. Non-MLB Leagues Still Rely On The Generic Team-Props Flow

MLB has:

- direct candidate ingestion
- richer scoring
- repair worker
- zero-candidate audit

Non-MLB leagues mostly have:

- source-backed candidate generation
- prompt guidance
- validation against exact markets
- fallback generation when LLM output fails

That is useful, but thinner.

## What Is Still Missing

### 1. Cross-League Candidate Yield Audit

We do not yet have a league-by-league answer for:

- how many seeded events produce zero candidates
- how many produce only one-sided junk
- how many produce valid candidates but zero publishable props after validation
- which stat families are dying most often by league

MLB has dedicated audits.
Non-MLB does not.

Update after live rerun on 2026-04-01:

- This is now partly solved.
- We do have a working cross-league candidate audit worker in [player-prop-candidate-audit.ts](/var/www/html/rainmaker/backend/src/workers/player-prop-candidate-audit.ts).
- The bigger gap is not "we have no audit". The bigger gap is league-specific cleanup where one-sided markets still dominate otherwise healthy slates.

### 1A. Live Candidate Yield Snapshot

Live verified audit artifacts:

- NBA:
  - `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T18-20-58-783Z.json`
- NHL:
  - `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T18-20-58-919Z.json`
- MLB:
  - pre-cleanup: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T17-34-27-156Z.json`
  - post-cleanup: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T17-46-07-467Z.json`
  - post-overlay: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T18-09-51-908Z.json`
- Full-date rollup:
  - pre-cleanup: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T17-34-27-227Z.json`
  - post-cleanup: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T17-46-07-491Z.json`
  - post-overlay: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T18-09-51-906Z.json`
  - post-NBA/NHL verifier fix: `/tmp/rainmaker-prop-audits/player-prop-candidate-audit-2026-04-01T18-23-49-170Z.json`
- Coverage matrix:
  - `/tmp/rainmaker-prop-audits/league-coverage-audit-2026-04-01T18-26-20-568Z.json`

Current results from the latest full-date verified rerun:

- `nba`
  - 9 events
  - 0 zero-candidate teams
  - 0 no-two-way teams
  - 0 thin teams
  - 406 two-way candidates
  - 226 one-way candidates
  - 35.1 avg candidates/team
  - Status: healthy after alias-aware fetch plus all-book The Odds verification

- `nhl`
  - 3 events
  - 0 zero-candidate teams
  - 0 no-two-way teams
  - 0 thin teams
  - 47 two-way candidates
  - 1 one-way candidate
  - 8 avg candidates/team
  - Status: healthy after all-book The Odds verification

- `mlb`
  - 15 events
  - 0 zero-candidate teams
  - 0 no-two-way teams
  - 0 thin teams
  - 2492 two-way candidates
  - 124 one-way candidates
  - 87.2 avg candidates/team
  - Status: healthy after normalized-path cleanup plus event-level The Odds side fill

### 1B. What This Means

- NBA is clean on the live slate.
- NHL is clean on the live slate.
- MLB is clean on the live slate.
- The player-prop candidate path is no longer the blocker on today’s active leagues.
- The higher-leverage remaining work is structural coverage hygiene:
  - roster validation gaps in `champions_league`
  - partial The Odds prop coverage for soccer goal markets
  - keeping the written audit aligned with real code instead of stale assumptions

### 2. Coverage Matrix Audit

We now have a hard list of:

- leagues supported in registry
- leagues supported in seeding
- leagues supported in roster validation
- leagues supported in The Odds mapping
- leagues with real candidate yield

The new coverage audit worker in [league-coverage-audit.ts](/var/www/html/rainmaker/backend/src/workers/league-coverage-audit.ts) makes that machine-readable.

Current matrix summary:

- 14 total leagues in the matrix
- 11 registry-backed prop leagues
- 12 active seeded leagues
- 9 end-to-end ready leagues
- 1 registry league missing ESPN roster coverage
- 6 registry leagues with partial The Odds prop coverage

### 3. League-Specific Publish Tuning Outside MLB

The generic suppression logic is decent, but still broad.

We do not yet have a clear pass for:

- WNBA low-information under suppression
- soccer one-sided goal ladders beyond the current crude checks
- NCAAB publish thresholds beyond broad basketball heuristics
- NHL secondary market weighting beyond the current registry/fallback defaults

### 4. Monitoring And Remediation For Non-MLB

Missing operational pieces:

- zero-candidate audit outside MLB
- auto-remediation queue outside MLB
- per-league stale-rate and publish-rate reporting
- regression guardrails for newly activated leagues

## Recommended Execution Order

### Phase 1. Coverage Matrix And Roster Audit

Goal: make support claims match reality.

Deliverables:

- decide how to handle `champions_league` roster validation
- keep the machine-readable league coverage matrix current

Why first:

- these are the remaining support claims that still do not match reality

### Phase 2. Soccer And Roster Gap Remediation

Goal: fix the structural gaps the audits now expose.

Deliverables:

- add or explicitly waive roster validation for `champions_league`
- decide whether soccer `goals` should get a verified source-backed path or stay intentionally thin

Why second:

- the candidate-yield audit is already built and currently green

### Phase 3. Remediate Highest-Yield Gaps

Goal: fix the top failure modes from the audit.

Expected targets:

- dormant WNBA activation issues
- soccer roster/market validation mismatches
- NCAAB and NHL low-yield publish filters
- missing The Odds market key coverage where official support exists

### Phase 4. Product Surface Expansion

Goal: make broader coverage visible once the backend yield is real.

Deliverables:

- broaden top-board league surfacing if yield justifies it
- expose per-league coverage health internally
- keep CLV-first board logic while preventing thin leagues from surfacing trash

## Immediate Next Move

The highest-leverage next move is to resolve the remaining support mismatches the new audits exposed:

1. Decide whether `champions_league` gets real roster coverage or an explicit validation exception.
2. Decide whether soccer `goals` should stay partially unsupported or get a verified market-completion path.

The live candidate surface is now healthy. The remaining work is about making support claims honest and durable.

## Current Commit Baseline

Relevant recent work:

- `81b30bd` Add prop registry and multi-source market pairing
- `4751c80` Normalize prop registry usage in signals and CLV
- `2b3d873` Canonicalize player prop grading identity
- `83c9ce2` Expand registry-driven player prop league coverage
