#!/usr/bin/env python3
"""Read only this run's synthetic database and minimum native protocol fields."""
import pathlib,sys,json,sqlite3,platform,glob
r=pathlib.Path(sys.argv[1]);m=json.loads((r/'run.json').read_text());h=pathlib.Path(m['home'])
c=sqlite3.connect('file:'+str(h/'userdata/statev2.sqlite')+'?mode=ro',uri=True);c.row_factory=sqlite3.Row
threads=[json.loads(x['payload_json']) for x in c.execute('select payload_json from orchestration_v2_projection_threads')]
projects=[dict(x) for x in c.execute('select project_id,title,workspace_root from projection_projects where deleted_at is null')]
protocol=[]
native_ids=set()
for p in (h/'userdata/logs/provider').glob('events.*.log'):
 for line in p.read_text().splitlines():
  if '] NTIVE: ' not in line:continue
  v=json.loads(line.split('] NTIVE: ',1)[1]);payload=v.get('event',{}).get('payload',{});method=payload.get('method')
  if method=='turn/start':
   params=payload.get('params',{});native_ids.add(params.get('threadId'));protocol.append({'time':line.split(']')[0][1:],'method':method,'cwd':params.get('cwd'),'model':params.get('model'),'sandboxPolicy':params.get('sandboxPolicy'),'approvalPolicy':params.get('approvalPolicy'),'approvalsReviewer':params.get('approvalsReviewer')})
  if method in ['item/started','item/completed','item/commandExecution/outputDelta','thread/settings/updated']:
   params=payload.get('params',{});item=params.get('item',{})
   if item.get('type')=='commandExecution':protocol.append({'time':line.split(']')[0][1:],'method':method,'item':item})
   if method=='thread/settings/updated':protocol.append({'method':method,'sandboxPolicy':params.get('threadSettings',{}).get('sandboxPolicy')})
assertions=[]
for t in threads:
 p=next(p for p in projects if p['project_id']==t['projectId']);w=pathlib.Path(t['worktreePath'])
 assertions.append({'threadId':t['id'],'projectId':t['projectId'],'model':t['modelSelection'],'runtimeMode':t['runtimeMode'],'worktreePath':str(w),'realPath':str(w.resolve()),'isCanonical':str(w)==str(w.resolve()),'parentIsPhysicalScratch':w.parent== (h/'scratch').resolve(),'projectWorkspaceRoot':p['workspace_root'],'scratchWorkspaceRoot':str(h/'scratch'),'projectIdentityPreserved':p['workspace_root']==str(h/'scratch'),'markerExists':(w/'made-by-codex').exists()})
items=[json.loads(x[0]) for x in c.execute("select payload_json from orchestration_v2_projection_turn_items where type in ('command_execution','assistant_message')")]
native_tools=[]
for nid in native_ids:
 if not nid:continue
 for p in pathlib.Path('/home/ubuntu/.codex/sessions/2026/10/27').glob('*'+nid+'.jsonl'):
  for line in p.read_text().splitlines():
   v=json.loads(line);item=v.get('payload',{})
   if v.get('type')=='response_item' and item.get('type') in ['custom_tool_call','custom_tool_call_output','function_call','function_call_output']:
    native_tools.append({'timestamp':v.get('timestamp'),'payload':{k:x for k,x in item.items() if k in ['type','name','input','output','arguments','call_id']}})
wire=json.loads((r/'wire-scratch.json').read_text()) if (r/'wire-scratch.json').exists() else None
if wire:
 for a in assertions:
  a['scratchWorkspaceRoot']=wire['scratchWorkspaceRoot'];a['advertisedScratchIdentityPreserved']=a['projectWorkspaceRoot']==wire['scratchWorkspaceRoot']
out={**m,'advertisedScratch':wire,'nativeToolTranscript':native_tools,'os':platform.platform(),'codex':json.loads((h/'caches/codex.json').read_text()).get('version'),'browser':json.loads((r/'browser.json').read_text()),'symlink':{'source':str(h/'scratch'),'target':str((h/'scratch').resolve()),'isSymlink':(h/'scratch').is_symlink()},'projects':projects,'assertions':assertions,'protocol':protocol,'items':items}
(r/'manifest.json').write_text(json.dumps(out,indent=2));print(json.dumps({'row':m['row'],'assertions':assertions,'commandItems':len([i for i in items if i['type']=='command_execution'])},indent=2))
