Path coverage: every branch from acquisition to the first grade callback

Audited the corridor rather than trusting the recorder. Nine upstream operations
sit between acquisition success and gradeAndCacheSlate — game binding, per-date
schedule fetch, roster index, attachEventIdentity, book-price capture, team-stat
refresh, hits-factor context, matchup keys — each with its own catch. NONE of
them early-returns, so the corridor always reaches the grader; but seven of them
were SILENT, and two could be misattributed.

COVERAGE WAS INCOMPLETE. Closed:
  * BINDING — bound / unresolved / already-had, and its throw
  * SCHEDULE — dates requested, game count, and its throw. A schedule outage
    previously surfaced as an EVENT IDENTITY error because it lands in that
    catch; it now records SCHEDULE_STAGE_ERROR and rethrows unchanged.
  * ROSTER — indexed players/teams/failed and evidence-date-validity, and its
    throw. Its catch only console.warn'd, so this stage was entirely invisible.
  * DEDUPE per-reason accounting off the filter's OWN branches: invalid fields /
    non-model book / duplicate identity / capped / not examined, plus
    model-book eligibility. Counts reconcile to the input exactly.
  * GRADE BOUNDARY — `reached` is derived from a candidate count and proves
    nothing. GRADE_LOOP_ENTERED, FIRST_GRADEBESTSIDE_STARTED and
    FIRST_ONGRADED_OBSERVED are now separate control-flow facts. No model
    output is recorded; a test greps for p_win/grade/confidence/edge/side.

Terminal states now name the stage: SCHEDULE_STAGE_ERROR, ROSTER_STAGE_ERROR,
BINDING_STAGE_ERROR, EVENT_IDENTITY_STAGE_ERROR, ADMISSION_STAGE_ERROR,
DEDUPE_STAGE_ERROR, IDENTITY_ALL_UNRESOLVED, ALL_REJECTED,
DEDUPE_ALL_NON_MODEL_BOOK, DEDUPE_ALL_INVALID_FIELDS, DEDUPE_EMPTY_OTHER,
READY_FOR_GRADING, GRADE_LOOP_STARTED, FIRST_GRADE_CALLBACK_OBSERVED.

TRACE COMPLETENESS INVARIANT. `reconcilePregrade` — acquisition NONZERO +
CONTINUED with no correlated downstream state is an OBSERVABILITY_GAP, never a
pipeline verdict. This programme has twice read an absence as a conclusion
("MLB exited at acquisition", "all props rejected at admission"); both were
wrong. Now it is a typed state with tests.

dedupeProps takes an OPTIONAL stats object and increments on the branches it
already takes, in the same order — reused, never reimplemented. Without the
object it is byte-identical; a test asserts that.

A PRODUCTION-BREAKING BUG CAUGHT BY THE FULL SUITE: the frozen no-op recorder
did not implement gradeStarted/firstOnGraded, so any caller without a recorder
threw inside the grade loop — and gradeAndCacheSlate's catch turned that into
{written:false,count:0}. Every slate would have graded NOTHING, silently. Fixed,
NO_PREGRADE now covers the full recorder surface, and a test asserts it does.

Exception semantics unchanged throughout: every added catch records and RETHROWS
the identical error. Admission rules, dedupe predicates, MODEL_BOOKS, event
identity, gameBinder, gradeBestSide and its arguments: 0 changed lines.
eventIdentity, oddsService, retentionService, gameBinder, bookRoles,
analyzeViaEngine1, probabilityEstimator, snapshotScheduler and ledgerService:
UNCHANGED. Zero new external calls — the only diff hit is the existing
getScheduleWithPitchers line re-indented into its own try.

Twelve teeth, injections verified present, against a green baseline of 89:
upstream catch silent (1) · missing trace as failure (1) · admission exception
as ALL_REJECTED (2) · non-model-book as duplicate (3) · dedupe-empty as
rejection (1) · falsely says grading started (6) · onGraded unrecorded (1) ·
sport overwrite (2) · intraday overwrite (1) · different attempt id (3) ·
observer adds an external call (1) · store failure changes outcome (2).
Restored byte-identically; teeth 3/4/6 re-run after the NO_PREGRADE fix.

The first teeth pass ran against a baseline the finer states had invalidated;
six superseded assertions were updated first and the run repeated.

388 suites / 5,269 tests pass. web tsc exit 0. Lineage stays OFF.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CQJeAG8vcDoL5zkiaJyVb8
This commit is contained in:
Kev
2026-08-28 01:21:09 -04:00
parent 8d741e3932
commit c1d9ec5bbb
6 changed files with 523 additions and 32 deletions
+44 -9
View File
@@ -58,7 +58,14 @@ const { isModelBook } = require('../config/bookRoles');
// statsapi is free and unlimited. Concurrency stays at 5 — one variable at a time.
//
// Env-tunable so the ceiling can move without a deploy: GRADE_SLATE_LIMIT.
const NO_PREGRADE = Object.freeze({ identity() {}, admission() {}, dedupe() {}, gradeLoop() {} });
// MUST implement EVERY recorder method. A missing one throws inside the grade
// loop, and gradeAndCacheSlate's catch turns that into {written:false,count:0}
// — i.e. a silently empty slate for every caller without a recorder.
const NO_PREGRADE = Object.freeze({
binding() {}, schedule() {}, roster() {},
identity() {}, admission() {}, dedupe() {}, gradeLoop() {},
gradeStarted() {}, firstOnGraded() {},
});
const DEFAULT_LIMIT = Number(process.env.GRADE_SLATE_LIMIT) > 0
? Number(process.env.GRADE_SLATE_LIMIT)
@@ -201,17 +208,31 @@ function propositionEventKey(p) {
return `legacy:${date}:${away}@${home}`;
}
function dedupeProps(props, limit) {
function dedupeProps(props, limit, stats) {
const seen = new Set();
const out = [];
for (const p of props || []) {
if (!p || !p.player || !p.stat_type || p.line == null) continue;
if (!isModelBook(p.book)) continue;
// DIAGNOSTIC COUNTERS ONLY. Incremented on the SAME branches the filter
// already takes, in the same order, so the counts describe the real code path
// rather than a second implementation of it. `stats` is optional; without it
// this function is byte-identical in behaviour.
const bump = (k) => { if (stats) stats[k] = (stats[k] || 0) + 1; };
const list = props || [];
let examined = 0;
for (const p of list) {
examined += 1;
if (!p || !p.player || !p.stat_type || p.line == null) { bump('dropped_invalid_fields'); continue; }
if (!isModelBook(p.book)) { bump('dropped_non_model_book'); continue; }
const key = `${propositionEventKey(p)}::${p.player}::${p.stat_type}::${p.line}`;
if (seen.has(key)) continue;
if (seen.has(key)) { bump('duplicate_identity_removed'); continue; }
seen.add(key);
out.push(p);
if (out.length >= limit) break;
if (out.length >= limit) { if (stats) stats.capped = true; break; }
}
if (stats) {
stats.input_count = list.length;
stats.output_count = out.length;
stats.model_book_eligible = out.length + (stats.duplicate_identity_removed || 0);
stats.not_examined = list.length - examined;
}
return out;
}
@@ -382,8 +403,9 @@ async function gradeAndCacheSlate(sport, props, opts = {}) {
+ `${gate.rejected.length} rejected ${JSON.stringify(gate.reasons)}`);
}
let unique;
const dedupeStats = {};
try {
unique = dedupeProps(gate.admitted, limit);
unique = dedupeProps(gate.admitted, limit, dedupeStats);
} catch (eDed) {
pregrade.dedupe({ started: true, completed: false, threw: true, input_count: gate.admitted.length, error: eDed && eDed.message });
throw eDed;
@@ -393,11 +415,24 @@ async function gradeAndCacheSlate(sport, props, opts = {}) {
input_count: gate.admitted.length,
output_count: unique.length,
removed_count: gate.admitted.length - unique.length,
// Per-reason accounting straight off the filter's own branches.
dropped_invalid_fields: dedupeStats.dropped_invalid_fields || 0,
dropped_non_model_book: dedupeStats.dropped_non_model_book || 0,
duplicate_identity_removed: dedupeStats.duplicate_identity_removed || 0,
model_book_eligible: dedupeStats.model_book_eligible || 0,
non_model_book: dedupeStats.dropped_non_model_book || 0,
capped: !!dedupeStats.capped,
not_examined: dedupeStats.not_examined || 0,
});
pregrade.gradeLoop({ candidate_count: unique.length, reached: unique.length > 0 });
if (unique.length === 0) return { written: false, count: 0 };
const graded = (await mapLimit(unique, concurrency, (p) => gradeBestSide(grade, p, sport, opts)))
const graded = (await mapLimit(unique, concurrency, (p) => {
// Proof that the loop actually began. `reached` above is derived from a
// count and cannot show this.
pregrade.gradeStarted();
return gradeBestSide(grade, p, sport, opts);
}))
.filter(Boolean)
.sort((a, b) => (Number(b.confidence) || 0) - (Number(a.confidence) || 0));
+80 -6
View File
@@ -237,15 +237,29 @@ async function history(sport, deps = {}) {
const PREGRADE_PREFIX = 'ops:pregrade:';
const PREGRADE_OUTCOME = Object.freeze({
// Progress states — the boundary moves forward as each is proven.
READY_FOR_GRADING: 'READY_FOR_GRADING',
GRADE_LOOP_STARTED: 'GRADE_LOOP_STARTED',
FIRST_GRADE_CALLBACK_OBSERVED: 'FIRST_GRADE_CALLBACK_OBSERVED',
// Refusals — the cohort was deliberately emptied, and by which filter.
IDENTITY_ALL_UNRESOLVED: 'IDENTITY_ALL_UNRESOLVED',
ALL_REJECTED: 'ALL_REJECTED',
IDENTITY_STAGE_ERROR: 'IDENTITY_STAGE_ERROR',
DEDUPE_ALL_NON_MODEL_BOOK: 'DEDUPE_ALL_NON_MODEL_BOOK',
DEDUPE_ALL_INVALID_FIELDS: 'DEDUPE_ALL_INVALID_FIELDS',
DEDUPE_EMPTY_OTHER: 'DEDUPE_EMPTY_OTHER',
// Exceptions — each names its own stage, never folded into a refusal.
SCHEDULE_STAGE_ERROR: 'SCHEDULE_STAGE_ERROR',
ROSTER_STAGE_ERROR: 'ROSTER_STAGE_ERROR',
BINDING_STAGE_ERROR: 'BINDING_STAGE_ERROR',
EVENT_IDENTITY_STAGE_ERROR: 'EVENT_IDENTITY_STAGE_ERROR',
ADMISSION_STAGE_ERROR: 'ADMISSION_STAGE_ERROR',
DEDUPE_STAGE_ERROR: 'DEDUPE_STAGE_ERROR',
DEDUPE_EMPTY: 'DEDUPE_EMPTY',
NO_INPUT_PROPS: 'NO_INPUT_PROPS',
OTHER_PREGRADING_ERROR: 'OTHER_PREGRADING_ERROR',
INCOMPLETE: 'INCOMPLETE',
// Reconciliation: acquisition CONTINUED but no downstream trace exists. This
// is an OBSERVABILITY failure, never evidence that the pipeline failed.
OBSERVABILITY_GAP: 'OBSERVABILITY_GAP',
});
function pregradeKey(sport) {
@@ -262,6 +276,9 @@ function beginPregrade({ attemptId, sport, trigger, codeSha, processStartedAt, p
process_generation: processStartedAt || null,
started_at: now || new Date().toISOString(),
input_props_count: Number.isFinite(propsCount) ? propsCount : null,
binding: null,
schedule: null,
roster: null,
identity: null,
admission: null,
dedupe: null,
@@ -278,10 +295,21 @@ function beginPregrade({ attemptId, sport, trigger, codeSha, processStartedAt, p
function pregradeRecorder(trace) {
const t = trace || {};
return {
binding(info) { t.binding = { ...info, error: sanitize(info && info.error) }; },
schedule(info) { t.schedule = { ...info, error: sanitize(info && info.error) }; },
roster(info) { t.roster = { ...info, error: sanitize(info && info.error) }; },
identity(info) { t.identity = { ...info, error: sanitize(info && info.error) }; },
admission(info) { t.admission = { ...info, error: sanitize(info && info.error) }; },
dedupe(info) { t.dedupe = { ...info, error: sanitize(info && info.error) }; },
gradeLoop(info) { t.grade_loop = { ...info }; },
gradeLoop(info) { t.grade_loop = { ...t.grade_loop, ...info }; },
// CONTROL-FLOW FACTS ONLY. `reached` is derived from a candidate count and
// does NOT prove grading began; these two do. No model output is recorded.
gradeStarted() {
t.grade_loop = { ...t.grade_loop, entered: true, first_gradebestside_started: true };
},
firstOnGraded() {
t.grade_loop = { ...t.grade_loop, first_ongraded_observed: true };
},
};
}
@@ -292,21 +320,66 @@ function pregradeRecorder(trace) {
*/
function classifyPregrade(trace) {
const t = trace || {};
if (t.identity && t.identity.threw) return PREGRADE_OUTCOME.IDENTITY_STAGE_ERROR;
const gl = t.grade_loop || {};
// EXCEPTIONS FIRST, each naming its own stage. An exception is never folded
// into a refusal, and a refusal is never reported as an exception.
if (t.binding && t.binding.threw) return PREGRADE_OUTCOME.BINDING_STAGE_ERROR;
if (t.schedule && t.schedule.threw) return PREGRADE_OUTCOME.SCHEDULE_STAGE_ERROR;
if (t.roster && t.roster.threw) return PREGRADE_OUTCOME.ROSTER_STAGE_ERROR;
if (t.identity && t.identity.threw) return PREGRADE_OUTCOME.EVENT_IDENTITY_STAGE_ERROR;
if (t.admission && t.admission.threw) return PREGRADE_OUTCOME.ADMISSION_STAGE_ERROR;
if (t.dedupe && t.dedupe.threw) return PREGRADE_OUTCOME.DEDUPE_STAGE_ERROR;
if (t.input_props_count === 0) return PREGRADE_OUTCOME.NO_INPUT_PROPS;
if (t.grade_loop && t.grade_loop.reached) return PREGRADE_OUTCOME.READY_FOR_GRADING;
// PROGRESS, strongest proof first. `reached` alone is only a candidate count.
if (gl.first_ongraded_observed) return PREGRADE_OUTCOME.FIRST_GRADE_CALLBACK_OBSERVED;
if (gl.first_gradebestside_started) return PREGRADE_OUTCOME.GRADE_LOOP_STARTED;
if (gl.reached) return PREGRADE_OUTCOME.READY_FOR_GRADING;
// REFUSALS, naming the filter that emptied the cohort.
if (t.admission && t.admission.completed && t.admission.admitted_count === 0) {
// Name the CAUSE when identity explains it, rather than the symptom.
if (t.identity && t.identity.completed && t.identity.total > 0 && t.identity.canonical === 0) {
return PREGRADE_OUTCOME.IDENTITY_ALL_UNRESOLVED;
}
return PREGRADE_OUTCOME.ALL_REJECTED;
}
if (t.dedupe && t.dedupe.completed && t.dedupe.output_count === 0
&& t.admission && t.admission.admitted_count > 0) {
return PREGRADE_OUTCOME.DEDUPE_EMPTY;
const d = t.dedupe;
if (d.dropped_non_model_book === d.input_count) return PREGRADE_OUTCOME.DEDUPE_ALL_NON_MODEL_BOOK;
if (d.dropped_invalid_fields === d.input_count) return PREGRADE_OUTCOME.DEDUPE_ALL_INVALID_FIELDS;
return PREGRADE_OUTCOME.DEDUPE_EMPTY_OTHER;
}
return PREGRADE_OUTCOME.OTHER_PREGRADING_ERROR;
}
/**
* TRACE COMPLETENESS INVARIANT.
*
* If acquisition recorded FINAL NONZERO + CONTINUED, a downstream state must
* exist. Its ABSENCE is an observability failure and must never be read as
* "the pipeline failed" — that inference is exactly what this programme got
* wrong twice.
*/
function reconcilePregrade(acqTrace, pgTrace) {
const a = acqTrace || null;
const continued = !!(a && a.final === FINAL.NONZERO && a.outcome === OUTCOME.CONTINUED);
if (!continued) return { applicable: false, complete: null, outcome: null };
if (!pgTrace) {
return { applicable: true, complete: false, outcome: PREGRADE_OUTCOME.OBSERVABILITY_GAP };
}
if (pgTrace.snapshot_attempt_id !== a.snapshot_attempt_id) {
return { applicable: true, complete: false, outcome: PREGRADE_OUTCOME.OBSERVABILITY_GAP, reason: 'attempt_id_mismatch' };
}
const undecided = !pgTrace.outcome
|| pgTrace.outcome === PREGRADE_OUTCOME.INCOMPLETE
|| pgTrace.outcome === PREGRADE_OUTCOME.OTHER_PREGRADING_ERROR;
return {
applicable: true,
complete: !undecided,
outcome: undecided ? PREGRADE_OUTCOME.OBSERVABILITY_GAP : pgTrace.outcome,
};
}
function finishPregrade(trace, { now } = {}) {
const t = trace;
t.outcome = classifyPregrade(t);
@@ -328,6 +401,7 @@ async function pregradeHistory(sport, deps = {}) {
module.exports = {
TRIGGER, SOURCE_OUTCOME, FINAL, OUTCOME, PREGRADE_OUTCOME,
pregradeKey, beginPregrade, pregradeRecorder, classifyPregrade, finishPregrade,
reconcilePregrade,
persistPregrade, pregradeHistory,
HISTORY_CAP, TTL_SECONDS, KEY_PREFIX,
key, sanitize, newAttemptId,
+36 -5
View File
@@ -516,6 +516,10 @@ async function runSnapshot(sport, opts = {}) {
try {
const binder = deps.gameBinder || require('./gameBinder');
const b = await binder.attachGameTimes(sp, props, { gradedAt: ts });
pgRec.binding({
started: true, completed: true, threw: false,
bound: b.bound, unresolved: b.unresolved, already_had: b.alreadyHad,
});
console.log(`[snapshot] game binding ${sp}: ${b.bound} bound, ${b.alreadyHad} already had times, ${b.unresolved} UNRESOLVED, ${b.ambiguous} ambiguous(doubleheader)`);
// CANONICAL EVENT IDENTITY (MLB). The bound game_time above is what makes
@@ -532,10 +536,19 @@ async function runSnapshot(sport, opts = {}) {
const mlbAdapter = deps.mlbAdapter || require('./adapters/mlbStatsAdapter');
const dates = [...new Set(props.map((p) => p && p.game_date).filter(Boolean))];
const games = [];
for (const d of dates) {
// eslint-disable-next-line no-await-in-loop
const g = await mlbAdapter.getScheduleWithPitchers(d);
if (Array.isArray(g)) games.push(...g);
pgRec.schedule({ started: true, completed: false, threw: false, dates_requested: dates.length });
try {
for (const d of dates) {
// eslint-disable-next-line no-await-in-loop
const g = await mlbAdapter.getScheduleWithPitchers(d);
if (Array.isArray(g)) games.push(...g);
}
} catch (eSch) {
// Record and RETHROW: the existing identity catch below still handles
// it exactly as before. Without this a schedule outage was reported as
// an event-identity error.
pgRec.schedule({ started: true, completed: false, threw: true, dates_requested: dates.length, error: eSch && eSch.message });
throw eSch;
}
// PARTICIPANT EVIDENCE. The prop's own team fields cannot police this:
// when the provider nests a player under the wrong event, those fields
@@ -545,6 +558,10 @@ async function runSnapshot(sport, opts = {}) {
// The roster is fetched AS OF THE SLATE'S DATE, not "now". statsapi
// honours a `date` parameter and it genuinely changes the answer, so
// the evidence describes the moment being judged.
pgRec.schedule({
started: true, completed: true, threw: false,
dates_requested: dates.length, games: games.length,
});
const slateDate = dates.length === 1 ? dates[0] : null;
let playerTeams = null;
let evidenceDateValid = false;
@@ -555,11 +572,17 @@ async function runSnapshot(sport, opts = {}) {
});
playerTeams = built.index;
evidenceDateValid = evid.evidenceIsDateValid(built.stats, slateDate);
pgRec.roster({
started: true, completed: true, threw: false,
players: built.stats.players, teams: built.stats.teams,
failed: built.stats.failed || 0, evidence_date_valid: evidenceDateValid,
});
console.log(`[snapshot] participant evidence ${sp}: ${built.stats.players} players`
+ ` across ${built.stats.teams} rosters as of ${slateDate || 'unscoped'}`
+ `${built.stats.failed ? `, ${built.stats.failed} unreadable` : ''}`
+ ` · date-valid=${evidenceDateValid}`);
} catch (e3) {
pgRec.roster({ started: true, completed: false, threw: true, error: e3 && e3.message });
console.warn(`[snapshot] participant evidence unavailable for ${sp}:`, e3.message);
}
// Without date-valid evidence a conflict can only be UNRESOLVED, never
@@ -589,6 +612,7 @@ async function runSnapshot(sport, opts = {}) {
});
}
} catch (e) {
pgRec.binding({ started: true, completed: false, threw: true, error: e && e.message });
console.warn(`[snapshot] game binding failed for ${sp} (rows without a real game time will be skipped):`, e.message);
}
@@ -698,6 +722,9 @@ async function runSnapshot(sport, opts = {}) {
}
}
// Records whether the hook existed at all, so `first_ongraded_observed:false`
// can be told apart from "no collector was attached".
pgRec.gradeLoop({ ongraded_hook_attached: !!collector });
await deps.gradeAndCacheSlate(sp, props, {
pregrade: pgRec,
factorContext,
@@ -710,8 +737,12 @@ async function runSnapshot(sport, opts = {}) {
source: (odds && odds.provider) || 'odds-api',
now: deps.now,
cacheSet: async (_k, v) => { envelope = v; },
onGraded: collector ? collector.onGraded : undefined,
// Wrapped ONLY when a collector exists, so the downstream
// `typeof opts.onGraded === 'function'` branch is exactly as before. The
// wrapper records a control-flow fact and delegates unchanged.
onGraded: collector ? ((base, sides) => { pgRec.firstOnGraded(); return collector.onGraded(base, sides); }) : undefined,
onPublished: collector ? collector.onPublished : undefined,
});
// Persist the pre-grading stage trace. Best-effort at the call site: a
// telemetry failure must never fail a healthy slate.