'use strict'; /** * Fix A2 — the learning readers do not regress to the unordered walk. * * WHAT THIS TEST IS AND IS NOT. It does NOT assert that a reader is correct — * correctness is measured by `scripts/read-integrity.js` against an ordered * control on live data, and a source grep can never stand in for that (the * meta-scar rule, specs/read-integrity-harness.md §2). * * What it DOES assert is structural: the two tables measured corrupt * (`ledger_entries`, `model_snapshots`) are never read through the legacy * `page()` helper again, the scripts stay importable without executing, and the * named READS the harness measures still exist. Those are the properties that, * if they silently regressed, would make the next harness run measure the wrong * thing. */ const fs = require('fs'); const path = require('path'); const SCRIPTS = [ 'prove-hit-factors', 'prove-tb-factors', 'cluster-prove', 'tb-solo-and-interactions', 'build-grade-bands', 'proven-status', 'champion-ablation', // A2b — the context readers and the two write-scripts 'prove-runs-rbi', 'pitcher-prove-k', 'prove-park-weather', 'skill-v1-stagea', 'stagea-gate-run', 'backfill-context', 'reconstruct-game-environment', 'calibrate-hits', ]; /** Every table these scripts page through must have a recorded unique key. */ const KEYED_TABLES = ['ledger_entries', 'model_snapshots', 'statcast_aggregates', 'batter_spray', 'team_defense', 'platoon_splits', 'park_dimensions', 'hitter_opportunity', 'lineup_context', 'game_context']; const CORRUPTED_TABLES = ['ledger_entries', 'model_snapshots']; /** * READS DELIBERATELY LEFT ON THE LEGACY WALK — named, with the reason. * * The A2 order scoped the fix to the eleven readers measured FAIL and said * explicitly to leave the siblings that measured 0%. Those siblings are listed * here rather than excluded by a loosened regex, so the gap is VISIBLE and * tracked instead of invisible. * * They are not safe by construction — they are clean by plan. The same query on * `ledger_entries` measured 16.5% one minute and 24.8% the next, so a 0% reading * is a fact about today, not a property. These should move in a follow-up. */ const KNOWN_LEGACY_READS = { // A2b closed the last exceptions: the three reads A2 left on the legacy walk // are converted, and the unordered helper is DELETED from every script rather // than parked beside the safe one — a dead broken helper is an invitation. }; const read = (name) => fs.readFileSync(path.join(__dirname, '..', '..', 'scripts', `${name}.js`), 'utf8'); describe.each(SCRIPTS)('scripts/%s.js', (name) => { const src = read(name); const allowed = KNOWN_LEGACY_READS[name] || []; it('never reads a keyed table through an unordered walk', () => { for (const table of KEYED_TABLES) { // `page(sb, 'ledger_entries'` — the unordered walk. `pageSafe(...)` is fine. expect(src).not.toMatch(new RegExp(`[^e]page\\(\\s*sb,\\s*'${table}'`)); } expect(allowed).toHaveLength(0); }); it('carries NO unordered page() helper at all — the landmine is deleted', () => { const code = src.replace(/\/\/.*/g, '').replace(/\/\*[\s\S]*?\*\//g, ''); const defs = code.match(/async function page\(/g) || []; for (const _ of defs) { // A surviving helper must itself order; an unordered one must not exist. expect(code).toMatch(/paginate\(/); } }); it('looks its key up from the schema rather than assuming id', () => { expect(src).toMatch(/uniqueKeyFor/); }); it('routes its fixed reads through safePaginate, not a hand-rolled walk', () => { expect(src).toMatch(/require\('\.\.\/src\/utils\/safePaginate'\)/); expect(src).toMatch(/async function pageSafe\(/); // pageSafe must delegate — a second walk implementation is the thing the // A2 order explicitly forbade. expect(src).toMatch(/return paginate\(/); }); it('exports the named READS the harness measures', () => { const mod = require(path.join('..', '..', 'scripts', name)); // must not execute expect(mod.READS).toBeDefined(); for (const fn of Object.values(mod.READS)) expect(typeof fn).toBe('function'); }); it('does NOT run main() on require — importing it must not hit the network or write', () => { // prove-hit-factors and friends write to mc_test_ledger via // tl.recordAndCount, which is the Bonferroni denominator. An unguarded // entrypoint would let the harness inflate the correction just by measuring. expect(src).toMatch(/if \(require\.main === module\)/); expect(src).not.toMatch(/^main\(\)\.catch/m); }); }); describe('the calibration services load rows through safePaginate', () => { it.each([ ['calibrationService', '../../src/services/model/calibrationService'], ['lowParamService', '../../src/services/model/lowParamService'], ])('%s exports loadSettledRows', (label, modPath) => { const mod = require(modPath); expect(typeof mod.loadSettledRows).toBe('function'); }); it.each([ ['calibrationService', 'src/services/model/calibrationService.js'], ['lowParamService', 'src/services/model/lowParamService.js'], ])('%s no longer swallows a query error as end-of-data', (label, rel) => { const src = fs.readFileSync(path.join(__dirname, '..', '..', rel), 'utf8'); // The :117 defect, in CODE. Anchored to line-start so the comment that // documents the defect does not count as the defect. expect(src).not.toMatch(/^\s*if \(error \|\| !data/m); expect(src).toMatch(/paginate\(/); }); }); describe('A2 did not open anything it was not meant to', () => { it('CALIBRATION_DEPLOYED is still empty — no stat serves a calibrated number', () => { const snapshotService = require('../../src/services/snapshotService'); expect(snapshotService.CALIBRATION_DEPLOYED).toEqual([]); }); it('both hits verdicts remain withdrawn', () => { const wv = require('../../src/services/model/withdrawnVerdicts'); expect(wv.isWithdrawn('mlb', 'hits', 'defense_by_direction')).toBe(true); expect(wv.isWithdrawn('mlb', 'hits', 'pitcher_contact_profile')).toBe(true); }); });