const $=id=>document.getElementById(id);
const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
let bundle;
function structure(b){
if(b?.schema_version!=='clw-reliability-lab/1'||!Array.isArray(b.experiments)||b.experiments.length!==4||typeof b.source_sha256!=='string')throw Error('Not a four-fixture Reliability Lab result.');
for(const e of b.experiments)for(const p of ['before','after'])if(!e[p]?.trace?.messages||!Array.isArray(e[p].attempts))throw Error('Incomplete evidence.');
}
function phase(p,label){return `
${label}
${esc(p.outcome.replaceAll('_',' '))}
${p.attempts.length} attempt${p.attempts.length===1?'':'s'} · ${esc(p.policy.timeout_ms)} ms deadline
${p.attempts.map(a=>`
Call ${a.attempt}: ${esc(a.outcome)} · ${esc(a.elapsed_ms)} ms
`;}
function download(data){const a=document.createElement('a'),url=URL.createObjectURL(new Blob([JSON.stringify(data,null,2)],{type:'application/json'}));a.href=url;a.download='reliability-evidence.json';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}
async function integrity(b){for(const e of b.experiments)for(const p of ['before','after']){const bytes=new TextEncoder().encode(JSON.stringify(e[p].trace));const digest=Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',bytes)),x=>x.toString(16).padStart(2,'0')).join('');if(digest!==e[p].trace_sha256)throw Error('Trace integrity mismatch.');}}
$('upload').onchange=async()=>{try{const file=$('upload').files[0];if(!file)return;if(file.size>2097152)throw Error('Maximum comparison file size is 2 MiB.');const other=JSON.parse(await file.text());structure(other);await integrity(other);if(other.source_sha256!==bundle.source_sha256||other.fixture_version!==bundle.fixture_version)throw Error('Source or fixture version differs. These runs are not directly comparable.');const rows=bundle.experiments.map(e=>{const o=other.experiments.find(x=>x.id===e.id);if(!o)throw Error('Missing fixture.');const agrees=['before','after'].every(p=>e[p].outcome===o[p].outcome&&e[p].attempts.length===o[p].attempts.length&&JSON.stringify(e[p].policy)===JSON.stringify(o[p].policy));return `
${esc(e.id)}: ${agrees?'same outcome and policy':'DIFFERENT outcome or policy'}