#!/usr/bin/env node /* TEETH — inject a defect, prove the suite CATCHES it, restore byte-identically. A green teeth run means the injection missed, not that the risk is absent, so every tooth asserts its own injection landed before the suite is run. */ const fs = require('fs'); const path = require('path'); const cp = require('child_process'); const ROOT = __dirname; const F = (p) => path.join(ROOT, p); const TEETH = [ { n: 1, name: 'resolved dedupe reverts to the raw spelling', file: 'src/services/gradeSlateService.js', from: "const who = p.mlb_person_id != null ? `mlb:${p.mlb_person_id}` : p.player;\n const key = `${propositionEventKey(p)}", to: "const who = p.player;\n const key = `${propositionEventKey(p)}", suite: 'tests/unit/participantIdentityConvergence.test.js' }, { n: 2, name: 'retention ignores the canonical participant (split returns)', file: 'src/services/model/participantIdentity.js', from: "const canonical = p.mlb_person_id != null ? p.canonical_player_name : null;", to: "const canonical = null;", suite: 'tests/unit/participantIdentityConvergence.test.js' }, { n: 3, name: 'two proven personIds sharing a name key are merged, not refused', file: 'src/services/event/eventIdentity.js', from: "if (ids.size !== 1) return null;", to: "if (ids.size < 1) return null;", suite: 'tests/unit/participantIdentityConvergence.test.js tests/unit/canonicalParticipant.test.js' }, { n: 4, name: 'representative chosen by arrival (last wins) — completion-order authority', file: 'src/services/gradeSlateService.js', from: " best.set(key, betterRepresentative(best.get(key), p));", to: " best.set(key, p);", suite: 'tests/unit/participantIdentityConvergence.test.js' }, { n: 5, name: 'representative chosen by input order (first wins)', file: 'src/services/gradeSlateService.js', from: " best.set(key, betterRepresentative(best.get(key), p));", to: " if (!best.has(key)) best.set(key, p);", suite: 'tests/unit/participantIdentityConvergence.test.js' }, { n: 6, name: 'raw provider name dropped from retention (provenance lost)', file: 'src/services/retentionService.js', from: " player_name: normalizeName(player).display || player,", to: " player_name: normalizeName(identity.display_source).display || player,", suite: 'tests/unit/canonicalParticipant.test.js' }, { n: 7, name: 'every participant is required to resolve', file: 'src/services/gradeSlateService.js', from: " if (p.participant_identity_conflict) {", to: " if (p.participant_identity_conflict || (p.event_binding_status === 'RESOLVED' && p.mlb_person_id == null)) {", suite: 'tests/unit/participantIdentityConvergence.test.js' }, { n: 8, name: 'name key used globally, without event/team scope', file: 'src/services/event/eventIdentity.js', from: "const cands = (persons.get(nameKey(prop.player)) || []).filter((c) => abbrs.has(c.abbr));", to: "const cands = (persons.get(nameKey(prop.player)) || []);", suite: 'tests/unit/canonicalParticipant.test.js' }, { n: 9, name: 'doubleheader propositions collapse across gamePk', file: 'src/services/gradeSlateService.js', from: " const key = `${propositionEventKey(p)}::${who}::${p.stat_type}::${p.line}`;\n if (seen.has(key)) { bump('duplicate_identity_removed'); continue; }", to: " const key = `${who}::${p.stat_type}::${p.line}`;\n if (seen.has(key)) { bump('duplicate_identity_removed'); continue; }", suite: 'tests/unit/participantIdentityConvergence.test.js tests/unit/eventPublication.test.js' }, { n: 10, name: 'retention collision hard-zero guard weakened', file: 'src/services/retentionService.js', from: " if (seen.has(id)) collisions += 1;", to: " if (false) collisions += 1;", suite: 'tests/unit/canonicalParticipant.test.js tests/unit/participantIdentityConvergence.test.js' }, { n: 11, name: 'lineage natural-key composition changed', file: 'src/services/retentionService.js', from: "const LINEAGE_KEYS", to: "const LINEAGE_KEYS_RENAMED_BY_TEETH", suite: 'tests/unit/publicationLineage.test.js tests/unit/readLineage.test.js', expectLoadError: true }, { n: 12, name: 'lineage history rewritten on an existing row', file: 'src/services/retentionService.js', from: "await upsertSnapshotChunk(supabase, chunk, { ignoreDuplicates: true })", to: "await upsertSnapshotChunk(supabase, chunk, { ignoreDuplicates: false })", suite: 'tests/unit/participantIdentityConvergence.test.js tests/unit/publicationLineage.test.js' }, { n: 13, name: 'model belief moved by the identity repair', file: 'src/services/intelligence/probabilityEstimator.js', from: "function estimate", to: "function estimate_TEETH_BROKEN", suite: 'tests/unit/probabilityEstimator.test.js', expectLoadError: true }, { n: 14, name: 'intraday belief-integrity repair reverted', file: 'src/services/intradayRefreshService.js', from: "const BELIEF_FIELDS", to: "const BELIEF_FIELDS_TEETH", suite: 'tests/unit/intradayBeliefIntegrity.test.js', expectLoadError: true }, { n: 15, name: 'MLB ET cache-date contract reverted to UTC', file: 'src/services/oddsService.js', from: "const ET_DATE_SPORTS = Object.freeze(['mlb']);", to: "const ET_DATE_SPORTS = Object.freeze([]);", suite: 'tests/unit/mlbOddsCacheDate.test.js' }, ]; let caught = 0; const missed = []; for (const t of TEETH) { const p = F(t.file); const orig = fs.readFileSync(p, 'utf8'); if (!orig.includes(t.from)) { missed.push(`${t.n} ANCHOR NOT FOUND: ${t.name}`); continue; } const broken = orig.replace(t.from, t.to); if (broken === orig) { missed.push(`${t.n} INJECTION NO-OP: ${t.name}`); continue; } fs.writeFileSync(p, broken); // Prove the defect is actually present before trusting any result. const present = fs.readFileSync(p, 'utf8').includes(t.to); let failed = false; try { cp.execSync(`npx jest ${t.suite} --testPathIgnorePatterns "/node_modules/" --silent`, { cwd: ROOT, stdio: 'pipe' }); } catch { failed = true; } fs.writeFileSync(p, orig); if (fs.readFileSync(p, 'utf8') !== orig) { console.error('RESTORE FAILED', t.file); process.exit(2); } if (!present) { missed.push(`${t.n} INJECTION NOT PRESENT: ${t.name}`); continue; } if (failed) { caught += 1; console.log(` tooth ${String(t.n).padStart(2)} CAUGHT ${t.name}`); } else { missed.push(`${t.n} NOT CAUGHT: ${t.name}`); console.log(` tooth ${String(t.n).padStart(2)} MISSED ${t.name}`); } } console.log(`\n ${caught}/${TEETH.length} teeth landed`); if (missed.length) { console.log(' PROBLEMS:'); missed.forEach((m) => console.log(' ', m)); process.exit(1); }