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
+87
View File
@@ -0,0 +1,87 @@
'use strict';
/**
* takeability — TAKEABILITY IS BOOK IDENTITY. NEVER PRICE SHAPE.
*
* A line is takeable if and only if it comes from a book a bettor can actually
* place a wager at. Nothing about the PRICE decides it.
*
* WHY THIS NEEDS TO BE A RULE AND NOT A HABIT. Baseball prop markets are
* genuinely thin, genuinely juiced, and genuinely one-sided, and all three are
* NORMAL market structure rather than signs of a bad quote:
*
* - `betrivers` and `hardrockbet` legitimately quote ONE SIDE ONLY on real
* props (verified in prod: 5 such rows surfaced in the 2026-08-02 re-stamp).
* - a hits-over at -300 is a real, placeable bet. Steep juice is a price, not
* a disqualification.
* - a market with two books is still a market.
*
* Any rule that inferred UN-takeability from price extremity, one-sidedness or
* thinness would throw those real markets away — while still happily admitting
* a DFS pick'em book at an ordinary-looking -119. That is exactly backwards:
* the -119 is the fake one, because PrizePicks is not a sportsbook.
*
* ── THE DISTINCTION THAT MUST NOT COLLAPSE ────────────────────────────────
*
* isTakeableMarket(book) — CAN a bettor place this? Book identity. This file.
* isWithinPriceBand(odds) — SHOULD we surface/promote it? A policy band on the
* price (`takeableStandard`, floor -160, uncapped
* plus). A -300 prop is takeable AND outside the
* band. Both statements are true at once.
*
* These answer different questions and are stored in different columns. The
* ledger's `takeable` column is the PRICE-BAND answer; its name predates this
* distinction and is misleading — flagged, not silently redefined, because four
* challengers and the ranking gate currently read it.
*/
const { TAKEABLE_BOOKS, isTakeableBook } = require('./bookRoles');
const { isLedgerTakeable, LEDGER_TAKEABLE_FLOOR } = require('./takeableStandard');
/**
* THE canonical takeability rule. Every path that asks "can this be bet?" calls
* this one function, so the answer cannot drift between the ledger write, the
* price anchor and any surface.
*
* @param {string|null|undefined} book
* @returns {boolean} — false for an absent book: we cannot assert takeability
* of a market we cannot name.
*/
function isTakeableMarket(book) {
return isTakeableBook(book);
}
/**
* Explicitly NOT a takeability test — kept here beside it so the two are read
* together and the difference is unmissable.
*
* @returns {boolean|null} null when the price is UNKNOWN (never false — an
* absent price is not an out-of-band price).
*/
function isWithinPriceBand(americanOdds) {
return isLedgerTakeable(americanOdds);
}
/**
* The full answer for one quote, with the two axes kept apart.
*
* `takeable` depends ONLY on the book. `within_price_band` depends only on the
* price. A one-sided -300 quote from DraftKings is `takeable: true,
* within_price_band: false` — a real bet we would not promote.
*/
function assessQuote({ book, odds } = {}) {
return {
book: book || null,
takeable: isTakeableMarket(book),
within_price_band: isWithinPriceBand(odds),
price_band_floor: LEDGER_TAKEABLE_FLOOR,
};
}
module.exports = {
isTakeableMarket,
isWithinPriceBand,
assessQuote,
TAKEABLE_BOOKS,
LEDGER_TAKEABLE_FLOOR,
};
+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
+7 -5
View File
@@ -35,11 +35,13 @@ function gradeRankOf(g) {
}
/** Strict numeric read — `Number(null) === 0` is the recurring fabrication bug. */
function strictNum(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('./known');
const strictNum = knownNumber;
/**
* The takeable-gated champion probability for one grade row, or null.
+84
View File
@@ -0,0 +1,84 @@
'use strict';
/**
* known — UNKNOWN IS NOT ZERO.
*
* This codebase's single most repeated defect: `Number(null) === 0`. A missing
* value slides through a naive finite check and becomes a real, measured zero —
* which is not a neutral default but usually the STRONGEST possible statement:
*
* - a null triple rate read as 0 says "this player never triples"
* - a null at-bat rate read as 0 says "zero opportunity" — a maximal fade
* - a null price read as 0 lands ABOVE a -160 floor and tags itself takeable
* - a null line read as 0 matches a 0-line quote
*
* It has appeared at least SIX times, including in a module written the same
* week its author documented the trap. Per-module vigilance has demonstrably
* failed, so the rule lives here and every site delegates.
*
* TWO functions, deliberately, because "is this a number?" and "is this a valid
* RATE?" are different questions and collapsing them is how the next variant
* gets in:
*
* knownNumber — any finite number, including negatives and zero. A REAL 0 is
* a fact (0 rest days, an even-money 0 gap) and must survive.
* knownRate — a non-negative finite magnitude, rejecting booleans. For
* counts, rates and probabilities, where a negative or a
* `true` is not a thin measurement but a broken one.
*/
/**
* A finite number, or null when the value is UNKNOWN.
*
* Semantics are byte-identical to the six local `num`/`strictNum` copies this
* replaces, so migrating a call site cannot change its output.
*
* NOTE ON BOOLEANS: this accepts them (`Number(true) === 1`), matching the
* behaviour of the copies it replaces. Use `knownRate` where a boolean would be
* nonsense — that one rejects them.
*/
function knownNumber(v) {
if (v == null || v === '') return null;
// `Number([]) === 0` and `Number([7]) === 7` — an empty array coerces to a
// measured ZERO, which is the exact trap this module exists to close, wearing
// a different type. Found by this module's own test. Only primitives are
// candidates for being a number.
if (typeof v === 'object') return null;
const n = typeof v === 'number' ? v : Number(v);
return Number.isFinite(n) ? n : null;
}
/** Is this a known (present, finite) number? */
function isKnown(v) {
return knownNumber(v) !== null;
}
/**
* A finite, NON-NEGATIVE magnitude, or null when UNKNOWN.
*
* Stricter than `knownNumber` on purpose: a rate/count of `true`, `-1` or `NaN`
* is not a thin measurement, it is a broken one, and admitting it would let a
* downstream distribution treat garbage as evidence.
*/
function knownRate(v) {
if (v == null || v === '' || typeof v === 'boolean' || typeof v === 'object') return null;
const n = Number(v);
return Number.isFinite(n) && n >= 0 ? n : null;
}
/**
* Coalesce to the first KNOWN value, or null.
*
* The point is that a present 0 wins over a later fallback — `firstKnown(0, 5)`
* is 0, because a measured zero is an answer. `a ?? b` gets this right and
* `a || b` does not, which is the same bug wearing different syntax.
*/
function firstKnown(...vals) {
for (const v of vals) {
const n = knownNumber(v);
if (n !== null) return n;
}
return null;
}
module.exports = { knownNumber, knownRate, isKnown, firstKnown };
+149
View File
@@ -0,0 +1,149 @@
'use strict';
/**
* STRUCTURAL GUARDS (2026-08-02) — two recurring failure families, locked.
*
* Both are the same principle: never silently convert something ABSENT or THIN
* into a false DEFINITE value.
*
* GUARD 1 unknown ≠ zero (`Number(null) === 0`, 6× recurring)
* GUARD 2 takeability = book identity, never price shape
*
* The fixtures are REAL rows from prod (2026-08-02 re-stamp), not invented.
*/
const { knownNumber, knownRate, isKnown, firstKnown } = require('../../src/utils/known');
const { isTakeableMarket, isWithinPriceBand, assessQuote } = require('../../src/config/takeability');
// ─────────────────────────────── GUARD 1 ───────────────────────────────
describe('GUARD 1 — unknown is not zero', () => {
it('every absent form is UNKNOWN, never 0', () => {
for (const v of [null, undefined, '', NaN, 'abc', {}, []]) {
expect(knownNumber(v)).toBeNull();
expect(isKnown(v)).toBe(false);
}
});
it('a REAL zero survives — 0 rest days is a fact, not an absence', () => {
expect(knownNumber(0)).toBe(0);
expect(isKnown(0)).toBe(true);
expect(knownRate(0)).toBe(0);
});
it('Infinity is not a measurement', () => {
expect(knownNumber(Infinity)).toBeNull();
expect(knownNumber(-Infinity)).toBeNull();
});
it('knownRate additionally rejects booleans and negatives', () => {
expect(knownRate(true)).toBeNull(); // Number(true) === 1
expect(knownRate(false)).toBeNull(); // Number(false) === 0 — the trap
expect(knownRate(-0.5)).toBeNull();
expect(knownNumber(-0.5)).toBe(-0.5); // a signed gap is legitimately negative
});
it('firstKnown prefers a present 0 over a later fallback (|| gets this wrong)', () => {
expect(firstKnown(0, 5)).toBe(0);
expect(firstKnown(null, 5)).toBe(5);
expect(firstKnown(null, undefined, '')).toBeNull();
});
it('REAL FIXTURE — a null triple rate must not read as "never triples"', () => {
const tb = require('../../src/services/projection/compoundTotalBases');
// Same components, one with an UNKNOWN triple rate vs a MEASURED zero.
const unknown = tb.tbPmf({ singles: 0.6, doubles: 0.2, triples: null, home_runs: 0.15 });
const zero = tb.tbPmf({ singles: 0.6, doubles: 0.2, triples: 0, home_runs: 0.15 });
// Both are computable, and an unknown component contributes nothing —
// but it must do so by being SKIPPED, not by being asserted as zero.
expect(unknown).not.toBeNull();
expect(zero).not.toBeNull();
// The guard that matters: no component usable at all → null, not a flat curve.
expect(tb.tbPmf({ singles: null, doubles: null, triples: null, home_runs: null })).toBeNull();
expect(tb.tbPmf({ singles: false })).toBeNull();
});
it('the migrated sites all delegate to the ONE rule', () => {
// Semantics must be identical across every migrated module, or the
// migration reintroduced drift.
const mods = [
require('../../src/utils/gradeRanking').strictNum,
require('../../src/services/consensusRuler').__internals.num,
];
for (const fn of mods) {
expect(fn(null)).toBeNull();
expect(fn('')).toBeNull();
expect(fn(0)).toBe(0);
expect(fn('2.5')).toBe(2.5);
}
});
});
// ─────────────────────────────── GUARD 2 ───────────────────────────────
describe('GUARD 2 — takeability is BOOK IDENTITY, never price shape', () => {
it('REAL FIXTURE — one-sided takeable markets are TAKEABLE', () => {
// Verified in prod: betrivers and hardrockbet quote one side only on real
// props (5 such rows surfaced in the 2026-08-02 re-stamp). One-sidedness is
// normal baseball market structure, not a defect.
expect(isTakeableMarket('betrivers')).toBe(true);
expect(isTakeableMarket('hardrockbet')).toBe(true);
// A one-sided quote: the other side is genuinely absent, and takeability
// does not depend on it at all.
expect(assessQuote({ book: 'betrivers', odds: 6600 }).takeable).toBe(true);
expect(assessQuote({ book: 'hardrockbet', odds: 400 }).takeable).toBe(true);
});
it('REAL FIXTURE — an extreme price is still TAKEABLE (steep juice is a price)', () => {
// Ohtani hits-over at -266 and Schwarber hits-over at -209 are real,
// placeable DraftKings bets.
for (const odds of [-266, -209, -300, -1400, 4900]) {
expect(assessQuote({ book: 'draftkings', odds }).takeable).toBe(true);
}
});
it('REAL FIXTURE — a DFS book at an ordinary -119 is NOT takeable', () => {
// The ordinary-looking price is the fake one: PrizePicks is not a
// sportsbook. Book identity decides, price shape never does.
for (const book of ['prizepicks', 'underdog', 'sleeper', 'dabble']) {
expect(isTakeableMarket(book)).toBe(false);
expect(assessQuote({ book, odds: -119 }).takeable).toBe(false);
}
});
it('exchanges and offshore are not takeable however normal the price looks', () => {
for (const book of ['kalshi', 'novig', 'smarkets', 'bovada', 'onexbet', 'pinnacle']) {
expect(isTakeableMarket(book)).toBe(false);
}
});
it('an ABSENT book is not takeable — we cannot assert a market we cannot name', () => {
for (const b of [null, undefined, '', 'not_a_book']) expect(isTakeableMarket(b)).toBe(false);
});
it('THE DISTINCTION: takeable and price-band are independent axes', () => {
// A -300 DraftKings prop is a REAL bet we would not promote. Both true.
const steep = assessQuote({ book: 'draftkings', odds: -300 });
expect(steep.takeable).toBe(true);
expect(steep.within_price_band).toBe(false);
const normal = assessQuote({ book: 'draftkings', odds: -110 });
expect(normal.takeable).toBe(true);
expect(normal.within_price_band).toBe(true);
// ...and a DFS book inside the band is still not takeable.
const dfs = assessQuote({ book: 'prizepicks', odds: -119 });
expect(dfs.takeable).toBe(false);
expect(dfs.within_price_band).toBe(true);
});
it('an UNKNOWN price is not an out-of-band price', () => {
expect(assessQuote({ book: 'draftkings', odds: null }).within_price_band).toBeNull();
expect(assessQuote({ book: 'draftkings' }).takeable).toBe(true); // book still decides
});
it('no drift: the canonical rule IS bookRoles.isTakeableBook', () => {
const { isTakeableBook } = require('../../src/config/bookRoles');
for (const b of ['draftkings', 'prizepicks', 'pinnacle', 'betrivers', null]) {
expect(isTakeableMarket(b)).toBe(isTakeableBook(b));
}
});
});