# Round 2 reproduction

Exact before [d1ec71e6ff](https://git.cafe/pingdotgg/t3code/commit/d1ec71e6ff8b46bacfbe103af72034eba3654e63) and after [524d29b43a](https://git.cafe/pingdotgg/t3code/commit/524d29b43ad3a089e28ef009e37c21f2ba1c137f). Detached worktrees under /home/ubuntu/wt/pr-29803-{before,after}; each installed with global vp install, started with T3CODE_DEV_SEED=0 vp run dev against its own fresh .t3. No copied live database. Same scratch synthetic MCP project on both sides.

1. Save this bundle in /tmp/lead-29803-r1/evidence/r2-20261103T210900Z. Run setup-fixture.py. It creates weather-project and project-scoped Claude settings/MCP configuration. No global credentials change. weather-server.py advertises show_weather, plain_weather, legacy_weather, read-only refresh_weather, mutating write_weather, and synthetic resources. weather-card.html is the resource rendered by the real app.
2. Start browser-host.mjs (Playwright Chromium 148 installed at the specified executable path; CDP 9333). Pair its persistent profile to each scratch web origin. Credentials and pairing logs are private and excluded from uploads. Ports here were before web6981/server15021 and after web7320/server15360.
3. reseed.mjs configures the dedicated claude-evidence instance, using real Claude CLI2.1.303 through the preconfigured hub, model claude-haiku-5-5. Launch settings alwaysThinkingEnabled=false, showThinkingSummaries=false, --thinking-display omitted suppress reasoning on both sides. seed-driver.js uses public RPC to create project/threads and dispatch exactly the same prompt per tool. New threads are evidence2-e1/e4/e5. No DB mutations. Run static-captures.mjs, then retake-e5.mjs for matching E5 composer focus. The390 focus preparation is in web-driver.mjs.
4. web-driver.mjs prepares whole frames, viewport1440x900 or390x1000, DPR1, no device emulation, rootfont16px, reduced motion. Wait networkidle, document.fonts.ready, settle, then byte-identical screenshots500msapart. Date fixed2026-11-03T21:00:00Z. Exact label normalization: time elements get [data-evidence-time-label] visibility:hidden; leaf span/time/p/div matching Worked for becomes Worked for5s and gets [data-evidence-normalized-duration]; matching HH:MM AM/PM becomes9:00 PM and gets [data-evidence-normalized-timestamp]. See metadata for actual matches. No broad masking. Sidebar/details closed, conversation top0, pointer1,1, editor blurred.390 composer expanded then blurred identically.
5. interactions.mjs run captures E2/E3 on live E1 before restarting any server. E2 Refresh ->23°C, Read resource ->synthetic station text; E3 Save weather -> T3 confirmation naming weather/write_weather ->Confirm ->25°C. No new model turn; interaction-proof.json has one persisted run. Before absence still is explicitly permitted by matrix. CDP screencast PNGs use wallclock timestamps; timeline.json action times are relative to first video frame; MP4s10fps. FFmpeg duplicates final hold. Review the MP4 itself: an interrupted later debugging run reused the E2 raw-frame directory, so that directory is not published or authoritative. Published MP4/timeline/stills are the completed original recording.
6. E9 stopped-session.mjs init creates another identical weather fixture thread. Stop the tracked after dev-runner with Ctrl-C and restart same command/home. Then stopped-session.mjs run records Refresh error, typing/sending recovery message, Ready again reply, then Refresh ->23°C. This is the app's real UI and normal server restart. retake-recovered.mjs repeats Refresh after navigation to restore23°C and saves the settled recovery still; the uninterrupted recording remains the proof of recovery.
7. advisories.mjs configures separate labelled fake-version executables279/100 via public settings RPC, refreshes providers and captures Settings popovers plus empty unsupported thread banner1440/390. These wrappers fake --version only and delegate health probes to installed claude; no old Claude runtime is claimed.
8. Android: prepare client in each worktree with vp run mobile:install android --prepare. Native fingerprint same6f4353597ab5fb1c05d840a5cd8b37220c392f0b. First build required JAVA_TOOL_OPTIONS -XX:ActiveProcessorCount=2 -Dorg.gradle.workers.max=2 -Dorg.gradle.parallel=false -Dorg.gradle.jvmargs=-Xmx2048m. Lease Pixel9 from device-pool from checkout cwd. vp run mobile:install android --session SESSION --metro http://fs-08.tail131df4.ts.net:8081 (after) or8082(before). Metro command from apps/mobile: REACT_NATIVE_PACKAGER_HOSTNAME=fs-08.tail131df4.ts.net vp run dev:client --port PORT. mobile-proxy.mjs exposes scratchservers16360->15360/16021->15021. Pair with repo test-t3-mobile pair-client.sh using the scratch home and proxyorigin. Never upload pairing tokens.
9. Android emulator API36 Android16,1080x2424physical,DPR2.625, logical411.4286x923.4286dp,fontscale0.94(medium),dark,animationsoff. Native inspector clock freeze failed socket1006. Alternative allowed by brief: E1 persisted messages both seeded9:17PM; agent-device screenshot --normalize-status-bar fixes9:41/removes fluctuating statusicons. normalize-native-label.py is capture-only and applied identically to both own checkouts: threadActivity.ts Worked for duration rendered as5s. No feature logic changed; no commit. E6 open actual E1 and expand Worked for on before. E8 open valid app route t3code-dev://threads/ENV/THREAD/apps/ENCODED_TOOL_ITEM_ID (same corresponding seeded call); before cannot show app, after full app. E11 settings/environments/ENV scrolled to provider cards, and unsupported E10 thread. Each still has two byte-identical frames at least500msapart; normalized statusbar preserved, no image crops as primary.
10. E12: guest-probe.mjs enables guest sharing and calls real guestShare.create. Both return connect_unavailable (VM lacks T3Connect). guest-harness.mjs uses separate fresh browser contexts with no owner cookies, real /guest page and real timeline, mocked guest session+EventSource carrying each real synthetic projection. No environment connection or app renderer is injected. After shows Unable to load the weather app. This is labelled stream harness throughout. Real tunnel/redeem/link end-to-end remains UNVERIFIED permanent.
11. make-diffs.py generates both amplified binary magenta RGB max-channel differences: Diff0 threshold>0; DiffT>16/255, background35% before. E4 Diff0 exactly0pixels. All images and differences opened and reviewed; recordings checked at scripted action boundaries. No broad visual differences excused as capture noise. build-round2.py creates per-row sheets and compact evidence section. Uploads use ghdrop upload -m and exact output is retained in uploads-*.md.
12. Stop tracked servers/Metros/browser/proxy, close all device leases, remove only own before/after worktrees. Lead checkout and round1 untouched. Evidence stays outside git.

E11 settings retake uses the same Pixel9 lease and switches only its Metro bundle between before8082 and after8081; both native fingerprints are identical. Wait for a connected environment, enter its details, and scroll to the bottom on both sides. `device-command.sh` retains the lease-owner cwd for device operations while `pair-client.sh` mints credentials from the target checkout. Native capture accepts an optional fifth argument for the lease-owner cwd.
