#!/usr/bin/env python3
"""Reproduction recipe for the manual proof (not a scenario-runner test).
Run from each installed checkout with its dev server running on fresh .t3 state.
Configure Codex binaryPath to codex-wrapper beside this file before starting.
The wrapper suspends app-server launches only while the suspend file exists.
Drive the browser to each printed thread link and record the full interaction.
"""
import argparse,json,pathlib,subprocess,time,os,signal
p=argparse.ArgumentParser();p.add_argument("workspace");p.add_argument("--base-dir",default=".t3");p.add_argument("--control",action="store_true");a=p.parse_args()
root=pathlib.Path(__file__).parent
out=root/time.strftime("run-%Y%m%d-%H%M%S");out.mkdir()
def cli(*args):
 r=subprocess.run(["node","apps/server/src/bin.ts",*args,"--base-dir",a.base_dir],capture_output=True,text=True,check=True)
 print(time.strftime("%H:%M:%S"),r.stdout,flush=True);return r.stdout
(root/"suspend").touch()
cli("project","add",a.workspace,"--title","Initialize recovery")
threads=[]
def start(title):
 t=json.loads(cli("thread","start",a.workspace,"Reply with the single word OK","--title",title,"--provider","codex","--model","gpt-6-luna","--json"))["threadId"];threads.append(t)
 (out/"threads.json").write_text(json.dumps(threads));return t
start("1 · Hung initialize")
time.sleep(16)
start("2 · Waiting behind hung server")
time.sleep(85)
(root/"suspend").unlink()
start("3 · Healthy future launch")
time.sleep(190)
for i,t in enumerate(threads):
 (out/f"thread-{i+1}.json").write_text(cli("thread","show",t,"--json"))
if a.control:
 for pid in (root/"suspended-pids").read_text().splitlines():
  try: os.kill(int(pid),signal.SIGCONT)
  except ProcessLookupError: pass
