# Proof audit, pass 1 — repository context for PR media

Target: [#21631](https://git.cafe/pingdotgg/t3code/pull/21631) ([GitHub mirror](https://github.com/pingdotgg/t3code/pull/21631)). Reviewed branch `batch-07-pr-media/context`, head [597f1aea](https://git.cafe/pingdotgg/t3code/commit/597f1aea6191d434ad23877e0e94121b7858382e), against supplied merge-base [15707a84](https://git.cafe/pingdotgg/t3code/commit/15707a84b03e27bb257d60e027812ebf94bbda25). Local HEAD and clean status matched. Upper stack work is excluded. All examples below are synthetic.

## Findings

1. **No spec contradiction found in the inspected paths.** The new repository value reaches the resource schema, signed claims, and resolved media asset. Web takes it from `detail.repository`; mobile takes it from `params.repository` through `pullRequestTarget`, deliberately independent of the checkout. The existing fetch response still calls `fetchMedia({url, headers})`; this layer cannot demonstrate repository-specific authentication, private EMU access, or SSO recovery. Those are not acceptance rows for this PR.
2. **A still cannot establish the change.** Mandatory evidence is the actual client-generated `assets.createUrl` input, the issued capability, and a signature-verified `resolveAsset` result retaining repository. Merely base64-decoding a token is insufficient. Also retain an omitted-field request and an old-shaped signed capability. A matched before/after still only establishes stable appearance.
3. **Existing tests are useful but partial proof.** The new server test exercises schema decode → issue → resolve without WebSocket transport. The web test mocks `useAssetUrlState` as Loading and observes resources; it neither signs nor displays successful media. The mobile test checks two gallery resources. None is a real end-to-end capture, and no tests or captures were run in this pass.
4. **Offline source data must be labelled.** There is no supported RPC to insert arbitrary fake PR detail. A local project alone does not produce synthetic GitHub PR detail. GitHub reads use the HTTP/GraphQL client, so a fake `gh pr view` executable is insufficient. Use a source-control/query boundary harness for the panel, with the real renderer and real asset hooks connected to an isolated dev server; label it **synthetic PR-detail harness + live asset RPC**. For successful display, serve synthetic media at the browser's outgoing asset-GET boundary, or inject a fake source-control driver in a server harness. Label the mocked boundary explicitly. Do not claim real private GitHub access.
5. **Mobile propagation is mandatory; native visual evidence is optional here.** Mobile changes code separate from web and changes the full-screen video thumbnail/presentation identity. Require mobile wrapper/hook/helper transcripts (M1–M6). There is no new native layout or platform branch, so a simulator screenshot is not in the minimum set. If a native visual/interaction pass is added, it must use a pooled Android emulator or iOS simulator, with `test-t3-mobile`/`device-pool`; do not present DOM/native-stub harness recordings as native evidence.

## Fixture data and axes

Use a synthetic environment `fixture-environment`, project `fixture-project`, checkout `/synthetic/personal-fork` whose synthetic repository identity is `fixture-user/personal-fork`, and PR repository **B** = `fixture-org/media-upstream`. For repository switching use **C** = `fixture-other/media-upstream`. PR number 42, fixed timestamps, viewer/author `fixture-user`. Disable mutations or make them no-ops in the harness. Do not use real credentials, projects, or private content.

Synthetic media identifiers (they are inputs to URL recognition; do not rely on upstream bytes):

- **I**: [GitHub blob PNG](https://github.com/fixture-org/media-upstream/blob/main/shot.png), normalizes to [raw PNG](https://raw.githubusercontent.com/fixture-org/media-upstream/main/shot.png).
- **J**: [legacy upload image](https://github.com/fixture-org/media-upstream/assets/123/fixture-image).
- **V**: [extensionless uploaded video](https://github.com/user-attachments/assets/fixture-video).
- **Q**: [raw MP4](https://raw.githubusercontent.com/fixture-org/media-upstream/main/clip.mp4).
- **D**: direct non-host media on `https://media.example.test/shot.png` / `clip.mp4`, intercepted to synthetic bytes.
- **I-dark/I-light**: raw GitHub PNGs differing only in basename, used by a theme-selected HTML picture.

Prepare valid synthetic PNGs at 240×100 and 100×180 and a short MP4 with a visible fixed test pattern. For dimensions use HTML `img` authored width/height and markdown images, a long alt/caption, and a linked image. Use 0, 1, and 2+ media, including a duplicate image URL. GitHub uploads without extensions are classified as video when bare, but remain images when authored as markdown images; exercise both, without requiring the remote URL to exist.

The repository string itself is never rendered and introduces no length, count, width, or theme branch. Repository B equal to checkout versus different from checkout is the same visual fixture; the different case is required for propagation proof. Missing/null context and omitted/empty optional value are separate logical controls. A 512-character repository is a schema boundary, not a screenshot fixture. GitHub is currently the only built-in definition supplying `mediaUrl`, so other forge media are direct-URL controls, not extra credentialed host fixtures.

Viewport cells:

- **WD** = web 1440×1000, dark; PR panel content width about 640 px.
- **WL** = identical web geometry, light.
- **WN** = web 900×1000, light; explicitly constrain PR content to 320 px if the app's panel width does not naturally do so. Label a constrained component capture as harness.
- **DT** = Electron at WD, same web renderer. No separate Electron visual change; W11 covers the shell-specific path only if desired.
- **MH** = mobile logic/DOM-native-stub harness, no native viewport claim; theme/platform are mocked inputs only. An optional real device pass uses its recorded actual phone size, dark, on one pooled device. iOS/Android rendering differences are pre-existing and do not multiply required resource transcripts.

## Complete path inventory and reduction

### Web/desktop

`PullRequestDetailPanel` has two production callers: `PullRequestsPage` (the `/pull-requests` page, `context=page`, `tabOnPage`) and `chat/RightPanelSurfaceContent` (thread right-panel surface, `context=page|thread`). The latter gates unsupported/unknown server capability before mounting. Panel detail absent/error/loading has no markdown provider; available or cached detail supplies its repository even while refreshing. Summary, Guide, Timeline, and Code are separately mounted tabs and remain mounted but invisible after switching. Activity loading/error gates Timeline rendering. These gates are represented by W0, W4–W7, and W10.

Every production `PullRequestMarkdown` caller:

- `PullRequestSummaryTab`: description and conversation bodies (via `PullRequestCommentBody`). Description empty state bypasses markdown; editing swaps to editor.
- `PullRequestCommentBody`: clipped at 240 px, overflow fade/toggle, focus expands, Show full comment / Show less. Empty/short/long and image below fold reduce to W0/W4.
- `PullRequestTimelineTab.TimelineBody`: markdown bodies only; plain text timeline messages bypass it. Conversation folding/loading/pagination only change when a shared renderer mounts (W5).
- `PullRequestReviewAnnotation`: rendered inline review comments; editing swaps to editor; load more comments mounts further bodies (W5/W6).
- `PullRequestMarkdownEditor`: Write textarea versus Preview; Preview empty shows Nothing to preview; nonempty mounts markdown, for description/comment/review editing. Saving and permission state disable controls, without changing repository propagation (W6).
- `guide/GuideProse`: formatted summary/attention/note/question paths; `plain=true` or missing GuideProseContext returns raw text and does not mount markdown. Callers are GuideStop, GuideNotes, and GuideExcerpt; their plain captions/file notes do not request assets (W7).

`PullRequestMarkdown` splits empty/text/image/video/unknown segments. Markdown segments and standalone image attachments enter `ChatMarkdown`. Recognized host video attachments (bare URL, markdown link, or standalone HTML video/source) enter the private `PullRequestHostVideo`, whose only caller is this component. Non-host video enters the direct MediaVideoPlayer. Unknown attachments render an external-link card. **The current splitter never emits `media=unknown`; that card is harness-only.** Fences, indented code, malformed/unclosed HTML video remain markdown and must not trigger a host-video resource merely because their text contains a URL.

`ChatMarkdown`'s changed image branch requires all of `sourceControlMedia`, cwd, environment, direct source, and a recognized host URL. It carries the repository to `ChatMarkdownAssetImage`, including an image-syntax video. Theme-selected picture source, authored width/height, image alt/title, standalone versus inline, link nesting, source fragment, and image/video kind affect the existing renderer or media UI; W1–W3 cover them. Context-reference chips, audio, file-link chips, non-host direct media, workspace/host-file images, and unavailable fallback use earlier/other branches and receive no new field (W9).

All other production `ChatMarkdown` callers leave `sourceControlMedia` false/absent, so one representative negative control is sufficient, with caller audit for the remainder: `composerContextPresentation`; `chat/WorkflowOpeningBlock`; `chat/TimelineWorkEntryRow` (2 sites); `chat/V2ItemInspector`; `chat/ProposedPlanCard` (2); `chat/TimelineAssistantRows`; `chat/TimelineWorkGroupRows` (2); `chat/TimelineUserMessageRow.UserMessageMarkdown`; `chat/UserMessageContextChips` (2); `chat/ComposerPendingUserInputPanel`; `files/FileMarkdownPreview`; `files/NotebookPreview`; `files/AttachmentFilePreview`. Their unrelated markdown/table/code/streaming branches have no new input or condition and do not require a full retest of those components.

Asset states: Loading gives null media source; Success gives signed URL; Failure falls back to fetchUrl on web. The underlying image/video can still fail loading and expose retry/unavailable controls. Image expansion/gallery, media actions, video playback/pause/fullscreen, retry, visibility, and capability refresh are interaction/time sequences, therefore recordings (W8, W10) where executed. A hosted video and image-syntax video are distinct producers despite similar playback UI.

### Mobile

`PullRequestMarkdownRepository` has three production wrapper callers: `PullRequestRouteScreen`, `PullRequestDiscussionScreen`, and `guide/PullRequestGuideStopScreen` (same file exports guide content). It derives media environment/cwd from target project, repository from target input; missing project/workspace gives null media location even if changeRequest metadata is present. Provider/url affects autolinking and hard line breaks, not media repository. See M1/M5.

Every production mobile `PullRequestMarkdown` caller: `PullRequestDescription`; `PullRequestTimeline` review/comment row; `PullRequestDiscussionScreen` comment; `PullRequestGuideScreen` stop summary, attention note, file note, question; `guide/ReviewGuideExcerpt` caption; `guide/GuideOutline` first-stop summary. Guide prose explicitly opts out of host hard line breaks. All inherit the owning wrapper. These render through `PullRequestMarkdownContent` + `MarkdownImage` or the standalone `PullRequestMedia` path.

`usePullRequestMediaUrl` callers: the private MarkdownImage, `PullRequestMedia`, and `PullRequestImageAttachment`. All now use one `pullRequestMediaSource`. Recognized host + location returns environment/resource, optional truthy repository included; missing location or unrecognized URL returns direct URI. Loading/Failure gives null URI for a resource; Failure sets unavailable. Success uses signed URI. Refresh closes over the same resource.

`PullRequestMedia` callers are markdown standalone segments (normal) and `PullRequestDescription` non-image attachment strip (compact). Image branch opens a FilePreviewModal/gallery. Video and unknown branches render a 176×112 compact tile or normal inline player; compact tap opens VideoPreviewModal, normal player renews via resolvePlaybackUri. Visible context comes from the RouteScreen's active tab; hidden media defers thumbnails/playback. Caption truthiness falls back to URL basename then Attachment. `PullRequestImageAttachment` has one caller, description strip, with 176 px image tile, one-line caption, supplied gallery. See M2/M3.

Description layout: empty placeholder; body measured ≤240 px versus >240 collapsed/expanded/faded, platform-specific fade color; no attachments versus horizontal mixed strip with long truncated captions. Timeline comment long means >450 characters OR >8 lines; initial displayed text is sliced to 1200 characters and clipped to 240 px, full gallery uses original body. Short, long, and expanded reduce to M6. Underlying native text, theme, and platform-specific chrome are unchanged; resource traces, not a new native layout matrix, are required.

`mediaVideoThumbnailKey` callers are `ThreadMarkdownVideo`, `VideoPreviewModal.MediaPreviewModal`, and `previewPresentation.videoPreviewKey` (presentation identity). Only github-media gains `repository ?? ""`; same env/cwd/url under B and C must differ, absent repository remains valid. URI, attachment, media-file, draft-workspace-file, and local-preview keys are unaffected controls. **Inline PR MediaVideoPlayer still uses `thumbnailKey={props.url}`; do not incorrectly demand repository in that unchanged inline key.** The changed key concerns resource-backed previews/ThreadMarkdownVideo/presentation. See M4.

### Contract/server

`AssetResource` keeps wire tag `github-media`. Repository is optional, trimmed nonempty ≤512 characters when present. `AssetClaimsSchema` permits absent repository, and issueAssetUrl places the decoded resource's repository inside signed version-1 claims while still normalizing URL. resolveAsset verifies signature and expiry before returning `kind=source-control-media` plus repository/cwd/url/expiresAt. WebSocket assets.createUrl passes the resource directly to minting without checking that cwd exists. HTTP asset route resolves the token and delegates unchanged to sourceControlMediaResponse. See S1–S4.

## Fixture matrix

Status is **planned/capturable**, not captured. Affected means this diff changes the carried resource/claim/identity; **control** means an unchanged branch that bounds the change. Minimum identifies mandatory evidence; other rows are inventoried controls or may be combined with the mandatory sequence. `recording` includes a chronological harness event log for nonvisual rows; no still substitutes for an interaction/time sequence. All harness evidence must identify its substituted boundaries.

| ID / named fixture | Data and branch / visible combination | Diff effect | Minimum capture cell and format | Seed / driver and status |
|---|---|---|---|---|
| W0 — no rendered media | Panel capability unknown/unsupported; detail pending with no data; terminal error/no detail; then loaded empty/whitespace or text-only body; no comments/attachments | Control: no resource should mint | No extra minimum still; if transition is exercised: **recording** | App capability/detail RPC results; synthetic query-boundary harness for deterministic states. **Planned, harness-capable** |
| W1 — settled image body | Detail repo B, checkout fork; markdown I and J; standalone bare PNG; duplicate I; one wide and one tall image, short + long alt; text around them | Affected: every recognized image resource has B and normalized URL; appearance stable | **WD before + after still mandatory**; include image's live RPC in S1 | `/pull-requests` selected PR or thread surface; stub detail in harness, real asset hook; intercept asset GET with fixed PNG. **Planned, harness required offline** |
| W2 — settled videos, both producers | B; bare V, standalone linked Q, HTML `<video><source src=Q>` and image-syntax Q; show fixed MP4 first frame | Affected: PullRequestHostVideo and ChatMarkdownAssetImage-video both carry B | First-frame still can share W1; play/pause/fullscreen/retry is **recording**, combined W8 | Same detail harness; valid synthetic MP4 asset GET response. **Planned, harness required offline** |
| W3 — themed/sized/linked image | B; HTML picture with I-dark/I-light, authored size 240×100, linked image with expansion suppressed, source fragment; 2 images and long caption in narrow panel | Affected resources for selected theme; all sizing/layout unchanged | **WL and WN before + after still mandatory**; WL must select light picture, WD dark picture | Same harness + appearance setting; constrain content if needed and label it. **Planned, harness capable** |
| W4 — summary conversation fold | B; summary description plus a short comment; second comment >240 px, media before and below fold; expand by toggle and focus, then collapse | Affected: descriptions/comments/replies keep B; clipping unchanged | **recording**, WD; resource transcript mandatory; no extra theme cells | Populate comments/reviewThreads through detail/activity harness; Show full comment → Show less, keyboard focus. **Planned, harness capable** |
| W5 — timeline / code annotation | B; markdown review/comment vs plain timeline event; inline review thread with two comments and one paginated media reply; open/fold thread, load next page | Affected only markdown review/comment resources; plain events control | **recording**, WD; can share W4 recording; inspect each producer request | Timeline tab and Code discussion with synthetic diff/threads/activity and comment-page replies. **Planned, harness capable** |
| W6 — editor Preview | B; body, comment, annotation editor; Write → nonempty Preview → empty Preview → cancel; saving and denied-write controls disabled | Affected only nonempty Preview resources; blank/write/saving UI unchanged | **recording**, WD; mandatory transcript that Preview retains B for editor caller | Edit in panel harness with no-op writes; host/write permission fixtures; do not submit to a real PR. **Planned, harness capable** |
| W7 — guide prose | B; summary, attention, note/question contain media; plain excerpt/file caption or absent GuideProseContext returns text; lineBreaks=false | Affected formatted prose inherits B; plain/no-context has no request | **recording** to enter Guide / advance stop; transcript mandatory; no separate still minimum | Seed synthetic guide state at guide-query/store boundary, no agent generation; standalone GuideProse harness for no-context branch. **Planned, harness capable** |
| W8-L — signing/loading | W1/W2 content; hold asset RPC response or media GET before completion | Affected request includes B; null-source placeholder unchanged | **recording** beginning before release; combine L→S→F→R | Deferred RPC/GET boundary gate, release explicitly; no timing sleeps. **Planned, harness capable** |
| W8-S — loaded media | Release W8-L to valid signed URL and fixed PNG/MP4 bytes | Affected signed token retains B; loaded layout unchanged | Same **recording**, plus W1/W3 settled stills | Live dev RPC for signing; synthetic bytes only at GET boundary. **Planned, mixed live/harness** |
| W8-F — failure / fallback | Reject asset RPC once (web uses fetchUrl fallback), separately fail media GET to show unavailable/retry UI | Carried field persists; fallback URL policy unchanged | Same **recording**; distinguish RPC failure from byte-load failure | Boundary rejects with deterministic fixture error; intercept fallback URL too. **Planned, harness capable** |
| W8-R — retry / renewal | Retry W8-F; trigger explicit asset refresh/reconnect; play then hide/show panel | Affected repeat resource still B; stable loaded video/controls per existing behavior | Same **recording** + chronological RPC log | Actual Retry/media control and reconnect/refresh through client harness; capture emitted requests. **Planned, harness capable** |
| W9 — legacy/no-host/gates | No PR context or repository null/omitted; then recognized image with sourceControlMedia=false, missing cwd, null env; direct D image/video, relative file, audio, code-fenced URL, malformed video; unknown attachment card | Legacy host resource omits field; other paths no new resource | Transcript controls; interactive external-link/action use is **recording** if performed; no extra minimum still | ChatMarkdown/PRMarkdown component harness; unknown attachment requires mocked splitter because production cannot emit it. **Planned, explicitly harness-only controls** |
| W10 — repository/environment/tab switch | Keep URL and cwd fixed; switch B→C and environment E1→E2; mounted inactive tabs; cached detail while refresh pending; reopen page via thread surface | Affected new resource identity must target current repo/env; hidden and stale-data lifecycle unchanged | **recording** + resource/RPC sequence mandatory; one WD session | Rerender panel reference/detail harness; real environment asset runtime; switch tabs and thread surface using app controls where seeded. **Planned, harness capable** |
| W11 — Electron shell | Same W1 data; attachment/open-original external navigation | Control: shared renderer, Electron openExternal handling unchanged | **recording**, DT, optional; no duplicate required still | Actual Electron shell if requested; unknown attachment only via harness. **Optional, no minimum native/desktop run** |
| M1 — mobile owning repository | Route wrapper, discussion wrapper, guide-stop wrapper; project cwd fork, params.repository=B, optionally changeRequest metadata deliberately names another synthetic URL; inline markdown image, description, timeline, guide and discussion bodies | Affected: context/source/hook input must use target B and environment, never derive from cwd or metadata URL | **Mandatory transcript**, MH; navigation sequences are **recording** if driven | DOM/native-stub hook harness patterned on useMediaVideoPlayback.test.tsx; render actual wrapper with mocked useProject and boundary queries, observe resources then call live dev RPC with them. This is **harness**, not native UI proof. **Planned, harness capable** |
| M2 — mobile image preview/gallery | 1 image then 2+ with duplicate selected image; inline markdown image + standalone image + 176 px description strip tile with long caption; tap second, page gallery, close | Affected inline hook and every preview/gallery resource retain B; selected item once | **Mandatory recording transcript**, MH; no mobile still minimum | Actual media/image-source/gallery functions and handlers with native views/modals stubbed; inspect supplied preview source and renewal resource; synthetic detail/document. **Planned, harness capable** |
| M3 — mobile video lifecycle | Normal inline versus compact 176×112 tile; caption / basename / Attachment fallback; visible/hidden tab; Loading/Success/Failure; compact tap opens preview, refresh/play then dismiss | Affected both hook resource and preview source retain B; loading/failure and hidden playback behavior unchanged | **Mandatory recording transcript**, MH | Existing playback harness pattern with captured asset-hook resource, controlled promises/player events; live RPC for the captured resource; no native playback claim. Unknown-kind prop is harness-only. **Planned, harness capable** |
| M4 — thumbnail/presentation identities | Same env/cwd/url, repo B versus C versus absent; resource-backed preview key and videoPreviewKey; URI/attachment/media-file/draft-file/local controls | Affected github-media key includes repo or empty marker, differing for B/C; other branches unchanged | **Mandatory transcript**, no viewport; if preview changes while open, **recording** | Import actual videoPreviewSource helper and preview identity helper with native navigation stubs. Show keys and equality comparisons; note inline PR key remains URL. **Planned, pure/helper harness capable** |
| M5 — null context / omitted field | Missing project/cwd → null location; non-host D; location with repository omitted/empty; optional provider metadata absent/unknown | Null/direct produce URI without resource; legacy recognized resource omits repo; autolink/linebreak controls unchanged | **Mandatory transcript**, MH; no separate still | Wrapper hook harness for no project and helper calls for optional location; observe no mint for direct path, legacy mint for omitted repo. **Planned, harness capable** |
| M6 — mobile folds/counts | Empty description; short body ≤240 px vs long collapsed then expanded; no attachments vs mixed strip; timeline comment 450/451 chars or 8/9 lines, >1200 with second image beyond truncation | Existing distinct layouts; mounted resources/whole-body gallery must keep B | **recording transcript**, MH, combined with M1/M2; optional native visual only | Synthetic description/timeline data and deterministic onLayout; expand/collapse and inspect resource/gallery changes. Native fade/theme screenshots are outside minimum. **Planned, harness capable** |
| S1 — new-shape live signing | Captured W1/W2 client resources `{_tag:github-media,cwd:fork,url:I/V/Q,repository:B}`; same-resource repo equal/different control | Affected decode/mint/resolve retains B, URL canonicalization unchanged | **Mandatory real dev WebSocket request/response log + server-service transcript**; not a screenshot | Send client-emitted resources to real isolated assets.createUrl; run actual resolveAsset using same isolated signing secret/config and issued suffix; print resolved fields, not credentials. No GitHub fetch needed. **Planned, real runtime capable offline** |
| S2 — old shapes | Request omits repository; pre-change version-1 signed token also omits it; resolve before expiry using same isolated secret | Affected optional schema must accept both; output keeps cwd/url/expiresAt and no repository | **Mandatory real RPC/service transcript**; capture base token then resolve on head; no still | Mint omitted resource with base or use authentic base-minted token transferred only inside sandbox; reuse isolated signer config for upgrade. Do not treat a newly generated head token alone as proof of old capability compatibility. **Planned, real runtime capable** |
| S3 — contract and signing boundaries | Omitted valid; whitespace-only invalid; max 512 valid / 513 invalid; mutate B→C inside payload without re-signing; invalid external-host URL rejected | Changed field validation/signature includes repository; invalid request has no usable capability | **Mandatory concise service/schema transcript**; expiry sequence, if added, is **recording** with controlled clock | Actual AssetCreateUrlInput decode and issue/resolve services; mutate only synthetic token, never print signing key. Boundary decode invalid must not proceed to mint. **Planned, isolated harness capable** |
| S4 — unchanged fetch policy | Identical url/range request from asset without repo vs B/C; synthetic source-control driver returns same bytes/status/type; accept-ranges + 206 | Control: this PR forwards only URL/range headers; repository is not consumed | **Mandatory short runtime transcript**; no real GitHub media claim | Actual sourceControlMediaResponse with fakeSourceControlDriverInstance + test HTTP response, patterned on existing AssetAccess test; compare fetch arguments/status/type/body. **Planned, labelled server harness** |

## Minimum evidence bundle to hand to the lead

1. **Matched visual pairs:** W1/W2 combined body at WD, W3 at WL and WN, at base and head with identical synthetic detail, bytes, timestamps and geometry. Six stills total if the body is arranged compactly enough to show images plus both video renderer types. If it does not fit, add one matched scrolled pair rather than shrinking media until the check becomes meaningless. Before/after pictures must be labelled with revision, viewport, theme, and mocked boundaries. The successful media GET fixture is identical on both revisions.
2. **One or more recording sequences:** W4–W8 and W10, labelled by producer. Do not use a still to claim a hidden-tab, retry, editor-preview, expand/collapse, or refresh transition. Combine footage where practical; a separate still per matrix control is not required. W9/W0 control transcripts can be attached to this sequence; W11 is optional.
3. **Live asset wire/service log:** S1/S2 show exact client request body (repository B), normalized signed payload fields, signature-verified resolved object, and omitted-field request plus genuine base-minted legacy capability accepted on head. Link each image/video producer to its request using synthetic correlation IDs. The same-repo case may share the protocol fixture; differing repo is required to disprove accidental checkout derivation.
4. **Server controls:** S3/S4 concise transcripts, with actual schema/signature/fetch-response functions and fake upstream transport clearly named.
5. **Mobile resource/identity log:** M1–M6 with actual wrapper/helper/hook code, labelled native stubs. This is required even though no native screenshot is required. It may reuse S1/S2 for signing, but must first show the resources produced by mobile, not manually reconstruct them from the spec.

A trace only constructed by the driver cannot prove renderer propagation. A successful service unit test cannot prove WebSocket decode. A browser GET fulfilled with fixture bytes cannot prove upstream authentication. Keep these boundaries distinct in evidence labels.

## Seeding and capture procedure

- This pass did not start a server, browser, device, lease, or test runner, and made no change in the target worktree. Current available disk was 18 GB. All artifact work is under the assigned step scratch directory.
- Capturer should reuse a task-owned isolated dev server if available. Otherwise use the repository's normal dev runner with explicit sandbox home; keep database, secrets, logs, bundler cache, and harness files in the lead's scratch area as its read-only-worktree policy requires. Do not use live T3 userdata or set VITE_HTTP_URL/VITE_WS_URL. Capture real ports from the runner. Do not publish startup pairing links/tokens.
- To seed an actual project, use normal project creation RPC/UI with a synthetic checkout. PR route is `/pull-requests` with environmentId, projectId, repository, number and host search parameters; use the same target via thread PR surface. This creates navigation/context only; it does not make arbitrary PR details exist.
- For a deterministic fake detail, use the existing `PullRequestDetailPanel.permissions.browser.test.tsx` fixture shape as the starting point: provider, projectId/title, workspaceRoot, repository, number/title/body/url/author/viewer/state, branches/timestamps, counts, reviewers/labels/checks/comments/reviewThreads/commits, capabilities and viewer permissions. Provide actual `detail` through a stubbed usePullRequestDetail query boundary or fake SourceControlDriver provider in a scratch server harness. Include detail.repository=B even when the requested reference or checkout differs, to prove web's actual source. Populate activity/diff/pagination/guide queries for W5/W7. No persistent database editing or fake source-control mutation RPC is available for this purpose.
- Keep asset hooks real for the primary S1 wire proof. Connect the harness to the isolated server's ordinary asset RPC runtime. Stub only the PR detail reads. For successful visuals, intercept outgoing `/api/assets/...` responses to return fixed PNG/MP4 bytes (serve byte ranges correctly for video). Record the interception rule and do not describe those bytes as fetched by the server. If Browser panel tooling cannot install an interception rule, use a scratch server harness with `fakeSourceControlDriverInstance.fetchMedia`; label it. Such a harness still uses the actual issue/resolve/HTTP-response implementation, but is not an unmodified shipped dev-server upstream fetch.
- resolveAsset is not an RPC response field. To prove actual resolution, use a service driver with the same sandbox ServerConfig/ServerSecretStore as the live signer, or a diagnostic hook in a scratch harness process. Supply only the token/suffix and log the signature-verified result. Sharing the isolated signer between before/head permits the old-capability upgrade row; do not export its key.
- Source files for a base visual build can be materialized by the lead's capturer outside this read-only worktree according to the lead's checkout policy. This audit did not create or remove a checkout. Ensure the base is the supplied merge-base, not the upper stack's parent/head.
- Native capture, if later requested, needs a pooled device lease and `mobile:install` on the host, then Metro from the correct checkout. Main route has linking path `pull-requests/:environmentId/:projectId/:repository/:number`; URL-encode repository's slash. The exact app URL scheme comes from the installed client, so do not invent a t3 scheme. Normal in-app navigation to PullRequest then Discussion/Guide also works. Fake detail transport still must be supplied. Lease/tool availability is not verified in this pass because native evidence is optional.
- Every driver run writes a new timestamped directory; await explicit deferred/event milestones rather than sleeps. Do not upload bearer credentials, reusable auth tokens, signing keys, live data, or unredacted executable startup logs. Token strings needed for a legacy capability test stay in the isolated sandbox; published logs can show decoded synthetic fields and verification result with a stable token digest.

## Verification limits and status

Read all 12 changed files, the complete supplied diff, direct production callers, parser/classifier, asset hook/RPC dispatch, signer/resolver, fetch response, mobile galleries and preview-key consumers, route definitions, and existing relevant fixture patterns. No forge metadata refresh was needed for this fixed-head proof audit. No test, real browser, native client, or private GitHub access is claimed. Pass 1 has no missing producible acceptance row: all mandatory rows have a concrete live-runtime or labelled harness path. Capture success remains to be established in the next pass.

FRICTION: none

VERDICT: SUFFICIENT
