#!/usr/bin/env node 'use strict'; /** * build-current-era-artifact — fit ONCE, freeze, version, commit. * * Policy B (CERTIFIED_PROCEDURE_FROZEN_ARTIFACT): the walk-forward certifies the * PROCEDURE; the promoted artifact is then fitted on ALL eligible current-era * evidence through one frozen cutoff. The holdout existed to certify the * procedure — it is not permanently withheld from the artifact that ships. * * The output is a committed JSON file. That is the repository-native immutable * seam (same shape as supabase/schema/model_snapshots.columns.json): versioned * by git, reviewable as a diff, and impossible to mutate at runtime. * * SUPABASE_URL=... SUPABASE_SERVICE_KEY=... node scripts/build-current-era-artifact.js */ require('dotenv').config({ quiet: true }); const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const { createClient } = require('@supabase/supabase-js'); const cal = require('../src/services/model/calibration'); const src = require('../src/services/model/currentEraSource'); const fitPolicy = require('../src/services/model/fitPolicy'); const { MODEL_VERSION } = require('../src/config/modelVersion'); const SPORT = 'mlb'; const STAT = 'hits'; const SUPPORT = [[0.50, 0.80]]; const MIN_FIT = 200; const digest = (o) => crypto.createHash('sha256').update(JSON.stringify(o)).digest('hex').slice(0, 16); /** The served function over certified support at p_win's own 3dp granularity. */ function servedCurve(map, bands) { const steps = []; let prev = null; for (const [lo, hi] of bands) { prev = null; for (let x = lo; x < hi - 1e-9; x += 0.001) { const raw = Math.round(x * 1000) / 1000; const v = cal.applyIsotonic(map, raw); if (v === null) continue; const rounded = Math.round(v * 1e6) / 1e6; if (rounded !== prev) { steps.push([raw, rounded]); prev = rounded; } } } return steps; } (async () => { const sb = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY, { auth: { persistSession: false } }); const fitAsOf = process.env.FIT_AS_OF || new Date().toISOString().slice(0, 10); const rows = await src.loadRows(sb, { sport: SPORT, stat: STAT, modelVersion: MODEL_VERSION, before: fitAsOf }); if (rows.length < MIN_FIT) throw new Error(`insufficient current-era rows: ${rows.length}`); const audit = src.eraAudit(rows, MODEL_VERSION); if (audit.wrong_era_rows !== 0) throw new Error(`wrong-era rows in source: ${audit.wrong_era_rows}`); rows.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : (a.id < b.id ? -1 : 1))); const trainingCutoff = rows[rows.length - 1].date; // NEWEST OBSERVATION ADMITTED const map = cal.fitIsotonic(rows, { minTotal: MIN_FIT }); if (!map) throw new Error('fitter refused'); const curve = servedCurve(map, SUPPORT); const sourceDigest = src.sourceDigest(rows); const artifact = { artifact_id: `mlb-hits-isotonic@${fitAsOf}`, procedure_version: fitPolicy.POLICY_V1.policy_version, sport: SPORT, stat: STAT, model_version: MODEL_VERSION, // TWO DIFFERENT FIELDS. `fit_as_of` is the eligibility bound the query used; // `training_cutoff` is the newest observation actually admitted. Substituting // one for the other would claim evidence the fit never saw. fit_as_of: fitAsOf, training_cutoff: trainingCutoff, fit_n: rows.length, withheld_from_fit: 0, // policy B: the promoted artifact is not starved source_digest: sourceDigest, algorithm: fitPolicy.POLICY_V1.fit_algorithm, algorithm_version: fitPolicy.POLICY_V1.fit_algorithm_version, knot_count: map.length, knot_digest: digest(map), served_curve: curve, served_curve_digest: digest(curve), certified_bands: SUPPORT, era_audit: audit, // Explicit, and NOT live. Promotion to live is a separate deliberate act. approved_for_shadow: true, approved_for_live: false, }; const check = fitPolicy.validate( { ...artifact, estimator_type: 'isotonic' }, { era_counts: audit.era_counts }, ); artifact.fit_policy_valid = check.valid; artifact.fit_policy_violations = check.violations; artifact.servable = check.servable; if (!check.valid) throw new Error(`artifact fails its own policy: ${check.violations.join(', ')}`); const out = path.join(__dirname, '..', 'src', 'services', 'model', 'artifacts', `${artifact.artifact_id.replace(/[@:]/g, '_')}.json`); fs.writeFileSync(out, `${JSON.stringify(artifact, null, 2)}\n`); console.log(JSON.stringify({ wrote: path.relative(path.join(__dirname, '..'), out), artifact_id: artifact.artifact_id, fit_as_of: artifact.fit_as_of, training_cutoff: artifact.training_cutoff, fit_n: artifact.fit_n, withheld_from_fit: artifact.withheld_from_fit, era_audit: audit, knot_count: artifact.knot_count, knot_digest: artifact.knot_digest, served_curve_digest: artifact.served_curve_digest, source_digest: artifact.source_digest, curve_steps: curve.length, servable: artifact.servable, bytes: JSON.stringify(artifact).length }, null, 1)); process.exit(0); })().catch((e) => { console.error(e.message); process.exit(1); });