"""Live server/MCP reproduction. Run with CHECKOUT OUTPUT_DIR PROOF_MODEL in environment.
Copy proof-mcp.ts into CHECKOUT/apps/server before running. No test doubles.
"""
import os,sys,json,subprocess,pathlib,sqlite3,time
w=pathlib.Path(os.environ['CHECKOUT']); out=pathlib.Path(os.environ['OUTPUT_DIR']);out.mkdir(parents=True,exist_ok=True)
log=[]
def say(s):
 print(s,flush=True);log.append(s);(out/'transcript.txt').write_text('\n'.join(log)+'\n')
def call(tool,args):
 r=subprocess.run(['node',str(w/'apps/server/proof-mcp.ts'),str(w/'.t3'),tool,json.dumps(args)],cwd=w,capture_output=True,text=True)
 raw=json.loads(r.stdout);(out/(str(len(list(out.glob('*.json'))))+'-'+tool+'.json')).write_text(r.stdout)
 if raw.get('isError'): raise RuntimeError('MCP tool error: '+tool)
 return raw['structuredContent']
sha=subprocess.check_output(['git','rev-parse','HEAD'],cwd=w,text=True).strip()
say('LIVE T3 SERVER / MCP REPRODUCTION  |  commit '+sha[:10])
say('Fresh dev database; synthetic repository; full-access MCP client.')
sample=out/'sample';sample.mkdir()
for args in [['init','-q'],['-c','user.name=Proof','-c','user.email=proof@example.invalid','commit','--allow-empty','-qm','Initial commit']]:subprocess.run(['git','-C',str(sample),*args],check=True,stdout=subprocess.DEVNULL)
p=call('t3_project_create',{'title':'Background schedule proof','workspaceRoot':str(sample)})
call('t3_project_update',{'projectId':p['id'],'defaultModelSelection':{'instanceId':'claudeAgent','model':os.environ['PROOF_MODEL']}})
task=call('schedule_task',{'projectId':p['id'],'title':'Fresh session background work','prompt':'Use the Bash tool with run_in_background set to true to run the command: sleep 900. Do not wait for it. Then reply with the single word: started','schedule':{'type':'interval','everyMs':900000},'freshSession':True,'bindToCurrentThread':False})
say('schedule_task: interval=900000ms, freshSession=true, bindToCurrentThread=false')
say('Prompt: background Bash sleep 900; do not wait; reply "started".')
thread=None;original={}; original_session=None
conn=sqlite3.connect('file:'+str(w/'.t3/userdata/statev2.sqlite')+'?mode=ro',uri=True)
def sleeps(cwd):
 found={}
 for d in pathlib.Path('/proc').iterdir():
  if not d.name.isdigit():continue
  try:
   cmd=(d/'cmdline').read_bytes().rstrip(b'\0').split(b'\0')
   if cmd not in [[b'sleep',b'900'],[b'/usr/bin/sleep',b'900']]:continue
   if str((d/'cwd').resolve())!=cwd:continue
   found[int(d.name)]=(d/'stat').read_text().split()[21]
  except (OSError,IndexError):pass
 return found
def original_alive():
 return bool(original) and all(pathlib.Path('/proc/'+str(pid)+'/stat').exists() and pathlib.Path('/proc/'+str(pid)+'/stat').read_text().split()[21]==tick for pid,tick in original.items())
results=[]
for fire in range(1,5):
 if fire==4:
  say('t3_thread_interrupt(stop=true)')
  call('t3_thread_interrupt',{'threadId':thread,'stop':True})
 say('\n> run_scheduled_task_now  fire '+str(fire))
 result=call('run_scheduled_task_now',{'taskId':task['scheduledTaskId']});results.append(result)
 say('  lastRunStatus='+result['lastRunStatus']+'; threadId='+('null' if result['threadId'] is None else 'present')+'; dispatch count='+str(result['runCount']))
 if result['threadId']:
  thread=result['threadId']
  while True:
   waited=call('t3_thread_wait',{'threadId':thread,'timeoutMs':15000})
   if not waited['timedOut']:break
   say('  foreground run still executing...')
  read=call('t3_thread_read',{'threadId':thread})
  latest=read['recentRuns'][0]
  texts=[i['text'] for i in read['items'] if i['type']=='assistant_message' and i['runId']==latest['runId']]
  say('  foreground run '+str(latest['ordinal'])+': '+latest['status']+'; reply='+json.dumps(texts[-1] if texts else None))
  row=conn.execute('select p.provider_session_id,p.status from orchestration_v2_projection_runs r join orchestration_v2_projection_provider_threads p on p.provider_thread_id=r.provider_thread_id where r.run_id=?',(latest['runId'],)).fetchone()
  if fire==1:
   original=sleeps(read['thread']['worktreePath']);original_session=row[0] if row else None
   say('  original background sleep processes alive: '+str(len(original)))
  else:say('  fresh provider session: '+str(bool(row and row[0]!=original_session)).lower())
 else:
  read=call('t3_thread_read',{'threadId':thread});say('  persisted foreground run count: '+str(read['thread']['runCount']))
 if fire<4:say('  original background sleep still alive: '+str(original_alive()).lower())
 if fire==1 and (latest['status']!='completed' or not original):
  raise RuntimeError('Prerequisite not established: foreground complete with live background command')
 if fire<4:
  row=conn.execute('select status from orchestration_v2_projection_provider_sessions where provider_session_id=?',(original_session,)).fetchone()
  say('  original provider session status: '+str(row[0] if row else None))
call('schedule_task',{'scheduledTaskId':task['scheduledTaskId'],'enabled':False})
say('\nSchedule paused. Reproduction complete.')
(out/'summary.json').write_text(json.dumps({'commit':sha,'fires':results,'originalSleepPids':list(original),'threadId':thread},indent=2))
(out/'done').write_text('done')
time.sleep(8)
