Files
vyndr/tests/unit/structuralGuards.test.js
builtbykev d103ecf4c3 Disambiguate takeable: THREE questions shared one word, now three names
BYTE-IDENTICAL. The audit found no consumer getting the wrong axis, so this
is a disambiguation, not a bug fix. 4,131 tests / 331 suites green.

STEP 1 AUDIT -- and the order's premise was wrong in a useful way:

  the four accruing challengers   read the flag ZERO times (not four)
  the ranking gate                wants PROMOTION, gets promotion  [correct]
  the ledger column               holds the LEDGER band, consumed as such
  the UI (LiveHeroProp)           TYPES a `takeable` field it never renders

THERE ARE THREE DEFINITIONS, NOT TWO -- and I only found the third by
tracing the ranking gate:

  1. IDENTITY    can it be bet?        book identity (takeability)
  2. LEDGER BAND worth recording?      odds >= -160, UNCAPPED plus
  3. PROMOTION   worth crowning?       -160..+200, i.e. band PLUS a ceiling

(2) and (3) genuinely disagree, and I measured it rather than asserting it:
439 rows -- 28.2% of all takeable=true ledger rows -- carry prices above
+200, up to +1300. A +1300 longshot is a real bet worth RECORDING and not
one worth CROWNING. Both are correct for their own purpose.

THE DANGER WAS NEVER THE LOGIC. It was that three questions shared one
word, so a reader could not tell which answer they held -- and hits, which
must model thin/juiced/one-sided REAL markets, would have been the next
reader to guess wrong.

RESOLUTION: all three now have distinct names in config/takeability.js;
gradeRanking calls isWithinPromotionBand so its intent is self-evident (a
test pins it byte-identical to the old valueEngine call across the whole
price range); the ledger dual-writes within_price_band with `takeable`
kept as a documented DEPRECATED MIRROR so nothing breaks. Column comments
in the database now say what each column actually holds.

I did NOT redefine `takeable` in place. Four readers and a ranking gate
sit on it, and silently changing its meaning under cover of a naming
change is exactly the class of move this session keeps removing.

Gates: 4,131 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
2026-08-02 18:19:17 -04:00

192 lines
8.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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));
}
});
});
// ──────────────────── THREE BANDS, THREE NAMES ────────────────────
describe('the three questions that used to share the word "takeable"', () => {
const { isTakeableMarket, isWithinPriceBand, isWithinPromotionBand, assessQuote } =
require('../../src/config/takeability');
it('a +1300 longshot: real bet, worth RECORDING, not worth CROWNING', () => {
// The measured disagreement: 439 prod rows (28.2% of takeable=true) sit
// above +200. All three answers differ in intent, none is wrong.
expect(isTakeableMarket('draftkings')).toBe(true); // can be bet
expect(isWithinPriceBand(1300)).toBe(true); // worth recording
expect(isWithinPromotionBand(1300)).toBe(false); // not worth crowning
});
it('a -300 favourite: real bet, outside BOTH bands', () => {
expect(isTakeableMarket('draftkings')).toBe(true);
expect(isWithinPriceBand(-300)).toBe(false);
expect(isWithinPromotionBand(-300)).toBe(false);
});
it('the ledger band is UNCAPPED upward; the promotion band has a ceiling', () => {
expect(isWithinPriceBand(200)).toBe(true);
expect(isWithinPromotionBand(200)).toBe(true);
expect(isWithinPriceBand(201)).toBe(true); // still recordable
expect(isWithinPromotionBand(201)).toBe(false); // ceiling bites here
});
it('the ranking gate uses the PROMOTION band — byte-identical to before', () => {
const { takeablePWin } = require('../../src/utils/gradeRanking');
const { isTakeable } = require('../../src/config/valueEngine');
for (const odds of [-160, -161, -110, 100, 200, 201, 1300]) {
const g = { p_win: 0.6, book_odds: odds };
// gate open iff the OLD valueEngine rule said so — no behaviour moved.
expect(takeablePWin(g) !== null).toBe(isTakeable(odds));
}
});
it('assessQuote reports all three axes so a caller cannot pick the wrong one blind', () => {
const q = assessQuote({ book: 'draftkings', odds: 1300 });
expect(q).toMatchObject({ takeable: true, within_price_band: true, within_promotion_band: false });
});
});