#!/usr/bin/env python3 """Post-output unblinded source audit. Original frozen grades are never edited.""" from pathlib import Path import json,hashlib,re,unicodedata,datetime,statistics from pypdf import PdfReader R=Path(__file__).resolve().parents[3]; E=R/'evidence/comparison-v15'; P=E/'input-pack'; A=E/'analysis/results'; O=E/'analysis/source-review' sha=lambda p:hashlib.sha256(Path(p).read_bytes()).hexdigest() load=lambda p:json.loads(Path(p).read_text()) rows=lambda p:[json.loads(l) for l in Path(p).read_text().splitlines() if l.strip()] compact=lambda s:''.join(c for c in unicodedata.normalize('NFKC',s) if not c.isspace()) frozen=load(E/'comparison-freeze.json'); completion=load(A/'analysis-completion.json') assert completion['frozen_inputs_reverified'] and completion['retained_A']==completion['retained_B']==120 before={p.name:sha(p) for p in A.iterdir() if p.is_file()} refs={x['id']:x for x in load(P/'references.json')}; docs={}; pdfhash={} for p in sorted((P/'corpus').glob('*.pdf')): assert sha(p)==frozen['corpus_pdf_sha256'][p.name] docs[p.stem]=[page.extract_text() or '' for page in PdfReader(p).pages]; pdfhash[p.stem]=sha(p) # Preserve character-to-page locations while ignoring PDF-parser whitespace only. indices={} for doc,pages in docs.items(): text=''; offsets=[] for page,source in enumerate(pages,1): for index,char in enumerate(source): for c in unicodedata.normalize('NFKC',char): if not c.isspace(): text+=c; offsets.append((page,index)) indices[doc]=(text,offsets) def locate(doc,quote): if doc not in indices or not quote:return None text,offsets=indices[doc]; needle=compact(quote); start=text.find(needle) if not needle or start<0 or text.find(needle,start+1)>=0:return None first,last=offsets[start],offsets[start+len(needle)-1] return {'document_id':doc,'pdf_sha256':pdfhash[doc],'pages':list(range(first[0],last[0]+1)),'first_page_character':first[1],'last_page_character_inclusive':last[1],'exact_nonwhitespace_character_match':True,'native_quote_sha256':hashlib.sha256(quote.encode()).hexdigest()} assert locate('doc-01','UNMATCHED SOURCE CANARY xzq-91') is None # These are explicit post-output review judgments, not newly accepted primary aliases. notes={ ('A','held-02'):'Verbatim source sentence supplies drawer L4 and the second evening bell, in a different word order than the canonical phrase.', ('A','held-16'):'Declines although doc-08 page 3 explicitly supplies 18:10.', ('A','held-18'):'Declines although doc-09 page 3 explicitly supplies 33 days.', ('A','held-06'):'Generic exhibit desk does not supply the requested desk identifier R2.', ('B','held-02'):'Complete source sentence states both requested handoff facts.', ('B','held-04'):'26-day sentence and subsequent disposal sentence are both in the cited source.', ('B','held-16'):'Full sentence correctly states 18:10.', ('B','held-29'):'Muted stage is the actual source section heading immediately defining the felt-damped test; Eli Dorr is correct.', ('B','held-28'):'Return stage is the actual section heading immediately defining the edge inspection; Ada Pell is correct.', ('B','held-30'):'Both full-sentence fold-settle stage and Rae Noll statements are supported.', ('B','held-26'):'Display dispatch stage identifies a source section but omits the explicitly requested completed contour-check condition; retain semantic uncertainty.', ('B','held-27'):'Muted stage is a source heading for silent preview, but playback cue does not state the required square-card trigger.'} field_override={('B','held-26','plinth_transfer_stage'):'unresolved',('B','held-27','playback_cue'):'fail',('A','held-06','log_desk'):'fail'} manual_audit=[]; summaries={}; signature={}; native_ranges=0; native_cross=0 for system,file in [('A','kingy-common.jsonl'),('B','lmstudio-common.jsonl')]: original=rows(A/file) for row in original: qid=row['question_id']; ref=refs[qid]; norm=row['normalized_response']; values=norm['answer_fields']; citations=norm['citations']; unknown=ref['expected_abstention'] sig=json.dumps({'fields':values,'abstain':norm['abstain']},sort_keys=True) key=(system,qid); assert key not in signature or signature[key]==sig; signature[key]=sig facts={f:field_override.get((system,qid,f),'pass') for f in row['required_fields']} if system=='A' and qid in ['held-16','held-18']:facts={f:'fail' for f in row['required_fields']} if unknown: assert norm['abstain'] or (set(values)==set(row['required_fields']) and all(v.strip().upper()=='UNKNOWN' for v in values.values())) supports=[] for c in citations: source=locate(c.get('document_id'),c.get('quote','')) assert source is not None, (system,qid,'unmapped original citation',c.get('native_id')) if system=='B':native_ranges+=1;native_cross+=len(source['pages'])>1 field=c['field']; claim=next((x for x in ref.get('essential_claims',[]) if x['field']==field),None) evidence=claim['evidence'] if claim else ref.get('abstention_evidence',[]) # An emitted citation earns credit only for the requested fact it accompanies. covered=any(e['document_id']==source['document_id'] and e['page'] in source['pages'] and compact(e['quote']) in compact(c['quote']) for e in evidence) status=facts.get(field,'pass') if covered else 'fail' supports.append({'field':field,'native_id':c.get('native_id'),'status':status,'source':source,'reference_support_covered':covered,'original_quote':c['quote']}) if unknown:status='pass'; factual=None else: factual='fail' if 'fail' in facts.values() else 'unresolved' if 'unresolved' in facts.values() else 'pass' status=factual if status=='pass' and (set(c['field'] for c in supports if c['status']=='pass')!=set(row['required_fields'])):status='fail' manual_audit.append({'system':system,'repetition':row['repetition'],'question_id':qid,'required_fields':row['required_fields'],'original_field_values':values,'raw_native_text':row.get('raw_text'),'expected_abstention':unknown,'fact_status':factual,'supported_answer_status':status,'abstention_status':'pass' if unknown else None,'field_judgments':facts,'citations':supports,'review_note':notes.get(key,'Canonical value/qualifiers and the original emitted source are supported.'),'reference':ref,'frozen_primary_status':row['grade']['status'],'frozen_primary_fact_status':row['grade']['fact_status']}) def count(states): n=len(states);p=states.count('pass');u=states.count('unresolved') return {'passed':p,'total':n,'unresolved':u,'lower':p/n if n else None,'upper':(p+u)/n if n else None} for system in ['A','B']: for rep in [1,2,3]: group=[x for x in manual_audit if x['system']==system and x['repetition']==rep]; assert len(group)==40 answerable=[x for x in group if not x['expected_abstention']]; unknown=[x for x in group if x['expected_abstention']] summaries[system+str(rep)]={'attempts':40,'factual_correctness':count([x['fact_status'] for x in answerable]),'supported_answer_success':count([x['supported_answer_status'] for x in answerable]),'correct_abstention':count([x['abstention_status'] for x in unknown]),'requested_claim_citation_support':count([c['status'] for x in group for c in x['citations']]),'original_emitted_source_ranges_grounded':count(['pass' for x in group for c in x['citations']])} a=rows(A/'kingy-common.jsonl');b=rows(A/'lmstudio-common.jsonl'); bm={(x['question_id'],x['repetition']):x for x in b} result={'schemaVersion':'kingy-document-unblinded-source-audit/1.0.0','reviewer':'Codex root in the same Kingy project','reviewedAt':datetime.datetime.now(datetime.timezone.utc).isoformat(),'blinded':False,'independentReview':False,'measurementAlreadyFinished':True,'originalFrozenResultsChanged':False,'qualificationGranted':False,'modelCalls':0,'sameSourceCriteriaBothWorkflows':True,'method':'All 240 original attempts. Exact NFKC non-whitespace character matching across the byte-verified original PDF page sequence resolves native cross-page chunks; preserves complete quote and page-range evidence. Explicit post-output semantic review separately credits supported section headings and full sentences, retains missing facts and one uncertain completed-stage condition. No new primary scorer alias or answer regeneration.','nativeEmittedCitationRanges':native_ranges,'nativeEmittedCrossPageRanges':native_cross,'summaries':summaries,'latency':{'pairedAttempts':120,'KingyReturnedSooner':sum(x['answer_seconds']