# Proof audit pass 1 — evidence matrix

Scope: [#21088 on GitHub](https://github.com/pingdotgg/t3code/pull/21088), [#21088 on GitCafe](https://git.cafe/pingdotgg/t3code/pulls/21088). Read-only source audit in `/home/ubuntu/wt/pr-21088`. Full seven-file replacement diff reviewed: `origin/main` 3cc31ed27c → `takeover/pr-21088` 0b086662f3. Original submission BGD-1DAY00/t3code `fix/video-missing-file-access` b2a0acbd2c62673486e21ebdb2b2d48cb69de9ab is provenance, not the before baseline. No execution, captures, tests, edits, comments, push, browser, server, watch, or delegation performed. This is a capture specification, not completed proof.

## Capture contract

Before = base 3cc31ed27c; after = replacement 0b086662f3. Use identical seeded content, client grants, scroll positions, viewport, theme and narrated actions on both revisions. Show whole web app, not isolated cropped players. Web widths 1440 and 390 CSS px; choose/document a fixed height (900 recommended) and device scale factor. Every required web row is captured in light and dark at both widths. Static states get paired stills. Interaction, time, playback and retry rows get paired recordings with the same spoken or captioned step narration. A still cannot establish absence of a retry loop or working playback. Do not mistake 390px web for Android verification.

Use stable synthetic fixtures, never publish real conversations or pairing credentials. Put three adjacent, clearly labelled messages in a real thread: `![Denied video](/tmp/ev-21088/fixtures/demo.mp4)`, a reachable video (direct URL to the same valid MP4 or a second environment with filesystem access), and `![Denied image](/tmp/ev-21088/fixtures/reference.png)`. Keep references in view together where possible; if they do not fit, use identical scroll positions in additional full-app stills. Denied file must EXIST; a nonexistent file produces the wrong failure. Include a nonempty descriptive label, plus empty-label assertion as noted below.

## Minimum visually distinct matrix

Legend: A = directly affected; R = regression/control; B/A = paired before and after. W4 = {1440×900,390×900} × {light,dark}. Android N = one actual portrait Android device viewport, record exact logical and physical dimensions; use light for black player/modal surfaces, dark only if surrounding theme introduces a distinct rendering. iOS U = explicitly unverified under dispatcher exception.

| ID | Affected | Fixture / changed branch and callers collapsed | Minimum viewport × theme × row | Evidence / same steps and expected after |
|---|---|---|---|---|
| W1 | A | ChatMarkdown local/workspace `media-file` video; Failure(missing-file-access), no fallback, src null. ChatMarkdownAssetImage → ChatMarkdownVideo → MediaVideoPlayer | W4 × W1, B/A | Still: video unavailable, label, exact pairing explanation; no Retry video. Long explanation wraps inside bounds at 390. Before has generic unavailable/retry. |
| W2 | R | Successful asset video; direct URL video converges to same player success UI | W4 × W2, B/A | Recording: show initial preview, play ≥2s, pause; no denied explanation and no unexpected unavailable state. Record one asset and one direct-source reachability assertion; share visual fixture where identical. |
| W3 | R | Denied host image; shared missingFileAccess/unavailableDetail refactor | W4 × W3, B/A | Still: existing image-specific unavailable state retains exact same pairing explanation; no video wording. May share W1 frame. |
| W4 | A/R | Generic asset failure WITHOUT reason; shared player generic unavailable branch, nonempty and empty label | W4 × W4, B/A | Still for text/layout; recording: click Retry video, show retry response (controlled generic failure or recovery). Pairing explanation absent; retry remains. Empty-label assertion checks no stray separators. |
| W5 | R | Source-control media with fallbackSrc on asset failure; fallback guard prevents denied detail and sourceFailed | W4 × W5, B/A | Recording: fallback video renders and plays; no pairing warning. A source-control resource is NOT naturally blocked by filesystem grant, so do not claim a real missing-file-access result for it. If synthetic missing reason is needed, label state injection separately. |
| W6 | A/R | Shared web player failed+onOpen versus failed without onOpen; no supplied unavailableDetail | 1440×900 × light × each distinct shell, B/A | Still: generic unavailable in file panel/PR; onOpen thumbnail remains preview-unavailable status/button, not the new alert. Recording: open and close preview. See caller inventory below; different feature shells get route smoke evidence, not duplicated W4 theme grids. |
| W7 | A | New data-thread-find-ignore on failed text | 1440×900 × light × W1/W4, B/A | Recording: open thread find, search unique label/explanation and navigate result; after must not count placeholder text as transcript content. Also verify authored matching text remains findable. Visual still alone cannot prove this interaction. |
| M1 | A | Android ThreadMarkdownVideo denied inline host video; shared MediaVideoPlayer no playbackUri | Android N × light × M1, B/A | Still: centered “Video unavailable. This connection cannot read host files. Pair again with a new link to view it.” No clipping. Accessibility inspection: name + unavailable explanation, disabled state; no misleading Play label. |
| M2 | A | Android markdown file VIDEO LINK opens media-resource VideoPreviewModal; uri null, connected, Failure(missing-file-access) | Android N × light × M2, B/A | Still of open fullscreen placeholder plus recording: tap link, show explanation, attempt tap on unavailable player, close, reopen, close. Same narration before/after. Save/share disabled without URI. Inline unavailable tile does not open this modal; use actual link. |
| M3 | A/R | Android generic failure and disconnected preview; no unavailableDetail | Android N × light × generic/disconnected, B/A | Still: generic Video unavailable, no pairing claim. Same generic visual collapses the two data causes; assert both states separately. Inline generic failure shares centered generic copy. |
| M4 | R | Android success/direct URI and local-draft preview; loading branch; retained playbackUri | Android N × light × each distinct loading/loaded shell, B/A | Recording: open reachable resource link, play, pause, close; open direct URL and local composer attachment; show loading → loaded; reopen. While already playing, temporary URL loss must not replace retained player with new unavailable message. No pairing text. |
| M5 | A/R | Other mobile shared-player shells: WorkspaceFileVideoPreview and PullRequestMedia; generic unavailable/available, no detail passed | Android N × light × each distinct file/PR shell, B/A | Still for centered generic unavailable state/accessibility label; recording for play or open/close when available. Route smoke assertions cover identical modal entry points below. |
| I1 | A | iOS NativeVideoPreview connected, resource, playbackUrl null, missing-file-access: alert then modal closes | iOS U | UNVERIFIED: “iOS simulator not available on Linux VMs”. Expected alert title Could not open video, pairing explanation, dismissal returns to thread. Android evidence does not verify this native path. |
| I2 | R | iOS disconnected precedence; generic failure; success/local/direct; retained playbackUrl | iOS U | Same explicit exception. Expected reconnect text when disconnected, generic load text otherwise; ready playback retained. Requires recording on future iOS verification, not a fabricated Android substitute. |

Mandatory requested core stills: W1/W3 can share one frame per W4 combination; W2 needs its own visible reachable player plus recording. Android M1 and M2 require distinct before/after frames. Extra static generic rows may share a seeded thread/screen, but cannot be silently omitted. Recordings may combine related rows with visible labels/timestamps and exact corresponding before/after steps. Do not count branch assertions as captures.

## Full-diff branch and caller accounting

- `ChatMarkdownMedia.tsx`: new optional detail forwards through ChatMarkdownVideo. `missingFileAccess` requires Failure + exact reason + no fallback; only that condition suppresses onRetry. Success, Loading/waiting, generic Failure and fallback remain separate data assertions. Image branch receives same derived detail (W3). Source fragments, authored size, label, original URL and media identity remain forwarded; include fragment/size smoke assertion without another fixture unless it changes layout.
- Web MediaVideoPlayer: optional detail adds alert text; conditional label changes separators; failed text is excluded from thread find. `failed && !onOpen` is the changed visual branch. Retry still appears for latestSrc nonnull OR onRetry; W1 removes both. onOpen branch and loaded/loading branches stay distinct controls. Original link and action menus remain available according to their existing inputs; test open/copy actions only where present, do not invent an external link for a local path.
- Shared web player callers: ChatMarkdownVideo; TimelineUserMessageRow attachment thumbnail; ExpandedImageDialog video; FilePreviewPanel; pullRequest/PullRequestMarkdown inline and asset video. Asset-image callers additionally include ChatMarkdown source-control and workspace media, TimelineWorkEntryRow, V2ItemInspector. The latter image-only callers collapse into W3; shell-only shared player variations collapse by branch into W6, with route smoke checks. Inspect these routes for unintended added separators or pairing text. No new provider/backend contract or desktop IPC path: desktop wraps these web branches. Local and remote must use destination environment grants; at least W1 must be a remote scoped connection, not an OS permission error.
- Mobile MediaVideoPlayer: detail changes visible text and accessibility label only in placeholder branch (no playbackUri). Centering affects generic unavailable too. Already-mounted LoadedMediaVideo branch bypasses placeholder and must remain playing across URL renewal/loss (M4).
- ThreadFeed image-style video embeds resolve through ThreadMarkdownVideo (M1), regular file video links through onMarkdownLinkPress → expandedVideo → VideoPreviewModal (M2). ThreadMarkdownVideo resource Failure marks unavailable; direct URI null-resource branch cannot naturally produce missing-file-access. Thumbnail visibility controls are unchanged; visible denied fixture suffices.
- Android VideoPreviewModal: media resource detail derives from exact failure reason; unavailable additionally requires uri null and environment present with missing connection or Failure. Loading (uri null + not unavailable), local attachment, direct URI, successful media and retained playback states are controls (M3/M4). No retry removal was added to mobile; the disabled press still permits long-press media actions. Check long-press opens existing actions and unavailable tap starts no playback (M1/M2 recording).
- All modal entry callers: ThreadFeed markdown link and attachments; ThreadComposer; QuestionAttachments; ComposerContextSheet; files/FileMarkdownPreview; pull-requests/PullRequestMedia. Resource-backed host file/draft sources can show denial, attachment and source-control resources are not blocked by blockedAssetUrlState. Local attachments use separate loader and direct URI sources bypass signing. Route smoke checks, then collapse matching resource-backed modal visuals into M2 and local/direct into M4. WorkspaceFileVideoPreview and PullRequestMedia use shared player directly (M5).
- iOS uses `.ios.tsx` instead of Android modal. New failure reason check sits AFTER disconnected check and BEFORE generic failure, only with resource and null playbackUrl. Alert effect closes modal; this is an interaction state, not a persistent fullscreen placeholder (I1/I2).
- Browser test adds a mocked denied-environment filesystem hook and one text/no-retry assertion. It supports intent only, not full-app, Android, wrapping, playback, fallback, or thread-find evidence. No tests were run by this audit.

## Real-app fixture route and feasibility

Real-app seeding is feasible; no harness-only fallback is justified by this audit. Lead should seed a synthetic thread through the isolated dev environment's supported orchestration path or stopped dev database fixture; use `.t3` isolated state, never run against live userdata. Open normal project → thread route on web and native Android thread screen. Start from base and replacement with same data. Existing migrate-dev-db setup can provide app context, but publish only synthetic thread content. Pair a second remote connection with explicit orchestration/access read grants and WITHOUT filesystem:read; retain enough access to view thread. Pair another with filesystem:read for reachable asset control, or use a controlled direct URL. CLI pair supports explicit repeated --scope (replaces defaults); inspect exact grant list rather than assuming defaults lack filesystem access. Actual blockedAssetUrlState only blocks workspace-file, media-file and draft-workspace-file; do not use attachment/source-control failure as denied fixture.

Generic signing failure, loading and transient renewal states may require a deterministic dev fixture/state injection. Prefer a labelled fixture mode within the WHOLE running app and clearly disclose injected states. A standalone component harness is acceptable only after documenting why the real app route is impossible; label every harness capture, and keep it distinct from integrated proof. This audit did not create a fixture mode or test feasibility by launching anything.

## FRICTION

- policy: Review brief prohibits browser/server execution; captures and fixture execution belong to lead. Matrix is derived from source, all evidence remains pending.
- infra: iOS simulator not available on Linux VMs (explicit permitted dispatcher exception); iOS is unverified.
- infra: Android SDK setup is owned by lead at /tmp/ev-21088/sdk. This audit neither reads nor modifies that directory.
- brief: “No edits” interpreted as no worktree edits; explicitly requested external matrix artifact written only to /tmp/ev-21088/matrix.md.
- tool: One bounded rg command used a nonexistent scripts/migrate-dev-db glob and exited 2; subsequent file discovery inspected available seed scripts. No conclusion depends on that missing path.

Valid category vocabulary: tool | brief | runbook | infra | forge | policy | model | other. No forge reads/writes or PR monitoring were needed.
