"""Reproduce pass 4 from bundled pass 3: python continue_audit_pass4.py [DIRECTORY]."""
import csv,json,sys,hashlib
from pathlib import Path
p=Path(sys.argv[1]) if len(sys.argv)>1 else Path(__file__).parent
source=p/'measurement_audit_pass3.csv'
rows=list(csv.DictReader(source.open(newline='')))
updates={
'H2025_005':dict(endpoint_audited='behavioural_flicker_discrimination',screening_status='hold_exact_value_and_baseline_extraction',numeric_source_status='approximate_39_supported_exact_39.5_unverified',evidence_level='primary_abstract',source_check_url='https://doi.org/10.1017/S0952523807070691',source_check_note='Abstract reports approximately 39 Hz and includes behavioural and ERG experiments plus pharmacological manipulations. Exact compiled 39.5 and its statistic require full text. Do not substitute approximate 39 or pool treated observations.',treatment_status_pass4='baseline_arm_required_not_yet_extracted'),
'H2025_006':dict(endpoint_audited='behavioural_two_alternative_forced_choice_flicker',screening_status='hold_exact_value_and_baseline_extraction',numeric_source_status='baseline_range_33_39_supported_exact_37.5_unverified',evidence_level='primary_abstract',source_check_url='https://doi.org/10.1017/S0952523802196106',source_check_note='Pre-injection thresholds span 33–39 Hz. This does not establish compiled 37.5 as a mean. Separate pre-treatment observations from drug, depletion and recovery arms; obtain exact statistic and conditions.',treatment_status_pass4='pre_injection_range_supported_exact_row_arm_unverified'),
'H2025_008':dict(endpoint_audited='conditioned_respiration_temporal_contrast_sensitivity',screening_status='hold_numeric_extraction_luminance_and_contrast',numeric_source_status='method_supported_24_unverified',evidence_level='primary_abstract',source_check_url='https://pubmed.ncbi.nlm.nih.gov/9474375/',source_check_note='Conditioned respiratory suppression to sinusoidal flicker measures temporal contrast sensitivity over luminance. Abstract does not verify 24 Hz or associated contrast/luminance. Extract conditions and threshold definition before interpreting a cutoff as comparable CFF.'),
'H2025_064':dict(screening_status='hold_compilation_statistic_reconciliation',numeric_source_status='compiled_70_matches_range_low_source_mean_73',statistic_type='compiled_value_matches_range_low_not_group_mean',evidence_level='primary_full_text_table',source_check_url='https://pmc.ncbi.nlm.nih.gov/articles/PMC5679541/',source_check_note='Table 1 green-light result is mean 73 Hz (range 70–74), four pigeons, five runs. Compiled 70 equals range low; its original selection rationale is unknown. Keep 70 in CFF_corrected pending estimand decision; store 73 separately. Do not automatically select the largest protocol result.',source_supported_summary_Hz_pass4='73',source_statistic_pass4='group_mean_green_LED',source_range_Hz_pass4='70–74',sample_size_pass4='4',conditions_pass4='565 nm LED; 3 microW/sr; approximately 2 log cd/m2; equal on/off pulses; 75% correct-choice interpolated threshold; five runs per bird; ambient approximately 300 lux; adult homing stock')}
fields=list(rows[0])+['source_supported_summary_Hz_pass4','source_statistic_pass4','source_range_Hz_pass4','sample_size_pass4','conditions_pass4','treatment_status_pass4','audit_date_pass4']
old={r['record_id']:r.copy() for r in rows}
for r in rows:
 for k in fields:r.setdefault(k,'')
 if r['record_id'] in updates:r.update(updates[r['record_id']],audit_date_pass4='2026-10-24')
def write(name,data):
 with (p/name).open('w',newline='') as f:
  w=csv.DictWriter(f,fieldnames=fields);w.writeheader();w.writerows(data)
write('measurement_audit_pass4.csv',rows)
write('source_decisions_pass4.csv',[r for r in rows if r['record_id'] in updates])
queue=[r for r in rows if r['longevity_years_audited'] and r['anage_quality_audited'] in ['acceptable','high'] and not r['screening_status'].startswith('exclude')]
write('candidate_source_check_queue_pass4.csv',sorted(queue,key=lambda r:(r['method_audited']!='Behavioural',r['Species_phylo'],r['record_id'])))
assert len(rows)==280 and len(old)==280 and set(updates)<=old.keys()
for r in rows:
 for k in ['CFF_original','CFF_corrected','final_analysis_eligible','Species_phylo','longevity_years_audited']:
  assert r[k]==old[r['record_id']][k]
 if r['record_id'] not in updates:
  assert all(r[k]==v for k,v in old[r['record_id']].items())
summary=dict(audit_pass=4,audit_date='2026-10-24',records_reviewed=4,numeric_replacements=0,total_measurements=len(rows),candidate_queue_measurements=len(queue),candidate_queue_taxon_labels=len({r['Species_phylo'] for r in queue}),final_analysis_sample_frozen=False,pass3_input_sha256=hashlib.sha256(source.read_bytes()).hexdigest())
(p/'audit_summary_pass4.json').write_text(json.dumps(summary,indent=2)+'\n')
print(json.dumps(summary,indent=2))
