A cohort is a snapshot_id, not a date slice of one
Found by cross-checking the observer against an independent SQL derivation of the same natural cohort instead of accepting its own answer. The 2026-08-31 03:00Z run held 1,970 rows across TWO game_dates — 1,572 on 08-31 and 398 on 08-30, the late slot spanning midnight ET. The observer selected the head row's date and filtered to it, so it measured 646 of the 801 eligible keys and reported HEALTHY. Both slices happened to be fully covered, so the verdict was right by luck; a date slice with zero lineage would have been invisible behind it. The audit now covers the whole snapshot_id and reports every game_date it spans. A test builds the real two-date shape with one slice uncovered and requires PARTIAL_COVERAGE. Suite 397/5,499/0 · 15/15 teeth. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQJeAG8vcDoL5zkiaJyVb8
This commit is contained in:
@@ -190,6 +190,7 @@ async function auditLatestCohort(sport, deps = {}) {
|
|||||||
const supabase = getClient();
|
const supabase = getClient();
|
||||||
if (!supabase) return Object.freeze({ audit_available: false, reason: 'no_supabase_env', sport: sp, health: HEALTH.AUDIT_UNAVAILABLE });
|
if (!supabase) return Object.freeze({ audit_available: false, reason: 'no_supabase_env', sport: sp, health: HEALTH.AUDIT_UNAVAILABLE });
|
||||||
|
|
||||||
|
// The head identifies the COHORT (snapshot_id), not one date within it.
|
||||||
const { data: head, error: headErr } = await supabase
|
const { data: head, error: headErr } = await supabase
|
||||||
.from('model_snapshots')
|
.from('model_snapshots')
|
||||||
.select('snapshot_id, game_date, captured_at, code_sha')
|
.select('snapshot_id, game_date, captured_at, code_sha')
|
||||||
@@ -202,15 +203,22 @@ async function auditLatestCohort(sport, deps = {}) {
|
|||||||
const h = head[0];
|
const h = head[0];
|
||||||
|
|
||||||
const { paginate } = require('../utils/safePaginate');
|
const { paginate } = require('../utils/safePaginate');
|
||||||
|
// A COHORT IS A snapshot_id, NOT A DATE SLICE OF ONE.
|
||||||
|
//
|
||||||
|
// Measured on the 2026-08-31 03:00Z run: the cohort held 1,970 rows across
|
||||||
|
// TWO game_dates (1,572 on 08-31 and 398 on 08-30, the late slot spanning
|
||||||
|
// midnight ET). Filtering to the head row's date measured 646 of 801
|
||||||
|
// eligible keys and still reported HEALTHY — so a whole date slice with
|
||||||
|
// zero lineage would have been invisible. The audit covers the cohort.
|
||||||
const rows = await paginate(() => supabase
|
const rows = await paginate(() => supabase
|
||||||
.from('model_snapshots')
|
.from('model_snapshots')
|
||||||
.select(AUDIT_COLUMNS)
|
.select(AUDIT_COLUMNS)
|
||||||
.eq('snapshot_id', h.snapshot_id)
|
.eq('snapshot_id', h.snapshot_id)
|
||||||
.eq('sport', sp)
|
.eq('sport', sp), { key: 'id', label: 'lineageCoverage.auditLatestCohort' });
|
||||||
.eq('game_date', h.game_date), { key: 'id', label: 'lineageCoverage.auditLatestCohort' });
|
|
||||||
|
|
||||||
const audit = auditRows(rows, {
|
const audit = auditRows(rows, {
|
||||||
snapshot_id: h.snapshot_id, sport: sp, game_date: h.game_date,
|
snapshot_id: h.snapshot_id, sport: sp,
|
||||||
|
game_date: [...new Set(rows.map((r) => r.game_date))].sort().join(','),
|
||||||
code_sha: h.code_sha, captured_at: h.captured_at,
|
code_sha: h.code_sha, captured_at: h.captured_at,
|
||||||
retention_terminal: deps.retentionTerminal ?? null,
|
retention_terminal: deps.retentionTerminal ?? null,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -354,6 +354,26 @@ describe('coverage observer', () => {
|
|||||||
.toEqual([...retention.VALID_LINEAGE_ACTION_FIELDS].sort());
|
.toEqual([...retention.VALID_LINEAGE_ACTION_FIELDS].sort());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a cohort spanning two game_dates is audited whole, not one slice', () => {
|
||||||
|
// The real 2026-08-31 03:00Z shape: a late slot spanning midnight ET. Rows
|
||||||
|
// from BOTH dates are eligible and both must be counted, or a date slice
|
||||||
|
// with no lineage stays invisible behind a HEALTHY verdict.
|
||||||
|
const mk = (d, p, covered) => ({
|
||||||
|
sport: 'mlb', game_date: d, player_key: p, stat: 'hits', side: 'over',
|
||||||
|
line: 0.5, published: true,
|
||||||
|
...(covered ? {
|
||||||
|
read_id: `r-${p}`, read_natural_key: `mlb|${d}|${p}|hits|over|0.500000`,
|
||||||
|
lineage_action: 'ORIGIN', claim_digest: 'd', revision_ordinal: 0,
|
||||||
|
lineage_state: 's', lineage_version: 'lin@1', claim_schema_version: 'claim@1',
|
||||||
|
digest_algorithm_version: 'sha256-json-sorted@1',
|
||||||
|
} : {}),
|
||||||
|
});
|
||||||
|
const a = coverage.auditRows([mk('2026-08-31', 'a', true), mk('2026-08-30', 'b', false)]);
|
||||||
|
expect(a.expected_keys).toBe(2);
|
||||||
|
expect(a.covered_keys).toBe(1);
|
||||||
|
expect(a.health).toBe(coverage.HEALTH.PARTIAL_COVERAGE);
|
||||||
|
});
|
||||||
|
|
||||||
test('an unavailable audit is not a coverage claim', () => {
|
test('an unavailable audit is not a coverage claim', () => {
|
||||||
expect(coverage.classify({ audit_available: false })).toBe(coverage.HEALTH.AUDIT_UNAVAILABLE);
|
expect(coverage.classify({ audit_available: false })).toBe(coverage.HEALTH.AUDIT_UNAVAILABLE);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user