#!/usr/bin/env node // Offline rival audit for the existing audible G#2 trace. No browser, no writes // to production defaults, and no counterfactual is applied here. import fs from 'node:fs'; import { dequantiseSal } from '../static/salience.js'; import { ScoreFollower, FOLLOWER_DEFAULTS } from '../static/follower.js'; const tracePath = process.argv[2] || '/tmp/notepilot-left-loop/mic-follower-hold-diagnostics-20260918-v2.json'; const tr = JSON.parse(fs.readFileSync(tracePath, 'utf8')); const ev = tr.events, meta = tr.meta || {}, opt = meta.follower?.options || FOLLOWER_DEFAULTS; const sr = meta.follower?.sr || 48000, frames = ev.filter(e => e.type === 'follow.frame' && Array.isArray(e.sal)); const attempts = ev.filter(e => e.type === 'input.testEvent' && e.scoreEventId === 1); const nextTimes = attempts.map((a,i) => ev.find(e => e.type === 'input.testEvent' && e.t > a.t)?.t || a.t + 3000); const input = e => ({seq:e.k, at:e.at, sr, win:meta.follower?.win||2048, hop:meta.follower?.hop||512, db:e.db, floorDb:e.fl, gated:!!e.g, attack:!!e.a, onset:e.on, sal:dequantiseSal(Uint8Array.from(e.sal), e.pk)}); const name = m => { const n=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']; return `${n[((m%12)+12)%12]}${Math.floor(m/12)-1}`; }; const finite = x => Number.isFinite(x) ? +x.toFixed(4) : null; const hOffsets = [2,3,4,5,6,7,8,9,10].map(h => [h, Math.round(12*Math.log2(h))]); const harmonic = q => { const d=q-44; const h=hOffsets.find(([_,o])=>Math.abs(d-o)<=1); return h ? `h${h[0]} (Δ${d-h[1]})` : null; }; const all = []; const diag=[]; const sf=new ScoreFollower({solo:meta.rh, sampleRate:sr, win:2048, hop:meta.follower?.hop||512, position:0, options:{...opt, diagnostic:d=>diag.push(d)}}); const fired=new Set(); for (const f of frames) { for (let fi=0; fif.t>=a.t && f.t<=nextTimes[i]); if (ai < 0) continue; const c=d?.hyps?.find(h=>h.i===1); if (!c || !d?.wrong) continue; const p=out.posterior?.find(x=>x.i===1)?.p ?? 0; all.push({attempt:ai, t:f.t, role:'all', p, ev:c.ev, distinct:c.distinct, ell:c.ell, rss:c.fit?.rss ?? null, explained:c.explained, gNw:c.fit?.gNw ?? null, gPv:c.fit?.gPv ?? null, ageMs: f.t-c.tOn, onsetDriven:c.onsetDriven, pWrong:out.pWrong, wrongEll:d.wrong.ell, q:d.wrong.q, path:d.wrong.path, feedQ:d.wrong.feed?.q ?? null, feedEll:d.wrong.feed?.ell ?? null, wrongGain:d.wrong.gain ?? null, wrongRss:d.wrong.rss ?? null, gHeld:d.wrong.gHeld || null}); } for (let ai=0; air.attempt===ai); if(!rows.length) continue; const picks=[['peak-posterior',rows.reduce((x,y)=>y.p>x.p?y:x)],['peak-evidence',rows.reduce((x,y)=>y.ev>x.ev?y:x)]]; if(ai===2) { const cross=rows.filter(r=>r.ev>=2.5).reduce((x,y)=>Math.abs(y.p-0.5)