"""Offline handover consistency only. Never authorises execution or promotion."""
from pathlib import Path
from datetime import datetime
import json,hashlib,math,sys
ROOT=Path(__file__).resolve().parent
FILES=('handover.json','human-gate-observation.json','existing-profile-pin.json','compute-boundary.json','dataset-split-observation.json','source-references.json')
def parse(raw):
 if not isinstance(raw,bytes)or len(raw)>1000000:raise ValueError('SIZE_OR_TYPE')
 def pairs(items):
  obj={}
  for k,v in items:
   if k in obj:raise ValueError('DUPLICATE_KEY')
   obj[k]=v
  return obj
 def number(x):
  v=float(x)
  if not math.isfinite(v):raise ValueError('NONFINITE')
  return v
 return json.loads(raw.decode('utf-8'),object_pairs_hook=pairs,parse_float=number,parse_constant=lambda _:(_ for _ in ()).throw(ValueError('NONFINITE')))
def validate(plan,evidence):
 if not isinstance(plan,dict)or plan.get('schema')!='csoai.meok.completion-handover/1':raise ValueError('PLAN_SCHEMA')
 for flag in ['execution_authority','gspc_admitted','automatic_promotion','scheduled','new_paid_compute_authorised']:
  if plan.get(flag)is not False:raise ValueError('AUTHORITY_OR_SCHEDULE_ASSERTION:'+flag)
 if plan.get('pilot_status')!='HELD_PENDING_OWNER_AND_HUMAN_ACCEPTANCE'or plan.get('production_status')!='NOT_ESTABLISHED':raise ValueError('UNSUPPORTED_READINESS_ASSERTION')
 if plan.get('research_status')!='EXISTING_RESEARCH_EVIDENCE_RECORDED':raise ValueError('RESEARCH_SCOPE')
 stamp=datetime.fromisoformat(plan['created_at'])
 if stamp.tzinfo is None:raise ValueError('TIMESTAMP_TIMEZONE')
 gates=plan.get('gate_order');seen=set()
 if not isinstance(gates,list)or len(gates)!=9:raise ValueError('GATE_COUNT')
 allowed={'HELD_FOR_OWNER_DECISION','PARTIAL_RESEARCH_ONLY','NOT_ESTABLISHED','UNDER_ACTIVE_EVALUATION','OWNER_ACCEPTANCE_REQUIRED','NOT_EVIDENCED'}
 for index,g in enumerate(gates):
  if g.get('id')!='G'+str(index)or g['id']in seen:raise ValueError('GATE_ID_OR_ORDER')
  deps=g.get('depends_on')
  if not isinstance(deps,list)or len(deps)!=len(set(deps))or not set(deps)<=seen:raise ValueError('CYCLE_OR_UNKNOWN_DEPENDENCY')
  if g.get('state')not in allowed:raise ValueError('UNSUPPORTED_COMPLETION_STATE')
  if not all(isinstance(g.get(k),str)and g[k] for k in ['title','owner','next_action']):raise ValueError('MISSING_HANDOVER')
  if not isinstance(g.get('acceptance'),list)or not g['acceptance']or not all(isinstance(x,str)and x for x in g['acceptance']):raise ValueError('MISSING_ACCEPTANCE')
  if not isinstance(g.get('evidence'),list)or not g['evidence']or any(n not in evidence for n in g['evidence']):raise ValueError('UNRESOLVED_EVIDENCE')
  seen.add(g['id'])
 cost=evidence['compute-boundary.json']
 if cost.get('programme_never_used_paid_compute')is not False or cost.get('programme_total_cost')!='NOT_ESTABLISHED':raise ValueError('UNSUPPORTED_ZERO_SPEND_CLAIM')
 if cost['this_pass']['jobs_launched']!=0 or cost['this_pass']['new_paid_compute_authorised']is not False:raise ValueError('COMPUTE_BOUNDARY_CHANGED')
 return {'status':'HANDOVER_CONSISTENT','gate_count':9,'next_gate_ids':[g['id']for g in gates if not g['depends_on']],'human_identity_gate':'G2','real_approval_effect_gate':'G3','production_ready':False,'pilot_approved':False,'execution_authority':False,'gspc_admitted':False,'scope':'Document structure and declared evidence binding only, not independent source truth or permission.'}
def main():
 if len(sys.argv)!=1:raise ValueError('No caller paths or remote URLs accepted')
 manifest=parse((ROOT/'HANDOVER_FILES.json').read_bytes());expected=manifest['files_sha256']
 if set(expected)!=set(FILES):raise ValueError('FIXED_FILE_SET')
 evidence={}
 for name in FILES:
  file=ROOT/name
  if file.is_symlink()or not file.is_file()or file.stat().st_size>1000000:raise ValueError('SOURCE_FILE_TYPE')
  raw=file.read_bytes()
  if hashlib.sha256(raw).hexdigest()!=expected[name]:raise ValueError('SOURCE_BYTES_CHANGED:'+name)
  evidence[name]=parse(raw)
 report=validate(evidence['handover.json'],evidence);print(json.dumps(report,indent=2));return report
if __name__=='__main__':
 try:main()
 except (ValueError,KeyError,TypeError,OSError,RecursionError)as e:
  print(json.dumps({'status':'REFUSED','reason':str(e)[:160],'execution_authority':False}));sys.exit(2)
