# Proof audit pass 1 — sidebar images outside workspace

Target: [#21064 on GitCafe](https://git.cafe/pingdotgg/t3code/pulls/21064), [#21064 on GitHub](https://github.com/pingdotgg/t3code/pull/21064); regression [#21067](https://github.com/pingdotgg/t3code/issues/21067).

Read-only code audit, not executed proof. No server, browser, live userdata access, source edits, test execution, push, comments, merge, or watch in this pass. Only this matrix was written, outside the repository. Worktree: `/home/ubuntu/wt/pr-21064`, branch `takeover/pr-21064`.

- After: [992698e28c](https://github.com/pingdotgg/t3code/commit/992698e28c3234a58c048aae77fb6a9aecb2e1b5).
- Before: merge base with origin/main, [3cc31ed27c](https://github.com/pingdotgg/t3code/commit/3cc31ed27c8956b36cee35a50509fb7c6869592b).
- Original contributor: [trahane/t3code](https://github.com/trahane/t3code), `fix/sidebar-images-outside-workspace`, [31c5a73eb0](https://github.com/trahane/t3code/commit/31c5a73eb08d114a3ded38ab0aa74af498339be9), base main.

## Code-derived scope

Full base-to-head diff: 3 files, 29 additions / 4 deletions. `FilePreviewPanel.test.ts` adds resource regression assertions; `filePreviewMode.ts` moves outside-root selection into `workspaceAssetResource`; `FilePreviewPanel.tsx` removes the duplicate selection from `WorkspaceBrowserPreview`. No server/contracts/auth/permissions changes. This is resource selection, not a layout change.

Sources at audited head: [resource helper](https://github.com/pingdotgg/t3code/blob/992698e28c3234a58c048aae77fb6a9aecb2e1b5/apps/web/src/components/files/filePreviewMode.ts#L64), [four callers and render branches](https://github.com/pingdotgg/t3code/blob/992698e28c3234a58c048aae77fb6a9aecb2e1b5/apps/web/src/components/files/FilePreviewPanel.tsx#L155), [sole panel caller](https://github.com/pingdotgg/t3code/blob/992698e28c3234a58c048aae77fb6a9aecb2e1b5/apps/web/src/components/chat/RightPanelSurfaceContent.tsx#L185), [path classification](https://github.com/pingdotgg/t3code/blob/992698e28c3234a58c048aae77fb6a9aecb2e1b5/packages/client-runtime/src/mediaReference.ts#L33), [existing direct Markdown image resource](https://github.com/pingdotgg/t3code/blob/992698e28c3234a58c048aae77fb6a9aecb2e1b5/apps/web/src/components/ChatMarkdown.tsx#L1461).

| Caller / branch | Before resource → after resource | Impact / proof allocation |
|---|---|---|
| Persisted image, outside root | workspace-file → media-file | Affected: primary regression, full width/theme grid |
| Persisted image, inside root (relative or absolute spelling) | workspace-file → workspace-file | Unaffected: full grid workspace PNG control |
| Persisted HTML/PDF, outside root | media-file → media-file | Routing refactored: separate iframe and native PDF embed settled views |
| Persisted HTML/PDF, inside root | workspace-file → workspace-file | Routing refactored: same two renderers, paired representative controls |
| Persisted video/audio, inside or outside | media-file → media-file | Helper callers, unchanged result: one combined playback recording per revision |
| Draft, any of four callers, inside/outside | draft-workspace-file → draft-workspace-file | Early return before classification; one image-based draft recording per revision, resource trace checks for other kinds |
| Image URL pending / signing failure / img decode failure / success | Existing spinner / error / error / image | Core recordings cover natural pending-to-terminal transitions; before outside error and after success are primary proof. Separate labelled failure harness below |
| Browser URL pending / failure / success | Existing spinner / error / frame | Refactored selection preserves result. Settled iframe/embed pairs plus labelled failure harness; no artificial loading-duration claim |
| Ordinary text / Markdown / CSV / editable source | Does not call asset helper | Non-image text full-grid control; other text renderers unchanged and collapsed |
| Folder, Files explorer, host-folder tree | No asset helper until file selected | Real app recording starts from Markdown folder link; file tree selects host absolute path |
| Attachment surface | AttachmentFilePreview bypasses helper | Unchanged, code audit only; do not substitute an attachment for PNG proof |
| No selected file / workspace-root path | Explorer branch, no preview resource | Unchanged, code audit only |

`RightPanelSurfaceContent` is the sole panel caller, for `files` and `file` surfaces. It passes draft identity via `composerDraftTarget`; the panel uses `typeof composerDraftTarget === "string"`. Folder link → read reports not-file → host-rooted tree → selection calls `onOpenFile(panelPath(selectedPath))` → same image branch. Direct file link, Files tree/file picker and existing file tabs converge on this surface; they do not warrant repeated full grids. Direct Markdown image/expanded viewer bypasses this helper and is not evidence of the fix. HTML's separate “Open file in preview browser” also bypasses this panel helper and cannot substitute for its iframe.

Web and desktop use this same web panel. Width 390 is responsive web, not native mobile. Native mobile, providers, MCP, scheduler, contracts and serving policies do not change; no separate surface/provider/transport capture grids. Keep environmentId and threadId tied to the test environment; do not use parent thread binding.

## Named fixture data (synthetic only)

All created fixture/state/script/output files must live below `/tmp/ev-21064`. No copying or reading `~/.t3/userdata`, no migrate-dev-db. Do not use an existing `/tmp/screenshots` folder: use its isolated equivalent below, label it as the issue-equivalent path.

| Name | Path / data | Purpose |
|---|---|---|
| PROJECT | `/tmp/ev-21064/project` — tiny synthetic git repository, project name `PR 21064 image proof` | Real project cwd; not the implementation checkout |
| OUTSIDE_PNG | `/tmp/ev-21064/screenshots/example.png` | PNG, 960×540, opaque labelled card `OUTSIDE WORKSPACE`, colored quadrants, visible corners. Actual PNG bytes, not renamed SVG |
| INSIDE_PNG | `/tmp/ev-21064/project/assets/workspace.png` | Same dimensions, distinct `INSIDE WORKSPACE` label/colors |
| TEXT | `/tmp/ev-21064/screenshots/notes.txt` | UTF-8: `PR 21064 non-image control\nOutside workspace text remains readable.\n` — read-only host text control |
| INSIDE_HTML / OUTSIDE_HTML | `PROJECT/docs/control.html` / `/tmp/ev-21064/screenshots/control.html` | Self-contained HTML card labelled `HTML CONTROL`, no external resources, neutral fixed colors, input prefilled `preserve me` |
| INSIDE_PDF / OUTSIDE_PDF | `PROJECT/docs/control.pdf` / `/tmp/ev-21064/screenshots/control.pdf` | Actual one-page PDF labelled `PDF CONTROL`, visible geometry; identical bytes inside/outside |
| VIDEO / AUDIO | `PROJECT/media/control.mp4`, `control.wav` and copies in screenshots folder | 2-second video with visible frame counter and short audible tone WAV; no external URLs |
| MISSING / CORRUPT | `/tmp/ev-21064/screenshots/missing.png` absent; `corrupt.png` contains invalid image bytes | Failure harness only; not a policy/permission probe |
| HISTORY | One persisted stopped thread `Outside image proof` with synthetic assistant Markdown below | Required: persisted thread ID resolves project workspace; not an unsent draft |
| DRAFT | New unsent composer draft for same project | Explicit early-return control, real draft identity |

Synthetic assistant Markdown:

```markdown
[Issue-equivalent screenshots folder](/tmp/ev-21064/screenshots/)
[Workspace folder](assets/)
[Workspace image](assets/workspace.png)
[Text control](/tmp/ev-21064/screenshots/notes.txt)
[Outside HTML](/tmp/ev-21064/screenshots/control.html)
[Inside HTML](docs/control.html)
[Outside PDF](/tmp/ev-21064/screenshots/control.pdf)
[Inside PDF](docs/control.pdf)
```

Do not use `![image]` as the regression entry point: that renders the existing media viewer and can produce a false pass.

## Exact minimum capture set

Use fixed height 900 CSS px, DPR 1, browser zoom 100%, widths **1440** and **390**. Four cells: `1440-light`, `1440-dark`, `390-light`, `390-dark`. Use explicit light/dark in Settings → Appearance (no system-auto), default theme halves and matching font/scale settings on both revisions. Keep panel width and explorer preference equal for before/after; do not restyle the app to fit 390. At 390 capture the actual responsive panel, including any native PDF viewer limitation.

| IDs / fixture | Revisions | Cells | Capture type / exact number | Expected result / acceptance |
|---|---|---|---|---|
| S01–S24: OUTSIDE_PNG, INSIDE_PNG, TEXT | Before + after | All 4 | **24 stills**: 3 files × 2 revisions × 4 cells | Before outside PNG shows existing load failure; after shows labelled image. Inside PNG shows identical image before/after. Text contents and read-only rendering unchanged. Include active file tab/path and app chrome, not just image pixels |
| R01–R08: folder → outside PNG → workspace PNG → text → close/reopen outside file tab | Before + after | All 4 | **8 recordings**: one continuous sequence per revision/cell | Begin with visible rendered Markdown folder link, click to host tree, select example.png, hold terminal result; visit workspace folder/select image; open text; close and reopen outside tab. Shows routing, loading, switching/reversal, correct active file, no stale preview. Natural loading may be too brief to see; do not add sleeps/delays or claim loading performance |
| S25–S28: OUTSIDE_HTML, INSIDE_HTML | Before + after | 1440-light only | **4 stills** | Both show HTML CONTROL inside file panel iframe. Tests both sides of relocated browser routing. Do not open integrated Browser panel |
| S29–S32: OUTSIDE_PDF, INSIDE_PDF | Before + after | 390-dark only | **4 stills** | Both use existing native PDF embed behavior, same before/after. If browser cannot render native PDF, explicitly record identical limitation; labelled harness cannot claim real PDF success |
| R09–R10: DRAFT image inside → outside → inside | Before + after | 390-dark only | **2 recordings** | Same draft resource and displayed images on each revision; no thread persistence/promotion during sequence. If real draft path cannot be reached, use clearly labelled component harness and report that limitation |
| R11–R12: VIDEO/AUDIO inside → outside, play/pause each | Before + after | 1440-dark only | **2 recordings** | Same media-file selection and playable output; both helper callers covered in one sequence per revision. Record motion/audio, stills cannot prove playback. If sound capture unavailable, explicitly say video demonstrates controls/frame advance and audio decoding/playback needs trace evidence |

**Total mandatory visual artifacts: 32 settled stills + 12 recordings.** Stills can be losslessly extracted from held settled portions of the matching recordings; preserve the source video and timestamps. No additional theme/width cross-product for browser, drafts or AV: their resource outcome and renderer are unchanged. Use before/after labels in filenames, e.g. `after-390-dark-outside.png`; never call an after-only screen “before”. All interaction/time claims above require the recordings, even when stills are also extracted.

Resource/edge audit supplement (not extra visual artifacts): capture the asset-create request resource tuples into a sanitized transcript alongside the visual evidence. Persisted outside image after must be `media-file`, inside image `workspace-file`; browser outside `media-file`, inside `workspace-file`; AV `media-file` regardless of location; draft all four kinds `draft-workspace-file` with synthetic cwd/path. Do not expose signed URLs, pairing URLs, auth headers or tokens. A screenshot alone cannot prove the resource tag.

For missing/decode failures and controlled pending states that cannot be seeded/reached reliably in a normal session, use a **labelled component/asset-state harness**, not a server patch: image pending→URL failure and pending→successful URL→img decode failure; browser pending→URL failure; draft HTML/PDF/AV resource tuple inputs. Retain machine-readable assertions/trace, no extra screenshots needed because these unchanged fallback renderers add no distinct fix claim. If presenting any timing/transition from this harness, it must be a recording and is additional to the minimum. Harness must use actual helper/caller code, not a hand-drawn imitation. No retries/auth/permission/serving modifications. Path normalization (absolute inside, `..`, sibling-prefix, Windows drive/UNC case rules) is existing media-reference logic; inspect/assert tuples, not a Linux screenshot matrix. Symlink serving rules stay untouched and are not a new feature claim.

## Efficient seed and capture handoff

This is guidance for the later authorized capturer; none was executed in this pass.

1. Every terminal command explicitly starts `cd /home/ubuntu/wt/pr-21064`. Keep all scripts under `/tmp/ev-21064`. Start isolated runtime with `T3CODE_DEV_SEED=0 vp run dev --no-seed --home-dir /tmp/ev-21064/state-after` (equivalent `state-before` for baseline), never ambient/default home. Read actual ports from dev-runner output; no VITE_HTTP_URL/VITE_WS_URL. Avoid `--share` unless requested. Retain spawned PID/session; never kill by pattern.
2. Let first isolated start perform migrations. Create the PROJECT and a persisted stopped thread through app commands/UI, without running a provider. Runtime route is `/<environmentId>/<threadId>`; use actual isolated IDs. Draft route `/draft/<draftId>` is separate. Pair only this isolated app; never navigate using parent T3 environment/thread IDs.
3. For assistant-history fixture, stop only the owned runtime, inspect current table columns, and use `apps/server/scripts/t3-sqlite-state.ts` with explicit `--base-dir /tmp/ev-21064/state-after` and SQL files under `/tmp/ev-21064`. Prefer the app-written fresh project/thread rows as schema templates. Client project rows are `projection_projects`; V2 history is `orchestration_v2_projection_*` with schema-valid `payload_json` matching `packages/contracts/src/orchestrationV2.ts`. Never seed old V1 projection tables; never fabricate a backend event-history claim from projection-only history. Mark synthetic assistant row seeding in evidence. Do not seed provider sessions, tasks, pending turns or effects. Restart only isolated app.
4. Reuse synthetic fixture bytes and a stopped **isolated** seed snapshot for before/after (copy only within `/tmp/ev-21064`, reset client panel preferences identically). Do not manufacture a before via a forced asset failure. Real baseline must run the resolved base commit. This pass's worktree-only/no-repo-edits constraint means the lead must arrange baseline execution separately; capturer must not reset/checkout this replacement worktree or create a second checkout unasked. If baseline is unavailable, report missing before proof instead of relabelling a harness.
5. Core sequence can capture all three settled states in one video per grid cell, avoiding repeated seeding/startup. The folder click is essential: absolute folder links initially enter the file surface, the not-file result creates a host-rooted tree, and file selection supplies the host absolute path. Inside PNG should be selected through workspace tree; source-panel text uses host read-only path. This exercises `files` and `file` surfaces without redoing direct-link/file-picker permutations.
6. `t3code.renderBrowserFile` defaults true: ensure it is true for HTML; PDF always renders as browser file. Do not confuse source mode or integrated Browser action with WorkspaceBrowserPreview. `t3code.fileExplorerOpen` affects inside image layout; freeze it consistently. Rendered assistant Markdown already exposes folder links; `t3code.renderMarkdown` only controls file Markdown previews and is unnecessary for primary repro.
7. Draft seeding fallback: if normal draft Files flow cannot target outside file, mount actual FilePreviewPanel with string `composerDraftTarget`, isolated environment context and synthetic cwd/path; label capture HARNESS. A persisted thread merely labelled “draft” does not hit the draft early return. Inspect tuples for draft browser/AV instead of multiplying unchanged visual permutations.
8. Upload evidence with ghdrop, embed before/after PNGs and recordings in lead's report, include capture manifest (revision, fixture, viewport, theme, resource tag, source timestamp, real app vs harness). Lead owns all forge publishing. This matrix is a capture prescription, not a claim that any proof passed.

## Audit conclusion / friction

Code inspection finds no omitted helper caller and no server policy change in the full diff. Only persisted outside-workspace image behavior changes. Existing image error wording still says “workspace image” for host files; unchanged copy, not scope for this fix. Before/after runtime evidence remains entirely outstanding.

FRICTION: isolation | dev runner can auto-seed from live install | explicit --home-dir under /tmp/ev-21064 plus --no-seed and T3CODE_DEV_SEED=0.

FRICTION: baseline | before commit cannot run concurrently from unchanged after checkout under worktree-only/read-only scope | lead arranges baseline runtime separately; do not reset this worktree or fabricate a before capture.
