# Android manual pairing comparison

Environment: pooled Pixel 9 emulator, Android 16 (API 36), 1080×2424, light appearance. Each commit runs its own real server and Metro bundle from a separate checkout. The native development-client fingerprint is identical on both commits. No production data or model/provider turn is used.

Base: [b68aedfdb7](https://github.com/pingdotgg/t3code/commit/b68aedfdb71534bfdfbe55771ef2e686742faa8e).
Head: [8237015483](https://github.com/pingdotgg/t3code/commit/82370154833883e680185c784511f877334980c6).

For each checkout, install dependencies with `vp i`, prepare/install the Android development client with `vp run mobile:install android`, and start Metro from `apps/mobile` with `CI=1 vp run dev:client --port <metro-port>`. CI mode avoids the VM's file-watcher limit.

Start each backend with a different port and a fresh scratch home:

```
T3CODE_DEV_SEED=0 node apps/server/src/bin.ts --mode web --port <port> --host 0.0.0.0 --base-dir <fresh-home>
```

The server must be reachable from the leased Android device over the tailnet. The comparison used direct HTTP, not Tailscale Serve HTTPS.

Run `open-form.py SESSION METRO_URL` to clear only the disposable mobile client's data, load the intended checkout, and open Add environment. Then run:

```
python pairing-driver.py CHECKOUT FRESH_HOME ADDRESS SESSION OUTPUT_DIR SIDE hyphens
```

Repeat with `spaces` on both commits. The driver issues a fresh 12-character code using that checkout's real `pair` command, enters the server address and the code in three groups of four, dismisses the keyboard, starts recording, and presses the Add environment checkmark. It captures the result twice, at least 500 ms apart, and stops recording. Output directories are timestamped.

Observed: both grouped formats show “The environment credential is invalid.” on base. Both formats pair on head and return to Settings with Environments 1. The selected base and head result screenshot pairs are byte-identical. The hyphenated-code videos show the submission and result on each side. Additional stills show the space-separated-code results.

Addresses and actual one-time codes in shared pairing-form captures are replaced with explicitly labelled redactions by `redact.py`; result messages and navigation are untouched. Videos are limited to the first 20 seconds. Inspect original captures privately and redacted outputs before uploading. Raw captures, logs, and credentials must not be uploaded.

No false timeout was observed over direct HTTP. Tailscale Serve HTTPS and the reported timeout-after-401 behavior are not proved by this comparison.

AI-assisted; verified with the tests and tools listed above.
