# Reproduce the terminal fallback captures

Evidence-only capture for PR [#24640](https://git.cafe/pingdotgg/t3code/pull/24640).

Before [e9236274a3](https://git.cafe/pingdotgg/t3code/commit/e9236274a3a6e0c731a7df4092db1e686f039068); after [63e44670dd](https://git.cafe/pingdotgg/t3code/commit/63e44670dd1d222cf63619d5e02415f2a9fd3bc2).

Environment: fs-31 Linux x86_64, Node v26.12.1, Python 3, real checkout apps/server, private mount namespace interception of /usr/bin/ps (also /bin/ps via symlink). No browser/client, viewport, theme, DPR, or font scaling applies. No user database was copied. Every real-server run creates a new home/database using T3CODE_DEV_SEED=0. All terminal commands and IDs are synthetic.

The exact scripts used by each pair are embedded in their transcripts and included by run directory in capture-scripts.tar.gz. The standalone latest driver also supports every fixture. Recreate the paths below, or consistently adjust the ROOT and checkout constants in the scripts.

1. Install dependencies at the two pinned checkouts with `vp install`. The after checkout is `/home/ubuntu/wt/pr-24640`; before is `/home/ubuntu/wt/pr-24640-before`.
2. Put the capture scripts in `/tmp/lead-24640-r1/capture/`. Copy the host ps before any namespace binding: `cp /usr/bin/ps /tmp/lead-24640-r1/capture/real-ps`. Make ps-shim.py, native-fail.py and native-proxy.py executable. Nothing modifies the host ps; `sudo unshare -m --propagation private` limits the bind mount to the captured server.
3. For native success/recovery rows, run `bash /tmp/lead-24640-r1/capture/build-native.sh`. It installs isolated Rust 1.99.1 and builds each checkout's real sidecar under the capture directory. It does not edit PATH or either checkout.
4. Run one row with `node /tmp/lead-24640-r1/capture/driver.mjs <before|after> <row> <delay-ms> <terminal-count> <query-count> <mode>`. The driver prints a new timestamped run directory. It generates a random dev credential internally, exchanges it for a WebSocket ticket, and never logs the credential/ticket. It creates the project/threads via shipped RPC and subscribes before opening terminals. It captures events and metadata, obtains final snapshots, closes terminals and stops only recorded owned PIDs.
5. Run labelled harnesses with `node /tmp/lead-24640-r1/capture/harness.mjs <before|after> <H01|H02|H03|H04>`. They use the real makeWithOptions polling loop with controlled runner, fake PTY and TestClock. They run no real server or RPC and are not counted as real-server proof.
6. `report.py` creates paired transcripts and comparison sheets from complete runs with preserved script snapshots. It fails if a required Linux row has no complete reproducible capture. `--partial` is only for work-in-progress inspection and prints missing rows explicitly.

| Row | Arguments after side |
|---|---|
| R01 | R01 0 1 1 valid |
| R02 | R02 0 1 3 valid |
| R03 | R03 0 20 3 valid |
| R04 | R04 750 1 2 valid |
| R05 | R05 5000 1 2 valid |
| R06 | R06 5000 20 2 valid |
| R07 | R07 14000 1 2 valid |
| R08 | R08 0 1 2 hang |
| R09 | R09 0 1 8 fail |
| R10 | R10 5000 1 2 fail |
| R11 | R11 0 1 2 truncate |
| R12 | R12 0 1 2 spawnfail |
| R13 | R13 0 1 1 valid |
| R14 | R14 0 1 1 valid, plus R14F 0 1 3 valid |
| R15 | R15 5000 2 2 valid |
| R16 | R16 5000 1 2 valid |
| R17 | R17 0 1 1 valid |
| R18 | R18 0 1 1 valid |
| R19 | R19 0 1 1 valid |
| R20 | R20 0 1 1 hang |
| R21 | R21 5000 1 1 valid |
| R22 | R22 500 1 1 valid |

R06 has a labelled failed-query seeding phase so all 20 live children precede the measured shared-slow phase. R09 switches from the failing native helper to a transparent proxy of the real compiled sidecar using server.retryResourceTelemetry, then stops only the proxy PID it logged to observe reset. R14 uses a transparent native protocol logger (response payloads are forwarded without alteration and never dumped); R14F supplies the separate idle-fallback contrast. R12 uses a Node preload only for the exact ps invocation, forcing actual spawn ENOENT. Other ps queries are mounted-script real subprocesses with normal ProcessRunner timeout/cancellation.

Query start/end/signal timestamps come from the shim; complete fallback acquisition timings and ownership come from application trace spans. Python startup, process exit and stream-drain overhead explains why the shim lifetime is slightly below the nominal timeout. Delay predictions use acquisition duration, not shim lifetime. RPC streams may batch/chunk events, and both the global and terminal-specific stream are retained raw; comparison sheets deduplicate logical terminal events by thread/terminal/type/sequence.

Observation milestones are matched across revisions, rather than requiring identical wall-clock duration after the PR changes timeouts and pacing. R03's initial candidate set can differ while 20 sessions are opening, followed by shared snapshots of all 20. R20 before includes an additional background query crossing the capture boundary while the driver waits for the flushed cleanup span. These setup/window differences are explicit in the sheets; they are not additional product behavior changes.

W01-W05 remain cannot-verify windows: no real Windows host was used. Linux POSIX results and harnesses do not claim Windows or native macOS/client proof.
