/* ============================================================ VYNDR — THE PRICE-LAYER LAW (Session 66). Plain CommonJS so .tsx components import it AND the Jest suite requires it directly (same pattern as colorContract.js / vyndrTokens.js / playerName.js). ONE function decides what a price row is allowed to SAY. The triplet renders whatever this returns and never re-derives a verdict of its own, so there is exactly one place where "is this value?" is answered. THE FIVE HONESTY STATES (design: `Vyndr Price Triplet.dc.html`, ACT 01): VALUE green — ev >= VALUE_EV_THRESHOLD *AND* the book price is inside the takeable band. Both. Raw positive EV is NOT value: +11.7% EV at -210 is a juiced price we will not call a play. PRICED_OUT blue — real edge, untakeable price. We won't call a juiced price a value, and we won't pretend the edge wasn't there. This state exists so green never has to lie in either direction. NO_EDGE grey — the honest number is already priced. Stated at full voice, never hidden. That "no" is the instrument working. QUARANTINE amber — the MODEL leg is withheld (graded against inputs we no longer trust). Book and fair still render: we never hide the honest fair number because a different leg is poisoned. RARE — as of Jul 2026 the only quarantined rows are one frozen historical cohort, so this is not a routine state. REFUSAL dim — no fair price we'd defend. Nothing renders. No fabricated price, no empty gauge. TRUTH LAW: the triplet never prints a number it can't stand behind. A leg with no value renders absent, never a zero and never a placeholder. ============================================================ */ /* ---- Backend mirror ----------------------------------------------------- These MUST match src/config/valueEngine.js. The browser can't require the backend module, so the constants are duplicated here and a test (priceTriplet.test.js) reads BOTH files and fails if they drift — the same guard used for playerName.js. If you tune the band, tune it in both. */ const TAKEABLE_ODDS_CEILING = -160; // most-juiced favourite we'll promote const TAKEABLE_ODDS_MAX = 200; // longest dog we'll promote const VALUE_EV_THRESHOLD = 2; // a real edge, not rounding const STATES = Object.freeze({ VALUE: 'VALUE', PRICED_OUT: 'PRICED_OUT', NO_EDGE: 'NO_EDGE', QUARANTINE: 'QUARANTINE', REFUSAL: 'REFUSAL', // Not a verdict — a display state. The model leg exists and is honest, the // viewer just isn't entitled to it (free tier). Book + fair render normally. NO_VERDICT_LOCKED: 'NO_VERDICT_LOCKED', // Honesty pass: the model price was never COMPUTED (the EV layer isn't // producing p_win/model_odds yet) — NOT quarantined, NOT locked. The model // leg renders absent ('—'), book + fair stand, and NO verdict is claimed. // This exists so a never-priced row never wears QUARANTINE's "we suppressed // our own price / a leg is poisoned" copy, which asserts a deliberate act // that did not happen. NO_MODEL: 'NO_MODEL', }); /** Strict numeric parse — `Number(null) === 0` is the fabrication bug this * whole layer exists to prevent, so nothing coerces. */ function num(v) { if (v == null || v === '') return null; const n = typeof v === 'number' ? v : Number(v); return Number.isFinite(n) ? n : null; } /** isTakeable(american) — inside the promotable band. Mirrors valueEngine. */ function isTakeable(american) { const a = num(american); if (a == null) return false; return a >= TAKEABLE_ODDS_CEILING && a <= TAKEABLE_ODDS_MAX; } /** isValue(american, evPct) — takeable AND clears the EV threshold. Both. */ function isValue(american, evPct) { const ev = num(evPct); return isTakeable(american) && ev != null && ev >= VALUE_EV_THRESHOLD; } /** * deriveValueState(row) — the single verdict function. * * row: { book_odds, fair_odds, model_odds, ev_pct, quarantine_reason, * refused, model_price_locked } * * Order matters and encodes the honesty ordering: * 1. REFUSAL first — with no fair price there is nothing honest to show. * 2. QUARANTINE next — the model leg is untrustworthy, so no verdict may be * computed from it, but book+fair still stand. * 3. Then the three real verdicts. * A locked (free-tier) model leg is NOT quarantine and NOT refusal: the data * exists and is honest, the viewer just isn't entitled to it — so the verdict * is withheld rather than asserted, and book+fair render normally. */ function deriveValueState(row = {}) { const fair = num(row.fair_odds); const book = num(row.book_odds); // 1. Refusal — no fair price we'd defend (or no market to price against). if (row.refused === true || fair == null || book == null) return STATES.REFUSAL; // 2. Quarantine — model leg withheld; book + fair still render. if (row.quarantine_reason) return STATES.QUARANTINE; // Free-tier lock: entitled data withheld, not absent. No verdict is claimed // (the verdict IS the model leg), but this is not quarantine — the row is // honest, it's just gated. if (row.model_price_locked === true) return STATES.NO_VERDICT_LOCKED; const model = num(row.model_odds); const ev = num(row.ev_pct); // Without a model price or an EV there is no verdict to render. This is an // absent leg, NOT a quarantine: we never computed it, so we must not claim we // "suppressed" or "poisoned" it. Book + fair still stand; the model leg goes // honestly absent and no verdict is asserted. if (model == null || ev == null) return STATES.NO_MODEL; // 3. The three real verdicts. if (isValue(book, ev)) return STATES.VALUE; if (ev >= VALUE_EV_THRESHOLD) return STATES.PRICED_OUT; // edge, untakeable price return STATES.NO_EDGE; } /** valueStateColor(state) — the ONE mapping from verdict to token. * Green appears here exactly once, on VALUE, and nowhere else. */ function valueStateColor(state) { switch (state) { case STATES.VALUE: return 'var(--g-a)'; case STATES.PRICED_OUT: return 'var(--priced-out)'; case STATES.QUARANTINE: return 'var(--amber)'; case STATES.NO_EDGE: case STATES.REFUSAL: case STATES.NO_VERDICT_LOCKED: case STATES.NO_MODEL: default: return 'var(--text-2)'; } } function impliedProb(american) { const a = num(american); if (a == null) return null; return a > 0 ? 100 / (a + 100) : -a / (-a + 100); } /** * modelVsFair(modelOdds, fairOdds) — the "+2.9% VS FAIR" figure on the verdict * line. It is the gap between OUR price and the honest de-vigged price, in * implied-probability PERCENTAGE POINTS (not a ratio, and NOT book-vs-fair). * * Derived from the design file's own two worked examples, both of which this * reproduces exactly: * STATE 1 book +125 · fair +110 · model +98 → "+2.9% VS FAIR" * STATE 3 book +118 · fair +104 · model +112 → "-1.8% VS FAIR" * Positive = the model prices it SHORTER than fair (we think it's likelier * than the honest number says). Null unless both legs are real. */ function modelVsFair(modelOdds, fairOdds) { const pm = impliedProb(modelOdds); const pf = impliedProb(fairOdds); if (pm == null || pf == null) return null; return Math.round((pm - pf) * 1000) / 10; } /** fmtOddsAmerican(v) — display an American price, or null for honest absence. * Never returns '0', never returns a placeholder number. */ function fmtOddsAmerican(v) { const n = num(v); if (n == null) return null; const r = Math.round(n); return r > 0 ? `+${r}` : String(r); } module.exports = { STATES, TAKEABLE_ODDS_CEILING, TAKEABLE_ODDS_MAX, VALUE_EV_THRESHOLD, isTakeable, isValue, deriveValueState, valueStateColor, modelVsFair, impliedProb, fmtOddsAmerican, };