From 212c08b11f09512e0a79bf3d5ecdfce77edf0af0 Mon Sep 17 00:00:00 2001 From: Kev Date: Sat, 1 Aug 2026 02:36:00 -0400 Subject: [PATCH] Fix the Step 0 probe: it was measuring itself, not the pipeline The first run reported 0% coverage for EVERY feature including l5_avg -- which projectionFor requires, on a pipeline that had just graded 365 props. That is impossible, so the probe was wrong, not the pipeline. Two bugs, both in my probe: featureCache.getFeatures takes camelCase (playerName/statType) and I passed the prop's snake_case shape, and it returns { features: {...} } while I read the top level. Either alone yields all-zeros. Now calls computeFeaturesForProp -- the grader's own entry point -- so it measures what the grade path actually sees. Same class as the earlier harness that returned a silent false: a measurement that makes working code look broken is more dangerous than no measurement. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc --- src/services/featureCoverage.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) 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) }; }