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
+23 -11
View File
@@ -48,8 +48,11 @@ describe('THE STAGE MATRIX', () => {
expect(trace.admission.rejected_count).toBe(0);
expect(trace.dedupe.completed).toBe(true);
expect(trace.dedupe.output_count).toBe(5);
expect(trace.grade_loop).toEqual({ candidate_count: 5, reached: true });
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.READY_FOR_GRADING);
expect(trace.grade_loop).toMatchObject({ candidate_count: 5, reached: true });
// The loop now proves it ENTERED, which is strictly stronger than a
// candidate count, so the terminal state advances past READY_FOR_GRADING.
expect(trace.grade_loop.first_gradebestside_started).toBe(true);
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.GRADE_LOOP_STARTED);
});
test('ALL_REJECTED — with EXACT reason accounting', async () => {
@@ -74,7 +77,7 @@ describe('THE STAGE MATRIX', () => {
expect(trace.admission.admitted_count).toBe(4);
expect(trace.admission.rejected_count).toBe(3);
expect(trace.admission.rejection_reason_counts.EVENT_PLAYER_TEAM_CONTRADICTION).toBe(3);
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.READY_FOR_GRADING);
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.GRADE_LOOP_STARTED);
});
test('DEDUPE_EMPTY — admitted > 0 but dedupe filters to zero, NOT ALL_REJECTED', async () => {
@@ -84,7 +87,9 @@ describe('THE STAGE MATRIX', () => {
expect(trace.admission.admitted_count).toBe(5);
expect(trace.dedupe.input_count).toBe(5);
expect(trace.dedupe.output_count).toBe(0);
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.DEDUPE_EMPTY);
// Now names WHICH filter emptied it, not merely that it emptied.
expect(trace.dedupe.dropped_non_model_book).toBe(5);
expect(trace.outcome).toBe(acq.PREGRADE_OUTCOME.DEDUPE_ALL_NON_MODEL_BOOK);
expect(trace.outcome).not.toBe(acq.PREGRADE_OUTCOME.ALL_REJECTED);
});
@@ -126,7 +131,9 @@ describe('THE STAGE MATRIX', () => {
await snap2.runSnapshot('mlb', {
getOdds: async () => ({ sport: 'mlb', props: props(2), provider: 'propline', source: 'cache' }),
gradeAndCacheSlate: async () => ({ written: false, count: 0 }),
eventIdentity: { buildPlayerTeamIndex: async () => { throw new Error('roster down'); },
// Roster succeeds so the IDENTITY throw is the one under test; roster now
// has its own terminal state and would otherwise (correctly) outrank it.
eventIdentity: { buildPlayerTeamIndex: async () => ({ index: {}, stats: { players: 1, teams: 1, failed: 0 } }),
evidenceIsDateValid: () => false,
attachEventIdentity: () => { throw new Error('statsapi shape changed'); } },
mlbAdapter: { getScheduleWithPitchers: async () => [] },
@@ -141,14 +148,14 @@ describe('THE STAGE MATRIX', () => {
expect(pg).toHaveLength(1);
expect(pg[0].identity).toBeTruthy();
expect(pg[0].identity.threw).toBe(true);
expect(pg[0].identity.error).toMatch(/statsapi shape changed|roster down/);
expect(pg[0].outcome).toBe(acq.PREGRADE_OUTCOME.IDENTITY_STAGE_ERROR);
expect(pg[0].identity.error).toMatch(/statsapi shape changed/);
expect(pg[0].outcome).toBe(acq.PREGRADE_OUTCOME.EVENT_IDENTITY_STAGE_ERROR);
});
test('IDENTITY_STAGE_ERROR outranks everything downstream', async () => {
test('EVENT_IDENTITY_STAGE_ERROR outranks everything downstream', async () => {
const t = acq.beginPregrade({ attemptId: 'a', sport: 'mlb', propsCount: 3 });
acq.pregradeRecorder(t).identity({ started: true, completed: false, threw: true, error: 'statsapi down' });
expect(acq.finishPregrade(t, {}).outcome).toBe(acq.PREGRADE_OUTCOME.IDENTITY_STAGE_ERROR);
expect(acq.finishPregrade(t, {}).outcome).toBe(acq.PREGRADE_OUTCOME.EVENT_IDENTITY_STAGE_ERROR);
});
test('an empty collector alone can NEVER be classified', () => {
@@ -225,7 +232,12 @@ describe('THE OBSERVER DOES NOT TOUCH THE PROPS', () => {
test('the recorder default is a frozen no-op, so existing callers are identical', () => {
const src = fs.readFileSync(path.join(ROOT, 'src/services/gradeSlateService.js'), 'utf8');
expect(src).toMatch(/const NO_PREGRADE = Object\.freeze\(\{ identity\(\) \{\}, admission\(\) \{\}, dedupe\(\) \{\}, gradeLoop\(\) \{\} \}\)/);
// Now multi-line and covering the full recorder surface; match the shape,
// not the exact one-line form.
expect(src).toMatch(/const NO_PREGRADE = Object\.freeze\(\{/);
for (const m of ['identity', 'admission', 'dedupe', 'gradeLoop', 'gradeStarted', 'firstOnGraded']) {
expect(src).toMatch(new RegExp(`${m}\\(\\) \\{\\}`));
}
expect(src).toMatch(/const pregrade = opts\.pregrade \|\| NO_PREGRADE;/);
});
});
@@ -309,7 +321,7 @@ describe('THE TRACE IS ACTUALLY PERSISTED BY THE REAL PATH', () => {
expect(pgStored[0].snapshot_attempt_id).toBe(acqStored[0].snapshot_attempt_id);
expect(pgStored[0].input_props_count).toBe(3);
expect(pgStored[0].admission.input_count).toBe(3);
expect(pgStored[0].outcome).toBe(acq.PREGRADE_OUTCOME.READY_FOR_GRADING);
expect(pgStored[0].outcome).toBe(acq.PREGRADE_OUTCOME.GRADE_LOOP_STARTED);
});
});