Layer 3 Step 1: wire the measurement instrument
Step 0 found we have been flying without one. p_win lives only in model_snapshots, which has 1,000 rows and ZERO settled outcomes; the closing line lives only in closing_captures, which carries no link to a result; and ledger_entries, the row that actually settles, carries no probability at all. So "is the projection calibrated" and "does it beat the market" have never been answerable — the entire measurable universe was 35 rows recovered by a lossy in-memory join. PHASE 0 — closing coverage verified BEFORE reuse, because an instrument built on a partial close measures a biased subset. closing_captures holds 70,254 rows of which 13,364 are usable, and the 56,890 refusals are candidates we never graded plus one-sided prices — not refusals of our props. Coverage on graded props since capture started is 83/83, 100%. Safe to reuse, with the honest caveat that capture only began 2026-07-20. THE FOUR-TUPLE NOW LANDS ON ONE ROW. ledger_entries gains p_win, fair_prob_lock, archetype_vector and projection_locked_at at LOCK time, and closing_prob plus closing_captured_at from the append-only capture store. The join is the whole point: calibration is p_win against outcome, market-comparison is p_win against the close, and both become plain SQL on one record instead of a join that silently drops 90% of the rows. p_win and the archetype vector are IMMUTABLE — written once at lock via the existing ignoreDuplicates upsert, never re-derived at settle. A re-derivation would measure a projection we never made. The archetype is stored as the VECTOR, not the label. "Did archetype-awareness help?" can only be answered against the axes that were live at grade time, and a single text column cannot express a blend. A grade with no archetype stores null rather than an empty object. HONEST-ABSENT BOTH WAYS. A past game with no usable capture is marked market_unavailable_reason and never given an imputed line; calibration still scores on those rows, only market-comparison is absent. And a game that has not started yet is NOT declared closeless — a close can still arrive, and premature absence is as dishonest as imputation in the other direction. One bug caught before it shipped: the scheduler hook iterated a SPORTS identifier that does not exist in that scope. Inside its try/catch it would have thrown ReferenceError every tick and silently never run — the instrument would have looked wired and captured nothing. Now iterates cadence.ALL_SPORTS. The baseline accrues FORWARD. Historical p_win and closes are gone, discarded before this existed. Calibration and market-comparison stay honest-absent until volume accrues. Tests 3614 passed / 294 suites, web build exit 0. Migration 033 applied. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj
This commit is contained in:
@@ -286,6 +286,21 @@ function startSnapshotScheduler(opts = {}) {
|
||||
}
|
||||
} catch (e) { console.warn('[harness] nightly run failed:', e.message); }
|
||||
|
||||
// Session 70 — THE MEASUREMENT INSTRUMENT. Attach the de-vigged CLOSING
|
||||
// probability to locked rows from the append-only closing_captures, so
|
||||
// p_win, the close, the archetype vector and the outcome all land on ONE
|
||||
// joinable record. Runs beside the settle pass; write-once, and a past game
|
||||
// with no usable capture is marked market-unavailable rather than imputed.
|
||||
try {
|
||||
const led = opts.ledgerService || require('./services/ledgerService');
|
||||
for (const sp of cadence.ALL_SPORTS) {
|
||||
const r = await led.attachClosingProb(sp, {});
|
||||
if (r && (r.updated || r.absent)) {
|
||||
console.log(`[instrument] ${sp} — closes attached ${r.updated}, market-unavailable ${r.absent}`);
|
||||
}
|
||||
}
|
||||
} catch (e) { console.warn('[instrument] closing attach failed:', e.message); }
|
||||
|
||||
// Session 68 — LAYER 1 MECHANISM DATA. Nightly full re-pull of the
|
||||
// ~1,350-row Statcast aggregate set at STATCAST_HOUR_UTC. Backfill and
|
||||
// refresh are the SAME call, upserted on the natural key, so the job is
|
||||
|
||||
Reference in New Issue
Block a user