"""Reproduce the frozen v1 coding crosswalk. Stdlib only; no fetch or recoding.
Usage: python3 reproduce-story-structure-codebook-2026-10-30.py DATA_DIRECTORY
Prints a separate audit JSON; original files are never overwritten.
"""
import collections,hashlib,json,pathlib,sys
SOURCES={
 'short-form-hook-corpus-2026.json':'d854e2fec64db8a8ae1170a21bde3fcea58b1f73f8ffc6eafdca9ce83dc3345c',
 'storytelling-corpus-2026.json':'d386334259a103c84a350081825bc9f2c774553b3fda1295c29aa992caa4b3d1'}
def audit(root):
 datasets=[]
 for name,digest in SOURCES.items():
  raw=(pathlib.Path(root)/name).read_bytes()
  if hashlib.sha256(raw).hexdigest()!=digest:raise ValueError('Source edition mismatch: '+name)
  datasets.append(json.loads(raw))
 hook,story=datasets;left={r['researchId']:r for r in hook['records']};right={r['researchId']:r for r in story['records']}
 if len(left)!=52 or len(right)!=52 or len(hook['records'])!=52 or len(story['records'])!=52 or left.keys()!=right.keys():raise ValueError('Expected exact one-to-one 52-record researchId join')
 counts=collections.Counter()
 for rid,h in left.items():
  s=right[rid]
  if h['sourceVideoId']!=s['sourceVideoId'] or h['storyDevelopmentStructure']!=s['storyStructureId']:raise ValueError('Identity or stored structure mismatch: '+rid)
  counts[h['primaryHookType'],s['storyStructureId']]+=1
 if len(counts)!=8 or len({x[0]for x in counts})!=8 or len({x[1]for x in counts})!=8:raise ValueError('Stored labels no longer form the audited eight one-to-one groups')
 rows=[]
 for st in story['structures']:
  examples=[r['storytellingId']for r in story['records']if r['storyStructureId']==st['id']and r['detailedPublicExample']]
  pairs=[(h,n)for(h,s),n in counts.items()if s==st['id']]
  if len(pairs)!=1 or len(examples)!=2 or pairs[0][1]!=st['count']:raise ValueError('Codebook/count/example mismatch')
  rows.append(dict(structure_id=st['id'],label=st['label'],definition=st['definition'],include=st['include'],exclude=st['exclude'],limitation=st['limitation'],opening_type=pairs[0][0],editions=pairs[0][1],example_ids=examples))
 return dict(audit_id='JLI-STORY-CODEBOOK-AUDIT-2026-10-30-v1',audit_date='2026-10-30',source_observation_date='2026-09-20',sources=[dict(filename=n,sha256=d,dataset_id=x['datasetId'],version=x['version'])for(n,d),x in zip(SOURCES.items(),datasets)],join_key='researchId',denominator=52,unmatched_records=0,missing_join_fields=0,rows=rows,interpretation='The eight stored primary opening groups and eight whole-story structure groups partition these same 52 editions identically. Their matching distributions are not independent evidence of an opening-to-structure association.',limitations=['Describes stored labels in one selected single-creator corpus; not a statewide, national or platform-wide estimate.','No new video viewing or recoding; coding procedure, coder count, tie breaking and inter-rater reliability are not established by this audit.','Does not validate every assignment or establish causation, performance, participant outcomes or industry-wide storytelling rules.','Original editions remain immutable, including their separately documented timestamp erratum.'])
if __name__=='__main__':
 if len(sys.argv)!=2:raise SystemExit('Usage: python3 '+pathlib.Path(__file__).name+' DATA_DIRECTORY')
 print(json.dumps(audit(sys.argv[1]),ensure_ascii=False,indent=2))
