From 3f7aa368c4c5466467de6252ab5dd29097912d90 Mon Sep 17 00:00:00 2001 From: Kev Date: Fri, 4 Sep 2026 22:45:43 -0400 Subject: [PATCH] The seam never ran: an undeclared variable, and a bare catch that hid it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LIVE was switched on in production and nothing happened, because `snapshotGating.stripModelPrice` contained try { rows = require(...).applyToRows(rows); } catch { } `rows` is not declared in that function — the parameter is `grades`. Under 'use strict' that is a ReferenceError on every call, and the empty catch swallowed it. For an entire release the seam was dead, the status surface reported LIVE ON, and every row was served raw. Two failures, and the second is the one that mattered: a silent catch turned a hard crash into nothing at all. It now names the failure in the log and degrades to uncalibrated rows explicitly. The entitled branch also returned `grades` — the original array — so even a working seam would have had its output discarded for exactly the tier meant to receive it. Both fixed; `grades` is threaded through. MY TESTS COULD NOT SEE IT. They called `applyToRows` directly and grepped the source for the require line. Neither exercises the boundary, and a source grep is not proof a line runs: the dead wiring contained that exact require. The new tests call `stripModelPrice` and assert on its RETURN VALUE, entitled and unentitled, plus a throwing-seam case that requires the warning. Verified on real production rows through the real entitled path: 0.706 -> 0.625 CERTIFIED, EV recomputed 8.7 -> -3.7, grade B unchanged 0.858 -> unavailable UNCERTIFIED, its value:true WITHDRAWN, grade B+ unchanged rbi -> untouched free tier -> model fields still stripped Found only because the live acceptance measured real rows instead of trusting a green suite. Suite 407/407, 5,697 passed. Teeth 48/48 + 10/10 + 23/23. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01CQJeAG8vcDoL5zkiaJyVb8 --- scripts/teeth-artifact-governance.js | 23 +++++++- src/utils/snapshotGating.js | 19 +++++-- tests/unit/servedProbability.test.js | 84 +++++++++++++++++++++++++++- 3 files changed, 118 insertions(+), 8 deletions(-) diff --git a/scripts/teeth-artifact-governance.js b/scripts/teeth-artifact-governance.js index 7d3ad6b..b13d60b 100644 --- a/scripts/teeth-artifact-governance.js +++ b/scripts/teeth-artifact-governance.js @@ -310,8 +310,8 @@ inject(41, 'raw model probability is erased when live serves', 'tests/unit/servedProbability.test.js'); inject(42, 'the serving boundary bypasses the seam', 'src/utils/snapshotGating.js', - ` try { rows = require('../services/model/servedProbability').applyToRows(rows); }`, - ` try { rows = rows; }`, + ` try { rows = require('../services/model/servedProbability').applyToRows(grades); }`, + ` try { rows = grades; }`, 'tests/unit/servedProbability.test.js'); inject(43, 'the monitor reaches a verdict from one settled date', 'src/services/model/forwardMonitor.js', @@ -356,6 +356,25 @@ inject(49, 'a certified row shows the raw probability as confidence', ` out.confidence = Math.round(resolution.raw_model_probability * 100);`, 'tests/unit/liveRenderContract.test.js'); +// ── THE SEAM MUST ACTUALLY FIRE (the release it did not) ───────────────── +inject(50, 'the seam is wired to an undeclared variable and silently no-ops', + 'src/utils/snapshotGating.js', + ` let rows = grades; + try { rows = require('../services/model/servedProbability').applyToRows(grades); }`, + ` let rows = grades; + try { rows = require('../services/model/servedProbability').applyToRows(undefinedRows); }`, + 'tests/unit/servedProbability.test.js'); +inject(51, 'the entitled path returns the uncalibrated originals', + 'src/utils/snapshotGating.js', + ` if (entitledToModelPrice(tierName)) return rows;`, + ` if (entitledToModelPrice(tierName)) return grades;`, + 'tests/unit/servedProbability.test.js'); +inject(52, 'a failing seam is swallowed silently', + 'src/utils/snapshotGating.js', + ` console.warn('[served-probability] seam skipped, serving uncalibrated:', e && e.message);`, + ``, + 'tests/unit/servedProbability.test.js'); + const landed = results.filter((r) => r.landed).length; console.log(JSON.stringify({ teeth_landed: `${landed}/${results.length}`, results }, null, 2)); process.exit(landed === results.length ? 0 : 1); diff --git a/src/utils/snapshotGating.js b/src/utils/snapshotGating.js index e5286f8..e53d39d 100644 --- a/src/utils/snapshotGating.js +++ b/src/utils/snapshotGating.js @@ -61,12 +61,21 @@ function stripModelPrice(grades, tierName) { // Placed BEFORE the tier strip on purpose: calibration decides what the number // IS, entitlement decides who may see it. Reversing them would calibrate // fields that had already been removed. - try { rows = require('../services/model/servedProbability').applyToRows(rows); } - catch { /* serving must never fail because calibration could not answer */ } - + // It must never break serving — but a SILENT catch is how this seam spent a + // release doing nothing: the first version assigned to an undeclared `rows` + // instead of `grades`, threw a ReferenceError under strict mode, and the bare + // catch swallowed it. Live reported ON while every row was served raw. The + // failure is now named in the log. if (!Array.isArray(grades)) return grades; - if (entitledToModelPrice(tierName)) return grades; - return grades.map((g) => { + let rows = grades; + try { rows = require('../services/model/servedProbability').applyToRows(grades); } + catch (e) { + rows = grades; + console.warn('[served-probability] seam skipped, serving uncalibrated:', e && e.message); + } + + if (entitledToModelPrice(tierName)) return rows; + return rows.map((g) => { if (!g || typeof g !== 'object') return g; const out = { ...g }; for (const f of MODEL_FIELDS) delete out[f]; diff --git a/tests/unit/servedProbability.test.js b/tests/unit/servedProbability.test.js index 165a947..80e0ef9 100644 --- a/tests/unit/servedProbability.test.js +++ b/tests/unit/servedProbability.test.js @@ -112,7 +112,11 @@ describe('one seam', () => { it('serving routes reach it through stripModelPrice, not by duplicating logic', () => { const fs = require('fs'); const path = require('path'); const gsrc = fs.readFileSync(path.join(__dirname, '../../src/utils/snapshotGating.js'), 'utf8'); - expect(gsrc).toContain("require('../services/model/servedProbability').applyToRows(rows)"); + // NOTE: a source grep is NOT proof the seam runs — the first wiring + // contained this exact require and did nothing. The behavioural proof is in + // "THE SEAM MUST FIRE THROUGH THE REAL BOUNDARY" below; this only checks + // that consumers do not duplicate the logic. + expect(gsrc).toContain("servedProbability').applyToRows(grades)"); // no consumer may hold calibration logic of its own for (const f of ['src/routes/snapshot.js', 'src/routes/heroProp.js', 'src/services/topGradedService.js']) { const src = fs.readFileSync(path.join(__dirname, '../../', f), 'utf8') @@ -123,3 +127,81 @@ describe('one seam', () => { } }); }); + +describe('THE SEAM MUST FIRE THROUGH THE REAL BOUNDARY', () => { + /** + * The first wiring assigned to an undeclared `rows` instead of `grades`, + * threw a ReferenceError under strict mode, and a bare catch swallowed it. + * Live reported ON while every row was served raw, for a whole release. + * + * The old tests could not see it: they called `applyToRows` directly and + * grepped the source for the require line. Neither exercises the boundary. + * These call `stripModelPrice` and assert on its RETURN VALUE. + */ + const OLD = process.env.PROBABILITY_CONTRACT_LIVE; + beforeAll(() => { process.env.PROBABILITY_CONTRACT_LIVE = '1'; }); + afterAll(() => { + if (OLD === undefined) delete process.env.PROBABILITY_CONTRACT_LIVE; + else process.env.PROBABILITY_CONTRACT_LIVE = OLD; + }); + + const batch = () => ([ + row({ p_win: 0.706, confidence: 71, ev_pct: 8.7, value: false, book_odds: -185, fair_odds: -160, grade: 'B' }), + row({ p_win: 0.858, confidence: 86, ev_pct: 51.8, value: true, book_odds: -130, fair_odds: -110, grade: 'B+', side: 'under' }), + row({ stat_type: 'rbi', p_win: 0.65, confidence: 65, ev_pct: 12, value: true, grade: 'C+' }), + ]); + + it('an ENTITLED caller receives the CALIBRATED rows, not the originals', () => { + const src = batch(); + const out = gating.stripModelPrice(src, 'desk'); + const expected = registry.applyCurve(A, 0.706); + expect(out[0].p_win).toBe(Math.round(expected * 1000) / 1000); + expect(out[0].p_win).not.toBe(0.706); // the seam actually ran + expect(out[0].probability_state).toBe(pc.STATE.CERTIFIED_CALIBRATED); + expect(out[0].raw_model_probability).toBe(0.706); + }); + + it('EV is recomputed from the served probability at the boundary', () => { + const out = gating.stripModelPrice(batch(), 'desk'); + const { evPct } = require('../../src/utils/devig'); + expect(out[0].ev_pct).toBe(evPct(out[0].p_win, -185)); + expect(out[0].ev_pct).not.toBe(8.7); // the raw-derived value + }); + + it('an UNCERTIFIED row loses its number and its TRUE value badge', () => { + const out = gating.stripModelPrice(batch(), 'desk'); + expect(out[1].p_win).toBeNull(); + expect(out[1].confidence).toBeNull(); + expect(out[1].ev_pct).toBeNull(); + expect(out[1].value).toBeNull(); // was true + expect(out[1].grade).toBe('B+'); // grade survives + expect(out[1].raw_model_probability).toBe(0.858); + }); + + it('a non-hits row is untouched at the boundary', () => { + const out = gating.stripModelPrice(batch(), 'desk'); + expect(out[2].p_win).toBe(0.65); + expect(out[2].probability_state).toBeUndefined(); + }); + + it('an UNENTITLED caller still gets the model fields stripped', () => { + const out = gating.stripModelPrice(batch(), 'free'); + for (const r of out) { + expect(r.p_win).toBeUndefined(); + expect(r.ev_pct).toBeUndefined(); + expect(r.value).toBeUndefined(); + } + }); + + it('a throwing seam degrades to uncalibrated rows and SAYS SO', () => { + const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const mod = require('../../src/services/model/servedProbability'); + const real = mod.applyToRows; + mod.applyToRows = () => { throw new Error('boom'); }; + try { + const out = gating.stripModelPrice(batch(), 'desk'); + expect(out[0].p_win).toBe(0.706); // unchanged, not lost + expect(warn).toHaveBeenCalled(); // and not silent + } finally { mod.applyToRows = real; warn.mockRestore(); } + }); +});