#!/usr/bin/env python3
# Drives real app CLI and signals only the native PID captured by the owned launcher.
import argparse,json,os,signal,subprocess,time,pathlib,datetime
p=argparse.ArgumentParser();p.add_argument('phase',choices=['start','stall','status']);p.add_argument('--checkout',required=True);p.add_argument('--artifacts',required=True);p.add_argument('--name',default='Recovery');p.add_argument('--count',type=int,default=3);p.add_argument('--delay-ms',type=int,default=20000);a=p.parse_args()
r=pathlib.Path(a.artifacts);r.mkdir(exist_ok=True,parents=True)
def log(event,**data):
 with (r/'driver.jsonl').open('a') as f:f.write(json.dumps({'at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'event':event,**data})+'\n')
def cli(*args):
 return subprocess.check_output(['node','apps/server/src/bin.ts',*args,'--base-dir','.t3'],cwd=a.checkout,text=True)
if a.phase=='start':
 threads=[]
 for n in range(a.count):
  result=json.loads(cli('thread','start','.t3/recovery-evidence','Use the bash tool to run sleep 240 with timeout 300000 milliseconds. Wait for it to finish, then reply exactly RECOVERY COMPLETE. Do not change files.','--title',f'{a.name} {n+1}','--provider','opencode','--model',os.environ['PROOF_MODEL'],'--json'));threads.append(result['threadId']);log('prompt-submitted',thread=n+1)
 (r/(a.name.lower()+'-threads.json')).write_text(json.dumps(threads))
elif a.phase=='stall':
 owned=json.loads((r/'owned.json').read_text());(r/'control.json').write_text(json.dumps({'delayMs':a.delay_ms}));os.kill(owned['native'],signal.SIGSTOP);log('native-suspended',durationSeconds=70,historyDelayMs=a.delay_ms)
 time.sleep(70);os.kill(owned['native'],signal.SIGCONT);log('native-resumed')
else:
 result=[]
 for n,thread in enumerate(json.loads((r/(a.name.lower()+'-threads.json')).read_text())):
  t=json.loads(cli('thread','show',thread,'--json'));result.append({'thread':n+1,'status':t['thread']['status'],'runCount':t['thread']['runCount'],'messages':[{'role':m['role'],'text':m['text']} for m in t['messages']]})
 log('app-status',threads=result);print(json.dumps(result,indent=2))
