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:
@@ -61,6 +61,9 @@ const VALID_STAT_TYPES = new Set([
|
||||
'strikeouts', 'hits', 'home_runs', 'rbi', 'total_bases',
|
||||
'walks', 'runs', 'earned_runs', 'innings_pitched',
|
||||
'hits_allowed', 'stolen_bases',
|
||||
// Session 56 audit — settleable against the real boxscore; PropLine now
|
||||
// requests batter_doubles + pitcher_outs. Keep in sync with scan.js + validation.py.
|
||||
'doubles', 'outs',
|
||||
]);
|
||||
|
||||
const VALID_DIRECTIONS = new Set(['over', 'under']);
|
||||
|
||||
@@ -112,7 +112,7 @@ router.post('/snapshot/all', async (req, res) => {
|
||||
*/
|
||||
router.get('/snapshot/status', async (req, res) => {
|
||||
const { cacheGet } = require('../utils/redis');
|
||||
const { HOURS_UTC } = require('../snapshotScheduler');
|
||||
const { HOURS_UTC, isSnapshotOverdue } = require('../snapshotScheduler');
|
||||
const SPORTS = ['mlb', 'nba', 'wnba'];
|
||||
try {
|
||||
const redis_keys = {};
|
||||
@@ -135,10 +135,13 @@ router.get('/snapshot/status', async (req, res) => {
|
||||
}
|
||||
const ticker = await cacheGet('ticker:items');
|
||||
redis_keys['ticker:items'] = !!ticker;
|
||||
// Session 56 — surface the missed-cron signal in the health probe.
|
||||
const mlbTs = last_snapshot.mlb && last_snapshot.mlb.updated_at;
|
||||
return res.json({
|
||||
cron_armed: process.env.SNAPSHOT_CRON === '1',
|
||||
cron_hours_utc: HOURS_UTC,
|
||||
last_snapshot,
|
||||
overdue: isSnapshotOverdue(mlbTs),
|
||||
redis_keys,
|
||||
ticker_count: Array.isArray(ticker) ? ticker.length : 0,
|
||||
});
|
||||
|
||||
@@ -17,6 +17,8 @@ const VALID_STAT_TYPES = new Set([
|
||||
'strikeouts', 'hits', 'home_runs', 'rbi', 'total_bases',
|
||||
'walks', 'runs', 'earned_runs', 'innings_pitched',
|
||||
'hits_allowed', 'stolen_bases',
|
||||
// Session 56 audit — keep in sync with analyze.js + validation.py.
|
||||
'doubles', 'outs',
|
||||
]);
|
||||
const VALID_DIRECTIONS = new Set(['over', 'under']);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user