from pathlib import Path
import json,hashlib,re,math,statistics
root=Path(r'D:\Skynet\browser_reliability_research')
R=root/'results'
orig=[json.loads(x) for x in (R/'runs.jsonl').read_text().splitlines() if x.strip()]
multi=[json.loads(x) for x in (R/'multitemplate_runs.jsonl').read_text().splitlines() if x.strip()]
horizon=json.loads((R/'horizon_sensitivity_summary.json').read_text()) if (R/'horizon_sensitivity_summary.json').exists() else None
llm=json.loads((root/'llm_pilot_n30'/'executions.json').read_text())
man=(root/'manuscript_final.md').read_text(encoding='utf-8')

def vts(x): return bool(x.get('vts',x.get('verifiedTaskSuccess',False)))
def app(x): return bool(x.get('apparent',x.get('apparentSuccess',False)))
def fc(x): return bool(x.get('falseCompletion',app(x) and not vts(x)))
def sha(p):
    b=p.read_bytes(); return {'bytes':len(b),'sha256':hashlib.sha256(b).hexdigest()}
assert len(orig)==432 and len(multi)==432 and len(llm)==60
allc=orig+multi
assert len(allc)==864
assert len({(x['taskId'],x['policy']) for x in allc})==864
summary={}
for p in ['selector-only','semantic-recovery','verification-aware']:
    rr=[x for x in allc if x['policy']==p]
    summary[p]={'n':len(rr),'verified':sum(vts(x) for x in rr),'verified_rate':sum(vts(x) for x in rr)/len(rr),'apparent':sum(app(x) for x in rr),'apparent_rate':sum(app(x) for x in rr)/len(rr),'false_completions':sum(fc(x) for x in rr),'mean_actions':statistics.mean(x.get('actions',0) for x in rr),'mean_latency_ms':statistics.mean(x.get('latencyMs',x.get('latency_ms',0)) for x in rr)}
assert summary['selector-only']['verified']==53 and summary['selector-only']['apparent']==184 and summary['selector-only']['false_completions']==131
assert summary['semantic-recovery']['verified']==192 and summary['semantic-recovery']['false_completions']==96
assert summary['verification-aware']['verified']==288 and summary['verification-aware']['false_completions']==0
# LLM paired
modes={m:[x for x in llm if x['promptMode']==m] for m in ['default','reliability']}
ls={m:{'n':len(rr),'vts':sum(x['verifiedTaskSuccess'] for x in rr),'apparent':sum(x['apparentSuccess'] for x in rr),'fc':sum(x['falseCompletion'] for x in rr),'mean_steps':statistics.mean(x['executedSteps'] for x in rr)} for m,rr in modes.items()}
d={x['taskId']:x for x in modes['default']}; q={x['taskId']:x for x in modes['reliability']}
a=sum(d[k]['verifiedTaskSuccess'] and not q[k]['verifiedTaskSuccess'] for k in d); b=sum((not d[k]['verifiedTaskSuccess']) and q[k]['verifiedTaskSuccess'] for k in d); n=a+b
pval=min(1,2*sum(math.comb(n,k) for k in range(min(a,b)+1))/2**n) if n else 1
assert ls['default']['vts']==16 and ls['reliability']['vts']==21 and a==0 and b==5 and abs(pval-.0625)<1e-12
# Horizon file may use different schema; locate known summary if present
if horizon:
    hs=horizon.get('summary',horizon)
    assert hs['state']['50']['vts']==0 and hs['state']['750']['vts']==24 and hs['verification']['1000']['vts']==0
# Manuscript consistency assertions
need=['864 paired validation-script Chromium traces','288 conditions and three interface archetypes','240-trace scoring-horizon ablation','16/30 (53.3%)','21/30 (70.0%)','p = 0.0625','131 false completions','96 false completions']
missing=[x for x in need if x not in man]
assert not missing,missing
abstract=re.search(r'## Abstract\n\n(.*?)\n\n\*\*Index Terms',man,re.S).group(1)
aw=len(re.findall(r"\b[\w'-]+\b",abstract)); assert 150<=aw<=250
# No uncited core related-work names in related section; references count 10.
assert len(re.findall(r'^\[\d+\]',man,re.M))==10
files=[root/'manuscript_final.md',root/'rstack_benchmark.js',root/'multitemplate_benchmark.js',root/'horizon_sensitivity.js',root/'execute_llm_n30.js',root/'llm_pilot_n30'/'plan_index.json',root/'llm_pilot_n30'/'executions.json',R/'runs.jsonl',R/'multitemplate_runs.jsonl',R/'summary.json',R/'multitemplate_summary.json']
if (R/'horizon_sensitivity_summary.json').exists(): files.append(R/'horizon_sensitivity_summary.json')
files += sorted((root/'figures_final').glob('*.png')) + sorted((root/'figures_final').glob('*.svg'))
hashes={str(x.relative_to(root)).replace('\\','/'):sha(x) for x in files if x.exists()}
out={'ok':True,'counts':{'task_conditions_cross_template':288,'validation_traces':864,'horizon_traces':240,'llm_plans':60,'llm_browser_executions':60,'total_browser_traces':1164},'validation_summary':summary,'llm_summary':ls,'llm_paired':{'default_wins':a,'reliability_wins':b,'exact_two_sided_mcnemar_p':pval},'abstract_words':aw,'manuscript_consistency':{'required_strings_missing':missing,'reference_entries':10},'hashes':hashes}
(root/'final_evidence_audit.json').write_text(json.dumps(out,indent=2),encoding='utf-8')
(root/'FINAL_REPRODUCIBILITY_MANIFEST.json').write_text(json.dumps({'generated':'2026-08-14','root':'browser_reliability_research','audit_sha256':sha(root/'final_evidence_audit.json')['sha256'],'files':hashes},indent=2),encoding='utf-8')
print(json.dumps(out,indent=2))

