From 2391574f005a48acddaf6b091b72fc53f6e86a59 Mon Sep 17 00:00:00 2001 From: Kev Date: Fri, 7 Aug 2026 03:49:54 -0400 Subject: [PATCH] Live-surface integrity on the repaired champion; fix the label my own repair falsified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHASE 0 — four checks PASS, one defect found and fixed. PASS gradeBands is required by NO serving code -- built across several orders, never wired. No stale band derived from the retired ten-game champion can reach a user, because none reaches a user at all. PASS CALIBRATION_DEPLOYED is [] and the calibrate loop iterates it, so calibrate() is never called and p_win_calibrated is never set. The only assignment site sits inside that empty loop. No withdrawn map can leak. PASS projectionFor reads l20_avg, which mlbGameLogFeatures now builds from fullLog -- so refusals are computed on the repaired full-window reference, not the retired ten-game one. PASS factors still fire with correct sign across the repaired base range (0.35/0.50/0.65/0.80): defense lowers, pitcher-contact raises, platoon raises at every point. Mechanical firing check only -- NOT a lift re-measurement, which waits for accrual. DEFECT FIXED — my own repair falsified a user-facing sentence. The grade card rendered "Last 20 games average: X" from l20_avg, and l20_avg is now a FULL SEASON average. The number changed and the label did not, so the surface was stating something the data no longer supported. Copy now reads "Season average"; trapDetection's L20 explanations likewise. The field name is kept -- it is read in many places -- but no rendered sentence claims a window that isn't there. That is the same class as everything else tonight, one layer out: a correct-looking string describing data that moved underneath it. Serving change; frozen model modules unchanged; p_win never mutated; no Bonferroni slot. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01W1sivYNqY2TS5ftykmHBU9 --- src/services/intelligence/analyzeViaEngine1.js | 6 +++++- src/services/intelligence/trapDetection.js | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/services/intelligence/analyzeViaEngine1.js b/src/services/intelligence/analyzeViaEngine1.js index 8930667..fe35a07 100644 --- a/src/services/intelligence/analyzeViaEngine1.js +++ b/src/services/intelligence/analyzeViaEngine1.js @@ -114,7 +114,11 @@ function buildConcreteReasoning(features = {}, engine1Result = {}, meta = {}, pr lines.push(`${prop.player || 'Player'} is averaging ${features.l5_avg.toFixed(1)} ${prop.stat_type || ''} over his last 5 games.`); } if (Number.isFinite(features.l20_avg)) { - lines.push(`Last 20 games average: ${features.l20_avg.toFixed(1)}.`); + // LABEL FIXED WITH THE DATA. `l20_avg` is now built from the full season + // log rather than a ten-game slice, so "Last 20 games" was a sentence the + // number no longer supported. The field name is kept (it is read in many + // places) but the copy states what is actually being shown. + lines.push(`Season average: ${features.l20_avg.toFixed(1)}.`); } // Trend direction relative to the line. diff --git a/src/services/intelligence/trapDetection.js b/src/services/intelligence/trapDetection.js index 270d318..5c9d15b 100644 --- a/src/services/intelligence/trapDetection.js +++ b/src/services/intelligence/trapDetection.js @@ -141,11 +141,11 @@ function signalRecencyInflation(input) { return inactive('l5_avg or l20_avg missing'); } const ratio = (l5 - l20) / l20; - if (ratio <= 0) return { score: 0, active: true, explanation: 'L5 not hotter than L20' }; + if (ratio <= 0) return { score: 0, active: true, explanation: 'L5 not hotter than season' }; return { score: Math.min(1.0, ratio), active: true, - explanation: `L5 (${l5.toFixed(1)}) ${(ratio * 100).toFixed(0)}% above L20 (${l20.toFixed(1)})`, + explanation: `L5 (${l5.toFixed(1)}) ${(ratio * 100).toFixed(0)}% above season (${l20.toFixed(1)})`, }; }