from pathlib import Path
source=Path('/tmp/lead-25978-r2/evidence-capture/capture-core.py').read_text().split('def run():')[0].replace('evidence-thread','turn-thread').replace("SIDE+'-core-'","SIDE+'-lifecycle-'")
exec(source)
def begin_video():
 main('process.env.CAP_LOG='+json.dumps(str(RUN/'events.jsonl'))+';process.env.CAP_REV='+json.dumps(REV)+';globalThis.capRow="setup";')
 main((ROOT/'observe.js').read_text())
 display=main('return process.env.DISPLAY;');env={**os.environ,'DISPLAY':display,'XAUTHORITY':main('return process.env.XAUTHORITY;')}
 v=subprocess.Popen(['ffmpeg','-y','-f','x11grab','-framerate','10','-video_size','1440x1000','-i',display+'+0,0','-an','-c:v','libx264','-preset','ultrafast','-crf','25','-pix_fmt','yuv420p',str(RUN/(SIDE+'-lifecycle.mp4'))],stdout=(RUN/'ffmpeg.log').open('w'),stderr=subprocess.STDOUT,env=env)
 (RUN/'ffmpeg.pid').write_text(str(v.pid));return v

def reopen():
 key=main('return [...capHost.tabs.values()].find(t=>t.key.threadId==="turn-thread").key;')
 server('const r=await c.runOperation({requestId:"reconnect-"+Date.now(),threadId:"turn-thread",tabId:'+json.dumps(key['tabId'])+',tabIdExplicit:true,agentSessionId:"fixture-agent",operation:"open",input:{open:false,reuseExistingTab:true},timeoutMs:15000},Date.now()+15000);return {tabId:r.tabId};')

def run_lifecycle():
 v=begin_video()
 try:
  mark('T','gated host-command harness; real provider Stop attempt retained separately')
  main('const t=[...capHost.tabs.values()].find(t=>t.key.threadId==="turn-thread");return await capHost.command({type:"turn",threadId:t.key.threadId,active:true});')
  hide();i=len(events());f=EXEC.submit(snapshot)
  wait_event(lambda e:e['event']=='native_send' and e.get('method')=='Page.captureScreenshot',start=i)
  wait_event(lambda e:e['event']=='B' and e.get('B') and e.get('pending')==1,start=i)
  note('host_harness_turn_end_requested')
  main('return await capHost.command({type:"turn",threadId:"turn-thread",active:false});')
  wait_event(lambda e:e['event']=='native_resolve' and e.get('method')=='Emulation.setFocusEmulationEnabled' and e.get('F') is False,timeout=20,start=i)
  time.sleep(.6);finish_pending(f,i)
  mark('R1','gated real-app: scratch ServerBrowser.dropTab(closeSession=false)')
  hide();i=len(events());f=EXEC.submit(snapshot)
  wait_event(lambda e:e['event']=='native_send' and e.get('method')=='Page.captureScreenshot',start=i)
  wait_event(lambda e:e['event']=='B' and e.get('B') and e.get('pending')==1,start=i)
  note('endpoint_scope_close_requested')
  server('const t=[...c.tabs.values()].find(t=>t.threadId==="turn-thread");c.dropTab(t,false);return {closed:true,tabId:t.tabId};')
  wait_event(lambda e:e['event']=='host_received' and e.get('type')=='release',start=i)
  wait_event(lambda e:e['event']=='native_resolve' and e.get('method')=='Emulation.setFocusEmulationEnabled' and e.get('F') is False,timeout=20,start=i)
  time.sleep(.6);finish_pending(f,i)
  reopen()
  mark('D','gated real-app: read-only viewer, natural settled JPEG, close owning socket')
  main((ROOT/'viewer-open.js').read_text().replace('evidence-thread','turn-thread'))
  time.sleep(.6)
  i=len(events());main((ROOT/'motion-and-hide.js').read_text())
  wait_event(lambda e:e['event']=='native_send' and e.get('method')=='Page.captureScreenshot' and e.get('format')=='jpeg',timeout=8,start=i)
  wait_event(lambda e:e['event']=='B' and e.get('B') and e.get('pending')==1,start=i)
  note('owning_viewer_close_requested');main('capViewer.close();')
  wait_event(lambda e:e['event']=='host_received' and e.get('method')=='Target.detachFromTarget',timeout=15,start=i)
  wait_event(lambda e:e['event']=='native_resolve' and e.get('method')=='Emulation.setFocusEmulationEnabled' and e.get('F') is False,timeout=20,start=i)
  time.sleep(.6);restore()
  if SIDE=='before':time.sleep(2);note('bounded_stall_end');rescue()
  else:wait_event(lambda e:e['event']=='native_resolve' and e.get('method')=='Page.captureScreenshot',start=i);idle()
  note('row_end')
 except Exception as e:
  (RUN/'driver-error.txt').write_text(repr(e));print('FAILED',repr(e),flush=True)
  try:restore()
  except:pass
 finally:
  v.send_signal(2)
  try:v.wait(timeout=15)
  except subprocess.TimeoutExpired:v.kill();v.wait()
  for row in ['T','R1','D']:(RUN/(SIDE+'-'+row+'.jsonl')).write_text(''.join(json.dumps(e)+'\n' for e in events() if e.get('row')==row))
  print('RUN',RUN,flush=True)
run_lifecycle()
