# Round 2 evidence reproduction

Before is 701e13cebb040d8433cd9aefb803ddd0d58c41d6; after is 37441e905194b062345c76d81caacd00d77ef5e5. The original round-1 scripts archive and runbook reproduce retained W1/W2/W4/W7/W8/W9/D1/H1/H2/H4 evidence. Round 2 adds W3 recordings and completes W5/W6/W10/H3. All state is synthetic; no live T3 home was used. No production sources were edited.

## Isolated servers and fixture

Create the before worktree next to the after worktree and install with global `vp install`. For each side run `T3CODE_DEV_SEED=0 T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=0 NODE_OPTIONS='--import /tmp/lead-29347-r2/capture-1/host-fixture.mjs' PROOF_HOST_LABEL='Synthetic development workstation with an intentionally long environment name for narrow layout evidence' vp run dev --home-dir <new-private-home>`. Read real ports from the dev runner. This run used before web 7799/server15839 and after web6742/server14782. Paths and ports in drivers are adjustable fixture coordinates, not credentials.

Create `demo-workspace` as a new git repository, branch main, one empty commit (Evidence / evidence@example.test). Mint an ordinary isolated-server pairing credential per side with `node apps/server/src/bin.ts pair --home-dir <private-home>` while in its checkout. Keep those outputs private. Run `seed-fixtures-fixed.mjs` with CHECKOUT, ORIGIN, TOKEN_FILE, PROFILE_FILE environment variables. Save its stdout as before-ids.json / after-ids.json. It validates RPC Success and visible history before saving the private browser profile. Do not use the intermediate seed-fixtures-r2.mjs: its legacy model-selection field produced a failed initial seed. Rejected empty frames were not selected as proof.

## Browser driver

Run `node browser-driver-r2.mjs`; each stdin line is an async JavaScript program evaluated in the driver scope. `make`, `pages`, `contexts`, `sample`, `still`, `online`, `reload`, `video`, `record`, `finish`, `fs`, and `root` are available. For each side call make, add cookies from its private profile, navigate to its localhost origin, restore that profile's localStorage entries with page.evaluate, then navigate to `/<environmentId>/<threadId>` using its ids JSON. These private credential/profile files are deliberately excluded from uploads. They must be regenerated against each isolated server.

Load helper files with `eval(fs.readFileSync(root+'/fault-rows.mjs','utf8'))`, then ready-r2.mjs (the latter overrides the readiness helper), blocked-harness.mjs, seed-remote.mjs, project-rows.mjs. The readiness helper first waits for the actual synthetic message, imports the real catalog/registry, then waits for connected and no Syncing messages. This avoids the original async waitForFunction predicate problem.

Use `globalThis.faults={before:false,after:false}` and register context.routeWebSocket(u=>u.pathname==='/ws', route=>{ if(faults[side]) route.close({code:1013,reason:'Synthetic transport unavailable'}); else route.connectToServer(); }). This affects only app transport; descriptor, auth, HTTP/static resources and Vite remain reachable.

Date constructor output is frozen to 2026-11-03 00:00 UTC; Date.now and timers remain native. No logical-clock advance is used for grace/backoff. Dark theme, reduced-motion reduce, no device emulation, DPR1, 100% zoom/font scale. `still` awaits fonts and network idle and requires two byte-identical screenshots 500ms apart; animation/caret disabled only for stills. Video is actual CDP JPEG screencast encoded with frame durations, with UTC checkpoints in JSONL; it contains transient startup/sync frames and ends at the checked recovered state. Durations are not performance benchmarks.

## Completed rows

1. W6: use original long synthetic environment label and runFaultRow('W6-final',390,844). It records false startup, selective /ws failure, >8s real grace wait, another 3.5s retry window, restoration while report remains false, then recovered history. Before stays offline; head reaches attempt4/generation1 with no stale banner or Syncing messages in selected still.
2. Short-label fixture for W5/H3/W10: context.route('**/.well-known/t3/environment', async route=>{ const response=await route.fetch();const json=await response.json();json.label='Development workstation';await route.fulfill({response,json}); }). This changes synthetic descriptor metadata only. Reuse on both sides. No endpoint/auth/transport change.
3. W5: runFaultRow('W5-short',1440,900). Same real timer and transport scenario, short label. Selected failed and recovered stills.
4. H3: labelled driver-boundary harness. Load blocked-harness.mjs. setupBlockedHarness intercepts the Vite-transformed real connection driver, inserts a typed ConnectionBlockedError(reason permission, hop server) before resolver.prepare after primary registration, and retains the actual pinned supervisor, catalog, banner, diagnosis and UI. With false report reload, capture actual offline/base versus blocked/head banner. Set __proofBlocked=false then click the actual Reconnect and await recovered head. `runBlocked` was called with H3 prefixes renamed H3-short; generated artefacts use H3-short-labelled-harness, H3-short-blocked and H3-short-recovered. Do not present this as a real server authorization rejection. Afterwards unrouteAll HTTP routes, set __proofBlocked=false in init scripts, reinstall short descriptor label route, reload true and await readiness. No harness module response persists in the worktrees.
5. W10: mint a fresh bootstrap pairing credential on each isolated server, save as before-/after-remote-pair.private.log. `node mint-remote.mjs` creates private bearer-token files. seedRemote('before') and seedRemote('after') register the other pinned server as a healthy saved bearer environment labelled Remote sandbox. Both environments intentionally have the same minimal synthetic project/thread, so two sidebar cards are expected. Both are live initially. runProjectRow flips report false while healthy, closes only primary WS, then uses Ctrl+K > Add project > Enter. Before has only the saved remote eligible, so skips chooser; head shows explicit primary + remote chooser. Head selects Development workstation, Local folder, types scratch path, waits for demo-workspace result. Escape and sidebar Add project show the same chooser. The optional Add to another machine entry was not exercised and is not claimed. No clone/project action/provider execution is triggered.
6. W3 supplemental video: remove bearer:evidence-remote through catalog.remove.run on each side, report/reload true, await readiness, start W3-online-control video, reload true, await live history, capture/sample, stop. Existing round-1 W3 stills are retained; both original and supplemental pairs are pixel-identical.

Every row call logs UTC state/attempt/generation/network snapshots, sanitized WS method/tag/exit data, and settled-still checks. The full web-protocol-r2.jsonl includes earlier exploratory runs; selected prefixes above disambiguate the final proof.

## Review, diff and upload

make-diffs.py selects completed failure/recovery/chooser pairs and generates Diff0 (every nonzero max-RGB delta, including one-level changes) and DiffT16 (>16/255), magenta change mask over 20% brightness after image. select-artifacts.py selects recordings and copies sanitized transcript. review-videos.py extracts 15/45/70/95% frames for manual skim. Every selected still and diff was opened, original retained evidence reviewed, videos skimmed; no selected still has a loading state. Original partial files are uploaded as superseded archive material only and are explicitly not proof.

Upload only artifacts/ plus explicitly selected public scripts/runbooks/archives with `ghdrop upload -m`. Preserve its emitted Markdown verbatim. Never upload *.private.*, private homes, cookies, pair logs, actual bearer tokens or dev output. Archives contain drivers/harness sources only. upload-files.py records CLI stdout and builds a URL map. Sheets/index/Evidence reference public URLs only. No PR edit occurs in this step-agent task.

Stop browser with finish, stop tracked dev runners and their children, remove only the before checkout created for this task. Preserve round-2 scratch for the lead/auditor and leave lead-owned after checkout alone.
