#!/usr/bin/env node // Post-fix re-grade validation (work-order item #4). Fetches the LIVE MLB // snapshot and checks the three degradation signatures are gone: // 1. projection == 0 count -> expect 0 (the nine vanish; those props refuse) // 2. edge_pct continuous, not the {20,60,100,140} degenerate cluster, no 100 // 3. grade <-> confidence agreement -> expect 25/25 (letter == band(conf)) // Run: node scripts/validate-grade-fix.js (needs outbound to api.vyndr.app) const https = require('https'); const BANDS = require('../src/services/python/data/grade_thresholds.json').grade_scale; const four = (g) => { const s = String(g||'').toUpperCase(); return s === 'A+' ? 'A+' : (s[0]||null); }; function bandOf(conf) { const p = conf/100; for (const [k,b] of Object.entries(BANDS)) if (p>=b.low && p<=b.high) return k; return null; } function get(url) { return new Promise((res,rej) => https.get(url,(r)=>{let d='';r.on('data',c=>d+=c);r.on('end',()=>res(JSON.parse(d)));}).on('error',rej)); } (async () => { const d = await get('https://api.vyndr.app/api/snapshot/mlb'); const g = d.grades || []; const proj0 = g.filter(x => x.projection === 0 || x.projection == null).length; const edges = g.map(x => x.edge_pct).filter(v => v != null); const has100 = edges.includes(100); const distinctEdges = [...new Set(edges)].sort((a,b)=>a-b); let agree = 0; for (const x of g) { const b = bandOf(x.confidence); if (b && four(b) === four(x.grade)) agree++; } console.log('=== AFTER (post-fix) — live MLB snapshot ==='); console.log('updated_at:', d.updated_at, '| grades:', g.length); console.log('1. projection==0/null:', proj0, proj0 === 0 ? 'PASS' : 'FAIL (expect 0)'); console.log('2. edge_pct distinct:', distinctEdges.length, 'values; contains 100:', has100, (!has100 && distinctEdges.length > 4) ? 'PASS' : 'CHECK'); console.log(' values:', distinctEdges.slice(0, 20)); console.log('3. grade<->confidence agree:', agree + '/' + g.length, agree === g.length ? 'PASS' : 'FAIL'); })().catch(e => { console.error('validation error:', e.message); process.exit(1); });