# Rebuilding the A/B fixtures

The scripts are scratch drivers, not production source changes. The published capture steps JSON is authoritative for each original take. Existing recordings were produced before the environment-id lookup was folded into `start-side.py`; that lookup originally ran as a separate `server.getConfig` RPC. The updated driver automates the same lookup for a fresh reproduction. Adapt the fixed VM IP, scratch root, worktree paths and device sessions if rebuilding elsewhere. Never point a server at live userdata.

1. Create the two detached worktrees at the exact before/after SHAs stated in evidence.md. In each run `vp install`, then `vp run mobile:install android --prepare`. The shared native fingerprint is stated in evidence.md; all JS comes from that side's own Metro checkout.
2. Download all scripts into a fresh scratch directory. The scripts default to this run's scratch path; use `CAPTURE_ROOT` for `start-side.py`, `rpc.mjs` and `device.py`, or update `ROOT=Path(__file__).parent` scripts together. Run `python3 start-side.py before`, then `python3 start-side.py after`. The explicit fresh home, `T3CODE_DEV_SEED=0`, ports and private local auth files are created by the driver. The final environment manifests retain only public environment capabilities.
3. Run `node configure-provider.mjs` to disable development replay on both servers. Actual Codex authentication must already work on the host. No credentials are included in the uploads.
4. On each side create the first project/thread with `python3 seed-active.py SIDE A-dark` **without** `--existing-project`. This creates the one synthetic workspace/project and starts a real sleep turn. `prepare-row.py` assumes that project already exists; its later new-thread calls use `--existing-project` automatically. For the pre-created first thread, pass `--existing-thread` to avoid duplicate creation.
5. Lease Pixel 9 through the device-pool skill. Connect/open it with a `pr29263-2a-*` session from the **before worktree cwd**, and use that cwd/session for every subsequent device command and native installation. Set `DEVICE_SESSION` for Python drivers. Install with `vp run mobile:install android --session SESSION --metro http://100.111.150.56:8091`. Run `python3 metro.py before`, then `python3 reset-client.py before`; the latter waits for initial JS download, pairs to that isolated server, and disables Android animation scales. The Expo launcher URL disables its floating tools button.
6. `python3 prepare-row.py before dark A A-dark --existing-thread` sets explicit T3 Code Dark, 16 pt, opens the active thread, focuses the composer and types the A draft. Run `python3 capture-ab.py before dark A`. It records first open, outside dismiss, reopen, two Android Back presses (IME then menu), refocus/reopen, and non-default Queue selection. Then `python3 stop-row.py before A-dark`.
7. `python3 prepare-row.py before light A A-light` creates the next thread in the same project and sets explicit Light. Capture/stop it in the same way. Selected run names in paired-runs.json identify retries; all rejected/control takes remain in the diagnostic archive. For the original A Light retake, key was `A-light-retake`.
8. Switch Metro to `after`, reset/re-pair the native client to `after`, and repeat A Dark/Light. Before and after use distinct fresh databases at their own migration levels. Stop each synthetic turn after capture. Close the Pixel lease.
9. Lease **Small Phone** (360 dp, Android API 36) from the pool in a different `pr29263-2a-*` session; install using the same cached native client and owning cwd. Reset/re-pair to each side after switching its Metro. Run `prepare-row.py SIDE THEME B KEY` once to set 22 pt; subsequent themes can use `--reuse-size`. The driver explicitly sets follow-up default Queue and types the three-line draft. `capture-ab.py SIDE THEME B` records five opens, including an IME hide/show before opening 5, then selects non-default Steer now. Its latest version awaits the visible final draft text before the final outcome still. For each new key, stop with `stop-row.py SIDE KEY`.
10. Update paired-runs.json to the selected timestamped take paths. Run `audit-settle.py`, `build-sheets.py`, and `review-recordings.py RUN...`. Inspect the originals, difference panels and original-speed recordings. `audit-settle.py` selects an unmodified frame from the least-changing first-open screenshot pair; it does not erase pixels. All raw pairs and residual bounds/counts remain.
11. `package-provenance.py` removes pairing invocations and gateway lease identifiers from public logs, packages raw captures, and computes hashes. `publish.py` uses `ghdrop upload -m` and saves its verbatim output plus upload-map.json. `write-report.py` writes the PR-body fragment and artifact index. Review those files before uploading them.
12. Stop synthetic turns and tracked process groups with `cleanup-runtime.py`; close every device session; remove only these two worktrees. Keep evidence/media/logs, and remove fresh server homes, synthetic workspaces and private credential files. The original capture completed with clean checkouts and all three own ports closed.

The latest capture driver adds the longer B message-readiness check used in the accepted Light retake. Earlier A runs used outside-dismiss coordinate (80,450); the latest driver uses (4,350), outside the narrow clamped menu as well. These differences are preserved in each original steps.json.

Strict frozen-clock/static-feed proof remains unverified. Do not mistake status-bar normalization for freezing the application clock. A deterministic production-component harness would be needed to close that static-proof gap without relying on live provider content.
