Structural hardening: unknown-is-not-zero + takeability-is-book-identity

Both guards are ADDITIVE. The full suite (4,111 -> 4,126 tests, 331 suites)
passes unchanged through the migration, which is the evidence that no
currently-correct output moved: served path, champion, reference ruler and
the four accruing challengers are byte-identical.

GUARD 1 -- src/utils/known.js. Number(null)===0 has produced at least SIX
separate defects here, including one in a module written the same week its
author documented the trap. Per-module vigilance has demonstrably failed,
so the rule lives in one place and SEVEN sites now delegate: platoonSplits,
projectionChallenger, challengerProjection, contactChallenger,
statcastAggregateService, consensusRuler, gradeRanking -- plus
compoundTotalBases moved onto knownRate.

Two functions, deliberately: knownNumber (any finite number -- a REAL 0 is
a fact and must survive) and knownRate (non-negative, rejects booleans --
for counts/rates where `true` or -1 is broken, not thin). Collapsing them
is how the next variant gets in. firstKnown() exists because `a || b`
discards a measured 0 and `a ?? b` does not.

MY OWN GUARD HAD THE BUG IT EXISTS TO PREVENT, and its own test caught it:
Number([]) === 0, so an empty array coerced to a measured ZERO. Same trap
wearing a different type. Both helpers now reject objects outright.

GUARD 2 -- src/config/takeability.js. Takeability is BOOK IDENTITY and
never price shape. Baseball prop markets are genuinely thin, juiced and
one-sided, and all three are NORMAL structure: betrivers and hardrockbet
legitimately quote one side only (5 such rows surfaced in yesterday's
re-stamp), and a hits-over at -300 is a real placeable bet. A rule that
inferred un-takeability from price extremity or one-sidedness would throw
those away while still admitting a DFS book at an ordinary -119 -- exactly
backwards, because the -119 is the fake one.

THE DISTINCTION THAT MUST NOT COLLAPSE, now enforced by test:
  isTakeableMarket(book)  -- CAN it be bet?     (identity)
  isWithinPriceBand(odds) -- SHOULD we promote? (policy band, floor -160)
A -300 DraftKings prop is takeable AND out of band; a PrizePicks -119 is in
band AND not takeable. Independent axes.

FLAGGED, NOT SILENTLY CHANGED: the ledger's `takeable` column is the
PRICE-BAND answer, and its name predates this distinction. Four challengers
and the ranking gate read it, so renaming or redefining it is its own
order -- doing it here would have changed correct current behaviour under
cover of a hardening change.

Fixtures are REAL prod rows from the 2026-08-02 re-stamp, not invented.

Gates: 4,126 tests / 331 suites green; next build exit 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
Kev
2026-08-02 17:25:40 -04:00
parent f67245e1e5
commit 8c764c22a4
11 changed files with 380 additions and 46 deletions
+7 -5
View File
@@ -86,11 +86,13 @@ const clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);
const toLogOdds = (p) => Math.log(p / (1 - p));
const fromLogOdds = (l) => 1 / (1 + Math.exp(-l));
function num(v) {
if (v == null || v === '') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
}
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
/**
* adjust({ pWin, direction, statType, classification }) — PURE.
+7 -5
View File
@@ -40,11 +40,13 @@ function median(xs) {
}
/** Strict numeric: null/''/NaN are ABSENT, never 0. (`Number(null) === 0`.) */
function num(v) {
if (v == null || v === '') return null;
const n = Number(v);
return Number.isFinite(n) ? n : null;
}
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
/**
* Per-book de-vigged fair probability for one side, at one line.
+7 -5
View File
@@ -71,11 +71,13 @@ const clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);
const toLogOdds = (p) => Math.log(p / (1 - p));
const fromLogOdds = (l) => 1 / (1 + Math.exp(-l));
function num(v) {
if (v == null || v === '') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
}
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
/** percentileOf(sortedAsc, x) — fraction of the reference below x, 0..1. */
function percentileOf(sortedAsc, x) {
+7 -5
View File
@@ -60,11 +60,13 @@ const STAT_RATE = Object.freeze({
/** Stats where a HIGHER rate means a LOWER prop outcome. */
const INVERTED = Object.freeze(new Set(['strikeouts']));
const num = (v) => {
if (v == null || v === '') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
};
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
const clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);
/**
+11 -11
View File
@@ -32,6 +32,8 @@
* actual generative structure, not by a family that happens to fit its name.
*/
const { knownRate } = require('../../utils/known');
const TB_CAP = 20; // bases per game; beyond this is not a real outcome
const N_CAP = 8; // per-component events per game
@@ -66,13 +68,12 @@ function tbPmf(rates = {}) {
{ w: 3, rate: rates.triples },
{ w: 4, rate: rates.home_runs },
];
// STRICT: `Number(null) === 0`, so a null rate would pass a naive finite check
// and be treated as a real, measured zero — the difference between "this
// player never triples" and "we do not know his triple rate". Absent stays
// absent; only a genuine number counts.
const isRate = (v) => v != null && v !== '' && typeof v !== 'boolean'
&& Number.isFinite(Number(v)) && Number(v) >= 0;
const usable = components.filter((c) => isRate(c.rate));
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` would read a
// null rate as a real, measured zero — the difference between "this player
// never triples" and "we do not know his triple rate". `knownRate` is the one
// rule; this module previously carried its own copy, which is exactly the
// pattern that has failed six times.
const usable = components.filter((c) => knownRate(c.rate) !== null);
if (usable.length === 0) return null;
let pmf = new Array(TB_CAP + 1).fill(0);
@@ -117,10 +118,9 @@ function pAtLeast(pmf, k) {
/** Expected total bases implied by the component rates. */
function tbMean(rates = {}) {
// Same strictness as tbPmf: an absent component contributes nothing, and is
// not silently read as a measured zero.
const n = (v) => ((v != null && v !== '' && typeof v !== 'boolean'
&& Number.isFinite(Number(v)) && Number(v) >= 0) ? Number(v) : 0);
// Same shared rule: an absent component contributes nothing and is not
// silently read as a measured zero.
const n = (v) => knownRate(v) ?? 0;
return n(rates.singles) + 2 * n(rates.doubles) + 3 * n(rates.triples) + 4 * n(rates.home_runs);
}
+7 -5
View File
@@ -48,11 +48,13 @@ const STAT_FIELD = Object.freeze({
});
const isNum = (v) => typeof v === 'number' && Number.isFinite(v);
const num = (v) => {
if (v == null || v === '') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
};
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
const clamp = (v, lo, hi) => (v < lo ? lo : v > hi ? hi : v);
const abbrOf = (team) => {
if (!team) return null;
+7 -5
View File
@@ -34,11 +34,13 @@ const statcast = require('./adapters/statcastAdapter');
const MIN_PA = Number(process.env.STATCAST_MIN_PA) || 50;
const MIN_IP = Number(process.env.STATCAST_MIN_IP) || 10;
const num = (v) => {
if (v == null || v === '') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
};
// MIGRATED to the shared guard (2026-08-02). `Number(null) === 0` has produced
// at least SIX separate defects in this codebase, including one in a module
// written the same week its author documented the trap — per-module vigilance
// has demonstrably failed. Semantics are byte-identical to the local copy this
// replaces, so no output changes; the point is that there is now ONE rule.
const { knownNumber } = require('../utils/known');
const num = knownNumber;
/**
* roleDetail(usage) — TRUE role from real usage, not the IP proxy (which drifts