'use strict'; /** * HARNESS RUNNER (Session 64) — joins, runs the backtest, appends the result. * * Runs on OUR scheduler, in our container. No external dependency: the join is * plain SQL through the service client, the harness is a pure function, and the * result lands in `harness_results`. * * THE JOIN: model_snapshots (the model's INPUTS + prediction) → ledger_entries * (the single source of truth for OUTCOMES) on the natural key * (sport, player_key, stat, line, side, game_date). Outcomes are never * denormalized onto snapshots. * * Rows that don't join are EXPECTED: retention stores both sides of every prop * plus refusals, while the ledger keeps only the graded side of graded props. */ const harness = require('./backtestHarness'); const HARNESS_VERSION = 'harness@2026-07-20'; /** Pull joined rows. Read-only. */ async function fetchJoinedRows(deps = {}) { const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient; const sb = getClient(); if (!sb) return { rows: [], skipped: true }; // Supabase's client cannot express this join, so it runs as two reads and is // joined in memory — the volumes here are hundreds of rows, not millions. const [{ data: snaps }, { data: ledger }] = await Promise.all([ sb.from('model_snapshots') .select('sport, model_version, grade, grade_11, p_win, player_key, stat, line, side, game_date, quarantine_reason, refused') .eq('refused', false) .limit(20000), sb.from('ledger_entries') .select('sport, player_key, stat, line, side, game_date, outcome, quarantine_reason') .is('user_id', null) .limit(20000), ]); const key = (r) => `${r.sport}|${r.player_key}|${r.stat}|${Number(r.line)}|${r.side}|${r.game_date}`; const byKey = new Map(); for (const l of ledger || []) byKey.set(key(l), l); const rows = []; for (const s of snaps || []) { const l = byKey.get(key(s)); if (!l) continue; // expected: unselected side / refusal rows.push({ sport: s.sport, model_version: s.model_version, grade: s.grade, grade_11: s.grade_11, p_win: s.p_win, outcome: l.outcome, quarantine_reason: l.quarantine_reason, snap_quarantine: s.quarantine_reason, }); } return { rows, skipped: false, joined: rows.length, snapshots: (snaps || []).length }; } /** * Run the harness and append the result. Never throws — a failed harness run * must not break the scheduler tick, and the staleness alarm is what surfaces * a run that stops happening. */ async function runAndRecord(deps = {}) { try { const { rows, skipped, joined, snapshots } = await fetchJoinedRows(deps); if (skipped) return { skipped: true, reason: 'no supabase client' }; const report = harness.runBacktest(rows, {}); const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient; const sb = getClient(); const record = { verdict: report.verdict, can_validate: report.can_validate, min_sample: report.min_sample, scored: report.counts.scored, excluded: { quarantine: report.counts.excluded_quarantine, terminal: report.counts.excluded_terminal, pending: report.counts.excluded_pending, push: report.counts.excluded_push, }, report, harness_version: HARNESS_VERSION, }; const { error } = await sb.from('harness_results').insert([record]); return { ok: !error, error: error ? error.message : null, verdict: report.verdict, scored: report.counts.scored, joined, snapshots, }; } catch (e) { return { ok: false, error: e && e.message ? e.message : String(e) }; } } /** Newest run timestamp, for the staleness alarm. */ async function lastRunAt(deps = {}) { try { const getClient = deps.getClient || require('../utils/supabase').getSupabaseServiceClient; const sb = getClient(); if (!sb) return null; const { data } = await sb.from('harness_results') .select('ran_at').order('ran_at', { ascending: false }).limit(1); return data && data[0] ? data[0].ran_at : null; } catch { return null; } } module.exports = { runAndRecord, fetchJoinedRows, lastRunAt, HARNESS_VERSION };