diff --git a/src/services/featureCoverage.js b/src/services/featureCoverage.js index 5e484ec..e428b4d 100644 --- a/src/services/featureCoverage.js +++ b/src/services/featureCoverage.js @@ -57,7 +57,14 @@ async function coverage(opts = {}) { const concurrency = Math.max(1, Math.min(10, opts.concurrency || DEFAULT_CONCURRENCY)); const getOdds = opts.getOdds || require('./oddsService').getOdds; - const getFeatures = opts.getFeatures || require('./intelligence/featureCache').getFeatures; + // Use the GRADER'S OWN entry point, not featureCache.getFeatures directly. + // Two reasons, both learned the hard way: getFeatures takes camelCase + // (playerName/statType) and returns `{ features: {...} }`, so calling it with + // the prop shape returns an empty object for EVERY field — a probe that + // reports 0% coverage while the pipeline grades 365 props is measuring + // itself, not the pipeline. + const computeFeatures = opts.computeFeatures + || require('./intelligence/computeFeatures').computeFeaturesForProp; const isModelBook = opts.isModelBook || require('../config/bookRoles').isModelBook; const odds = await getOdds(sport); @@ -77,11 +84,12 @@ async function coverage(opts = {}) { const feats = await mapLimit(batch, concurrency, async (p) => { try { - const f = await getFeatures({ - player: p.player, stat_type: p.stat_type, sport, + const r = await computeFeatures({ + player: p.player, stat_type: p.stat_type, line: p.line, sport, + direction: 'over', book: p.book, home_team: p.home_team, away_team: p.away_team, game_time: p.game_time, }); - return { p, f: f || {} }; + return { p, f: (r && r.features) || {} }; } catch (err) { return { p, f: {}, error: (err && err.message) || String(err) }; }