"""Real-server fault-injection proof. Usage: python drive.py CHECKOUT OUTPUT_DIR LABEL SHA PORT.
Requires dependencies installed with vp i. No application source is modified.
The preload injects a reported rejection shape; it does not simulate a network stall.
"""
import subprocess, os, sys, time, pathlib, signal, urllib.request, threading, json
checkout, output, label, sha, port = sys.argv[1:]
out = pathlib.Path(output); out.mkdir(parents=True, exist_ok=True)
start = time.monotonic(); lock = threading.Lock()
transcript = open(out / (label + '.txt'), 'w')
cast = open(out / (label + '.cast'), 'w')
cast.write(json.dumps({'version':2,'width':110,'height':30,'title':label+' — real backend fault injection'})+'\n')
def emit(text):
    with lock:
        transcript.write(text+'\n'); transcript.flush()
        cast.write(json.dumps([round(time.monotonic()-start,3),'o',text+'\r\n'])+'\n'); cast.flush()
        print(text, flush=True)
env = dict(os.environ, T3CODE_DEV_SEED='0', NO_COLOR='1')
command = ['node','--import',str(pathlib.Path(__file__).with_name('inject.mjs')),'apps/server/src/bin.ts','--no-browser','--host','127.0.0.1','--port',port,'--base-dir',str(out / 'fresh-home')]
raw = open(out / 'private-runtime.log','w')
process = subprocess.Popen(command, cwd=checkout, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
def read_output():
    for line in process.stdout:
        raw.write(line); raw.flush()
        if any(word in line for word in ['[proof]', 'AbortError:', 'at Is.onStall', 'Cursor SDK aborted']):
            emit('server: '+line.rstrip())
reader = threading.Thread(target=read_output, daemon=True); reader.start()
url = 'http://127.0.0.1:'+port+'/.well-known/t3/environment'
def probe():
    try:
        with urllib.request.urlopen(url, timeout=2) as response:
            response.read()
            return 'HTTP '+str(response.status)
    except Exception:
        return 'connection unavailable'
try:
    emit(label+' | commit '+sha)
    emit('Environment: Linux; real Node backend; fresh isolated database; no browser or agent sessions.')
    emit('Endpoint: GET /.well-known/t3/environment')
    for _ in range(120):
        status=probe()
        if status == 'HTTP 200': break
        if process.poll() is not None: raise RuntimeError('Server exited before ready')
        time.sleep(1)
    else: raise RuntimeError('Server did not become ready')
    emit('Before injection: '+status)
    emit('Action: SIGUSR2 triggers Promise.reject(DOMException AbortError) with Is.onStall stack.')
    process.send_signal(signal.SIGUSR2)
    for delay in [1,2,3]:
        time.sleep(delay)
        emit('After injection: '+probe()+'; process '+('running' if process.poll() is None else 'exit '+str(process.returncode)))
    emit('Scope: injected rejection only; natural Cursor stall and concurrent provider sessions unverified.')
finally:
    if process.poll() is None:
        process.terminate()
        try: process.wait(timeout=15)
        except subprocess.TimeoutExpired: process.kill(); process.wait()
    reader.join(timeout=3); raw.close(); transcript.close(); cast.close()
