Session 56: Full audit — PropLine + boxscore + pipeline + sport coverage (2289 tests)

Research (verified against live MLB Stats / ESPN / The Odds APIs):
- specs/propline-audit.md — every stat_type mapped against our 4-layer pipeline;
  real MLB boxscore fields; sport coverage status; pipeline gap analysis.
- specs/vyndr-roadmap.md — priority-ordered Sessions 57–64 + coverage targets.
- scripts/propline-audit.js + specs/audit-data/ (raw capture).

Headline bug: oddsNormalizer mapped batter_rbis → 'rbis' while the whole
grade/feature/outcome chain keys on 'rbi' — every PropLine RBI prop silently
failed to grade AND settle. Fixed (+ regression test).

Phase 4 — wired missing MLB stats end-to-end:
- PropLine MLB markets 6 → 12 (+runs, walks, doubles, earned_runs, hits_allowed,
  outs — same request, no extra quota).
- doubles/outs/triples added to featureCache + outcomeService MLB_LOG_FIELD and
  all three grade whitelists (analyze/scan/validation.py).

Phase 6 — pipeline resilience:
- opsNotify.js: ntfy alerts (never throws, test-disabled). Snapshot success/
  stale/failure alerts; retry-once on hard odds error (not on empty slate).
- Missed-cron watchdog (mostRecentExpectedSlot/isSnapshotOverdue); status probe
  now returns `overdue`.

Coverage truth: MLB is the only end-to-end-live sport; outcome settlement is
MLB-only (WNBA/NBA/soccer never settle) — documented as the #1 roadmap gap.

Backend 2276 → 2289 tests (+13). Web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-10 17:00:29 -04:00
parent d09a06c054
commit 2ae8a5697e
21 changed files with 1607 additions and 11 deletions
+26
View File
@@ -658,6 +658,32 @@ snapshot, locked to the line, and read from cache.
+ the ClaimMeter pitch). That expiry lapsing — NOT a tier-limit change — was the
cause of the "4 stripe test failures." Operators override via `FOUNDER_CODE_EXPIRY`.
## Data Audit + Pipeline Resilience (Session 56 — non-obvious)
- **`specs/propline-audit.md` + `specs/vyndr-roadmap.md` are the source of truth**
for data coverage + the session plan. Re-run `node scripts/propline-audit.js`
(writes raw JSON to stdout; live sections need network) to refresh.
- **A market only fully works if it's wired at FOUR layers:** requested in
`proplineAdapter.MARKETS`, mapped in `oddsNormalizer.MARKET_MAP`, whitelisted in
all three grade gates (`routes/analyze.js`, `routes/scan.js`,
`python/utils/validation.py`), AND (for MLB) present in `MLB_LOG_FIELD` in BOTH
`featureCache` and `outcomeService`. Miss the map → silent zero; miss the log
field → no features + no settlement. `batter_rbis` was mapped to `rbis` while
everything else keyed on `rbi` — a 4-layer desync that silently killed RBI
props. It's `rbi` now; a normalizer test locks it.
- **The streaks/hotlist path uses its own `rbis` key** built from raw MLB stats —
independent of the odds normalizer. Don't "unify" them; the split is intentional.
- **MLB is the ONLY end-to-end-live sport.** Outcome settlement is MLB-only
(WNBA/NBA/soccer grades never settle → `accuracy` reflects MLB only). Fixing
that (ESPN box-score settle path) is roadmap Session 57.
- **`src/utils/opsNotify.js`** pushes pipeline alerts to ntfy (`vyndr-pipeline-
kev2026`). It NEVER throws and is auto-disabled under `NODE_ENV==='test'` /
`PIPELINE_ALERTS=0` (inject `fetchImpl` to test it). `snapshotService` alerts on
success/stale/failure; `snapshotScheduler` has a per-minute missed-cron watchdog
(`isSnapshotOverdue`, exposed as `overdue` on the status probe).
- **Snapshot retry rule:** retry-once ONLY on a thrown/null odds response (a
transient provider blip). A successful-but-empty slate is NOT retried — that's a
legit off-hours empty slate, and retrying would waste quota + add 60s latency.
## Active Skills
- vyndr-voice (all user-facing output)
- prop-analysis (grading methodology)