Live-surface integrity on the repaired champion; fix the label my own

repair falsified

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1sivYNqY2TS5ftykmHBU9
This commit is contained in:
Kev
2026-08-07 03:49:54 -04:00
parent 494c83cf76
commit 2391574f00
2 changed files with 7 additions and 3 deletions
@@ -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.`); 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)) { 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. // Trend direction relative to the line.
+2 -2
View File
@@ -141,11 +141,11 @@ function signalRecencyInflation(input) {
return inactive('l5_avg or l20_avg missing'); return inactive('l5_avg or l20_avg missing');
} }
const ratio = (l5 - l20) / l20; 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 { return {
score: Math.min(1.0, ratio), score: Math.min(1.0, ratio),
active: true, 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)})`,
}; };
} }