# Rebuild evidence for PR 27882

Revisions: Before cb9629e6891ef0745772b69f09c9acd0c6a74f7e; After c60e8ba864784e06d9270bd1643b3256682fd0b0. This is evidence tooling, not project code. All paths in these scripts are explicit to the assigned scratch directory and checkouts. Change them together when reproducing elsewhere. Use a fresh scratch root and fresh server homes; never copy a live database. No provider turn is needed.

## Start the actual apps

The after checkout was installed. The before checkout was created with:

```sh
git -C /home/ubuntu/t3code worktree add --detach /home/ubuntu/wt/pr-27882-before cb9629e6891ef0745772b69f09c9acd0c6a74f7e
cd /home/ubuntu/wt/pr-27882-before
vp install
```

Run each command in its own checkout/background session, retaining its session/PID. Redirect stdout/stderr into the indicated scratch file; these startup logs contain pairing credentials and must stay private. The paths below are beneath `/tmp/lead-27882-r1/evidence-capture-1/`.

| Checkout | Command | Private log | Actual API / web ports |
| --- | --- | --- | --- |
| After | `T3CODE_DEV_SEED=0 T3CODE_PORT_OFFSET=5000 vp run dev --home-dir /tmp/lead-27882-r1/evidence-capture-1/after-a` | after-a.log | 18773 / 10733 |
| Before | `T3CODE_DEV_SEED=0 T3CODE_PORT_OFFSET=5010 vp run dev --home-dir /tmp/lead-27882-r1/evidence-capture-1/before-a` | before-a.log | 18783 / 10743 |
| After | `T3CODE_DEV_SEED=0 T3CODE_DEV_ALLOWED_ORIGINS=http://localhost:10733 T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=0 T3CODE_PORT_OFFSET=5020 vp run dev:server --home-dir /tmp/lead-27882-r1/evidence-capture-1/after-b` | after-b.log | 18793 |
| Before | `T3CODE_DEV_SEED=0 T3CODE_DEV_ALLOWED_ORIGINS=http://localhost:10743 T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=0 T3CODE_PORT_OFFSET=5030 vp run dev:server --home-dir /tmp/lead-27882-r1/evidence-capture-1/before-b` | before-b.log | 18803 |

Read the actual dev-runner ports; these exact values were used here. Forge authentication for A was already available on this VM. `seed.mjs` creates two synthetic empty Git repositories and authenticates through each startup pairing credential. A's remote identifies the public pingdotgg/t3code repository; B has no remote and a distinct project identity. Run `node seed.mjs`, then `node clean-bootstrap.mjs` to remove auto-created checkout projects, leaving only the two explicit projects. Never upload the generated cookie or pairing-private files.

## Browser and real fixtures

The shared preview returned ERR_CONNECTION_REFUSED twice during startup. Captures used an agent-browser-owned Chromium via CDP instead. Start a named agent-browser session and open A's web origin. The installed CLI was invoked as `npm exec --yes --package=agent-browser -- agent-browser --session evidence-27882 ...`. `cdp.mjs` uses the debugger port returned by that session (36343 here); update that literal for a new browser. It selects the first page target. Keep one page active while driving it.

For Before, run `node open-app.mjs before`, `node connect-b.mjs before`, then `node rename-b.mjs before`. The last command sets the saved client environment label through the application's actual ConnectionOnboarding.updateBearer method; it does not alter the machine or mock the label map. Wait for B's configuration/projects to load; `node verify-load.mjs` records presentations, distinct repository identities, targets, and result counts. `record-p07.mjs before` verifies B's socket owner's cwd before stopping it, types the new query, refreshes, retries, and excludes B through the Filters > Server controls. Then run `static-real.mjs before` (P01/P02), `record-p08.mjs before`, and `harness.mjs before`.

For After, stop only the tracked original After B runner/session, then restart it with the same home and the explicit `--port 18803` so both sides show the same failed endpoint. Before B must already be stopped. Preserve `T3CODE_DEV_SEED=0`, `T3CODE_DEV_ALLOWED_ORIGINS=http://localhost:10733`, and `T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=0`. Run `open-app.mjs after`, `connect-b.mjs after` while B is still at its initial port if it has not previously been paired, and `rename-b.mjs after` after relocation. The accepted capture used the already paired B profile, updated to 18803 through rename-b. Run `record-p07.mjs after`, `static-real.mjs after`, `record-p08.mjs after`, and `harness.mjs after`. Accepted run paths and inputs are in manifest.json. The final P07 driver waits for Authored grouping before starting; this avoids a transient Others grouping that was rejected during inspection. P01/P02's accepted Before frames came from an earlier equivalent run after B was stopped; the filenames and metadata identify them.

The browser clock is fixed at 2026-11-03T04:00:00Z. Real timers continue running for debounce and network work. For harness page loads the clock is installed before modules mount. For real app captures configure() installs it before the fixture query. Fonts and viewport are shared; reduced motion is emulated through CDP. still() waits fonts/network quiet, blurs focus, and accepts only byte-identical full-viewport PNGs 500ms apart. No crop is used for proof.

## Harness boundary

P03/P04 require arbitrary long labels and synthetic relay wording. P05/P06 require an impossible stable presentation/target label-map mismatch. P09 requires three controlled raw failure causes and order. harness.mjs intercepts two Vite JavaScript responses in the browser: usePullRequestScope supplies deterministic presentations/projects/target arrays, and per-environment list atoms return raw AsyncResult Success/Failure. The real page, route, CSS, createMergedEnvironmentQuery, formatter, and error renderers are untouched. No already formatted merged error or warning DOM is substituted. Each static fixture uses a fresh page mount. P05/P06 also switch the map from absent to empty and assert identical PNG bytes. P09 feeds A/B/C raw atoms in order and records C failure, B whitespace failure, B recovery, then C non-Error failure. The exact synthetic entry/result/labels/messages are in harness.mjs and fixture.json in the manifest.

## Capture and inspection

recorder.mjs uses Page.startScreencast, timestamped JPEG frames and ffmpeg H.264 at 30fps. It does not reload the app or extend a pending RPC. P07/P08/P09 event JSON files give actual interaction times; these vary with real network latency. P08's MutationObserver recorded disabled=false/aria-busy=false -> true/true -> false/false on both revisions. The short busy interval appears in the recording and is not a settled still.

build-diffs.py computes RGB absolute differences. Diff0 is abs(after-before)*16, saturated to 255, threshold zero. DiffT paints magenta where max channel delta >16/255; all other pixels are black. pairs.json records every count and bounding box. Sheets show the whole viewport at a smaller display scale with Before, After, Diff0, DiffT labels. Raw PNGs are full size. review-videos.py creates full-viewport one-second contact sheets used to inspect the movies; these supplement the actual recordings. Every accepted PNG, diff, sheet and recording was inspected; rejected takes remain local and are excluded from the evidence index.

## Cleanup

Stop all tracked dev runner sessions and the named browser. Never kill by process-name/path matching or touch the VM's t3code.service. Remove only the created before worktree with `git -C /home/ubuntu/t3code worktree remove --force /home/ubuntu/wt/pr-27882-before`. Explicit scratch homes keep runtime databases out of the after checkout. Keep raw scratch evidence until uploads are verified.
