# Rebuild the PR media evidence

Target: [#21631](https://git.cafe/pingdotgg/t3code/pull/21631). Before [15707a84](https://git.cafe/pingdotgg/t3code/commit/15707a84b03e27bb257d60e027812ebf94bbda25); after [597f1aea](https://git.cafe/pingdotgg/t3code/commit/597f1aea6191d434ad23877e0e94121b7858382e). No source changes or commits were made.

## Boundaries

Web is **synthetic PR-detail query harness + production panel/renderers/hooks + live asset WebSocket RPC**. The scratch HTTP proxy replaces only the PR-detail module response and successful `/api/assets/` GET bytes, and proxies WebSocket traffic to the real server. Media bytes are synthetic PNG/MP4 fixtures, not fetched from GitHub. Mutation callbacks in the standalone producer harness are no-ops. Guide and annotation/timeline combinations use production components inside the standalone harness rather than the complete guide/code route. Normal panel captures use its real memory router and app atom registry; the standalone producer harness has no router, so autolink navigation is outside its claims.

Mobile is a **React DOM/JSDOM harness with native views, modals, navigation, project query and asset hooks stubbed**. It bundles production wrapper, Markdown/Media/Description/Timeline components, media source/gallery/key helpers. It records chronological steps, actual hook resource arguments and modal source inputs, not native pixels or playback. The three named wrapper cases use the actual shared wrapper with identical owning-screen inputs; full screen navigation is not driven. `mobile-forwarder.js` passes the emitted mobile resources unchanged into the web client's real refresh hook and live dev RPC, mapping the harness environment alias to the isolated server's actual environment ID.

Server transcript uses actual contract decode, issue/resolve services and the same isolated signing secrets as each dev server. Genuine base-minted tokens are resolved by head code using the **before isolated home**; no keys are copied or published. Public wire logs redact bearer capability strings and retain exact request bodies, decoded synthetic claims, expiry and a SHA-256 of the complete capability relative URL. The resolver transcript supplies signature verification, not just decoding. S4 substitutes only the upstream SourceControlDriver fetch and supplies a fixed Effect Clock; its request, response headers/status and bytes are otherwise produced by production code.

## Run

1. Materialize each supplied revision in its own checkout; run `vp install`. Put all scratch files in one new evidence directory. Use the uploaded PNG/MP4 fixture bytes for byte-identical reproduction. `seed-fixtures.py` is an optional regeneration helper; those regenerated bytes may differ from the uploaded originals.
2. In each checkout run `T3CODE_DEV_SEED=0 vp run dev --home-dir <new isolated home>`. Do not copy live userdata. Read the chosen ports from the runner. This run used before server 15558/web 7518 and after server 16258/web 8218.
3. Run `node fixture-proxy.mjs <webPort> <proxyPort> <before|after> <scratchDir>`; this run used proxy 18532 before and 18531 after. The proxy creates a fresh timestamped log directory per run.
4. Open the proxy URL in a T3 Preview browser and pair using that isolated server's ordinary local pairing flow. Never publish the pairing URL or credential. Wait for the real app to finish bootstrapping its session, then evaluate `web-bootstrap.js`. It hides the original app root and mounts the production PR panel with synthetic detail and a live atom registry. Date is fixed to 2026-11-03T12:00:00Z in the page only.
5. Use `capture-driver.js` inside `functions.exec`, storing `{before:<tabId>,after:<tabId>}` as `tabs`. It sets appearance/viewport/content width/scroll, awaits fonts and visible image decoding, captures twice 500 ms apart, then runs `diff-captures.py`. Put every capture run under a new timestamped directory. The published final run contains only settled pairs; raw superseded attempts remain local for the auditor.
6. Static cells: WD 1440×1000 dark, panel640, scroll0/300/750; WL same in light. WN 900×1000 light, panel320, scroll0/550. Additional 390×1000 light panel320 scroll0/550 and dark scroll0. Fragment cell uses the same panel with a fragment-bearing blob PNG and linked tall PNG. All are whole browser viewports, not crops.
7. Interaction scripts are expressions evaluated in the mounted page: `interaction-recording.js`, `extra-producers.js` then `extra-recording.js`, `no-media-recording.js`, and `rpc-failure-recording.js`. Start/stop Preview recording around each. Scripts use one-second presentation beats to make the scripted sequence readable, not a passing-test timeout. Primary recording preparation restores the original body and long comment with a focus link/image before the fold, scrolls to the collapsed comments, and pauses videos at0.1s. The driver logs precise monotonic timestamps. Evaluate long scripts with `awaitPromise:false` and collect their completion after the sequence; Preview evaluate has a short command timeout.
8. `gallery-driver.json` records actual Preview click actions: open Wide image, Next media, Previous media, close. For denied editor controls, evaluate `extra-producers.js`, then `extra.set({scene:'editor',index:0,denied:true})`; its synthetic environment has no prepared/granted session. Save is disabled. Record the controls and return with `extra.unmount()`.
9. The RPC-rejection driver logs the client-emitted request, then rewrites just its URL to an unrecognized external host before native WebSocket send. The real server returns AssetGitHubMediaUrlValidationError. A MutationObserver records the original fallback image src before the image's byte load fails. This is an explicit transport fault injection, separate from the proxy's media-GET404 fixture.
10. Build/run mobile from each checkout: `node build-mobile-harness.mjs <checkout> <new bundle.cjs>` and `node run-mobile-harness.cjs <checkout> <bundle.cjs> <output base>`. Output is a new timestamped directory. Native module boundaries are enumerated in the builder/stubs. Extract the emitted resources and evaluate `mobile-forwarder.js` after assigning them to `window.mobileResources` in the connected web harness.
11. `collect-wire.py` correlates successful createUrl wire requests/replies, exports a public redacted log, and retains private inputs locally. Run `node --experimental-strip-types server-driver.mjs <head checkout> <signer's isolated home> <private input JSON> <output base>` before the issued capabilities expire. For S2 use head checkout + before isolated home + genuine before inputs. The runtime writes a new timestamped directory.
12. `package-evidence.py` copies approved artifacts and makes labelled contact sheets; `make-report.py` constructs the Evidence markdown and index after upload. Stop only the server/proxy processes you started. Remove only your own before checkout. Keep the assigned step scratch for the pass-2 auditor.

## Environment

VM fs-11, Linux x86_64; T3 Preview Chromium156, no device emulation, DPR2, font scaling100%, root font16px. CSS viewports are recorded above. Preview rescales saved PNGs: 1440×1000→1280×889; 900×1000→1280×1422; 390×1000→780×2000. No reduced-motion emulation is exposed by Preview; reducedMotion=false was observed. Fonts/media completion and two byte-identical frames500ms apart were used to settle stills. Dynamic footage is not a frame-exact timing benchmark. No desktop app or pooled native device was started.

## Additional completed controls

For the additional state captures, run `fixture-proxy-states.mjs` instead of the original proxy. It adds explicit pending/error/absent detail states through `fixture.update({__query:{...}})` and replaces only the review-guide query/writer boundary with `fixture.guide`. Run `panel-states-recording.js` for detail gates and cached refresh. `guide-tab-harness.js` mounts the production Guide tab and tour with synthetic stops; `guide-ui-driver.js` is the actual Preview orchestration for Next/Previous and J/K, with per-side timing in `guide-ui-steps.json`. Navigation buttons retain focus for the keyboard portion. This is a guide-query harness, not agent-generated guide data.

`thread-surface-harness.js` mounts actual RightPanelSurfaceContent inside a router/atom registry. The thread fixture changes capabilityKnown/supported, closes by setting surface=null, then reopens with a new surface id; exact chronological states/text are in `thread-surface-steps.json`. It uses context=thread via a synthetic linked PR. The page-context panel was captured separately. No real project/thread data was copied.

For actual Summary comment editing, set one synthetic comment's kind to issue-comment, edit.comment=true and matching viewer login. The body is `Synthetic editable comment` plus an image named comment-edit.png on the synthetic raw GitHub host. `comment-ui-driver.js` drives actual Edit comment, nonempty Preview, Write, clear, empty Preview, Cancel. `comment-ui-steps.json` records real timings and visible states. `comment-editor-recording.js` is the earlier page-expression driver; accepted clips use the slower UI driver so each state is readable. No Save action is performed.

`reconnect-recording.js` observes the actual dev WebSocket used for a fresh image resource, closes only that socket, then mounts a second resource. The client reconnects automatically; the logged send uses a different WebSocket instance and produces successful signing replies. Other mounted asset states also renew automatically. The driver restores WebSocket.prototype.send afterward.

The updated mobile entry includes unknown-provider metadata as well as absent metadata. The additional panel/guide/comment/reconnect transcripts contain exact synthetic resource inputs; published files contain no bearer strings. `package-additions.py` collects these additions and builds recording review sheets.
