# Real-app reproduction and capture procedure

1. Run `vp install` in detached base checkout [bcee0f5d28](https://git.cafe/pingdotgg/t3code/commit/bcee0f5d2872ba193c66ed9ba979bbe424f89ff9); head product source is [3cee08825d](https://git.cafe/pingdotgg/t3code/commit/3cee08825d0fdf2d79f0ff56eab4d98582534ed4).
2. `python3 servers.py start <head|base>` starts fresh, isolated shell H0 backend :19000, H0 Vite frontend :6733, and separate H1 :19001. T3CODE_DEV_SEED=0 is always set. The first combined dev launch hit ENOSPC (inotify); servers.py runs the backend without Node's watcher and Vite with CHOKIDAR_USEPOLLING=1. Same source and runtime, no product edits.
3. Mint separate codes using `node apps/server/src/bin.ts pair --base-dir <run>/<side>/host --label audit-web-A`, another for B and Z. Mint H0's code from its shell state. Keep all codes/logs under `.private` filenames and never upload them.
4. Pair the shell through `/pair#token=<private code>`. Use browser-driver.js via preview_evaluate. Call `capture.pair('http://127.0.0.1:19001', Acode)`; use returned environment ID as expectedEnvironmentId for `capture.pair('http://192.168.122.39:19001', Bcode, id)`. These are the actual production onboarding service; no presentation injection is used for real-app S01 or R02/R05.
5. Visit `/settings/connections`. Expand the actual `4 routes` button. The server advertises two extra learned routes (10.77.9.193 LAN and 100.73.218.5 Tailscale). They remain in the same order on both revisions. Confirm A precedes B. Independently exchange Z via admin.py. Its bearer credential is held in private scratch only. Read only the capturer-owned H1 database to map exact client labels to session IDs; revoke through authenticated POST /api/auth/clients/revoke.
6. Call the existing catalog retryNow command after revocation. Read state with capture.state(id), which returns only non-secret route IDs, addresses, phase and active/refused IDs. Capture actual server rejection text and B active.
7. For stills, freeze the client Date to 2026-11-10T10:30:00Z after authentication; set viewport to 1440x1000 or 390x844 CSS px, emulate the corresponding color scheme, and call production setAppearanceMode for light/dark. preview tools have no reduced-motion or network-idle emulation command; wait for expected connected content, fonts.ready, no loading UI, then take two full-surface images at least 550ms apart. settle.py accepts only pixel-identical frames. No device emulation; DPR2; default 16px root font, default browser font scale.
8. For R02/R05 recordings, first repair A using a fresh audit-web-A-repair code and actual onboarding. Record healthy A; Z revokes only that new session; retryNow; B active. Open Add route, observe H1 context, cancel, reopen, enter exact original A address, enter audit-web-A-final fresh code, submit via actual Add route UI. Confirm A active, warning absent and no duplicate address. React resets the temporary password input type: the public MP4 therefore masks the pairing-code field in post-processing during seconds 5–8 using redact-video.py. The original credential-bearing MP4 stays private; the remainder of the video is unchanged.
9. Stop recording. Retain tool's MP4 in scratch (do not mislabel it webm). Keep timestamps in per-recording companion transcript. Use diffs.py for zero-level amplified (any nonzero channel → 255) and threshold16/255 (6.2745098%) diffs. Open every accepted image and diff before upload.
10. Stop only tracked process groups from servers.py; close the capturer's browser tabs and device session; remove the detached base worktree. Leave scratch evidence for the lead. Upload only explicit synthetic evidence files/scripts, never private files.

11. web-harness.js mounts the actual checkout EnvironmentRoutesList with production theme/tooltip providers and a separate atom registry. It injects only synthetic route/presentation/prepared data; the banner labels this rendering-only proof. It reproduces the measured real parent list widths: 786px at 1440 viewport and 228px at 390 viewport. S02–S07/C01 are harness fixtures; source fixtures are included verbatim in that script. No relay transport or SSH connection was provisioned.
12. gates.cjs forwards three independent origins :19002/:19003/:19004 to H1 :19001, preserving raw TCP HTTP/WebSocket traffic. Pair separate credentials through each origin using pair-code.py, insert A/B/C in saved order with production catalog reorder, and leave old revoked/learned routes last. The gate control endpoint toggles an origin and destroys only its tracked sockets.
13. R03: establish C while A/B gates are closed; revoke A through independent Z while A is unreachable; start trace-driver.js subscription and recording; open A then call routeTrace.wake(), which dispatches visibility events to the real application listener. Verify A becomes refused while phase connected, active C and generation unchanged. This is a synthetic browser activation event; no fake refusal is injected. R04: open valid B, allow the production better-route check to run, observe active route changing C→B while A remains refused. Connection cooldowns can delay this check: the trace, not an action label, decides whether a switch occurred.
14. R06: close B/C so no usable saved route remains; observe backoff/connecting with empty refused IDs. Restore B/C, then disable/re-enable through the UI and inspect connected/available/connecting/connected states. No page reload or catalog reorder is used inside a recorded history claim.
15. classifier-runtime.mjs runs actual connectOverRoutes and supervisor code with one fresh scoped synthetic typed driver for each failure. It awaits the connected subscription event, closes every scope, and records active/refused IDs. Base failing the two positive after-expectations is the expected regression result. Six non-host/clock-skew cases should never flag A; server-hop authentication and permission should. These results establish classifier behavior, not a real relay/tunnel failure.
