# Proof audit PASS1 — corrected exact-base round

Scope: [#22300](https://git.cafe/pingdotgg/t3code/issues/22300). Compared only `git diff 96bae5465bc5dfaedc890998a7a71672fb470ae2 9190a8e2ac`.
Before: [96bae5465b](https://git.cafe/pingdotgg/t3code/commit/96bae5465bc5dfaedc890998a7a71672fb470ae2), `/home/ubuntu/wt/issue-22300-current-main`, existing lead-owned port 5807, isolated thread `triage-22300-main`.
After: [9190a8e2ac](https://git.cafe/pingdotgg/t3code/commit/9190a8e2ac83e6061f2891cd3076e75bc84ceab9), `/home/ubuntu/wt/issue-22300`, existing lead-owned port 6369, isolated thread `triage-22300`.
Both checkout HEADs verified. Only FilePreviewPanel.tsx (+53/-3) and its new 93-line browser test are in scope. The cancelled stale-base matrix and unrelated merged mobile/tooling changes are excluded.

Verdict: matrix ready; evidence pending. This read-only audit did not drive browsers, start servers, edit code, run tests, or capture evidence. No claim of visual PASS. Prior reviewer verdicts supplied by lead: Codex MERGE/no defect; Claude FIX_FIRST solely for pending evidence. No additional code defect established by this proof audit.

## Capture contract

Every affected fixture below requires matched BEFORE/AFTER whole-app captures at 1440×900 and 390×844 CSS pixels, each light and dark (four variants per revision per checkpoint), plus a recording for every interaction/time row. Set identical panel width/maximized state, explorer state, seed bytes, initial scroll, theme, zoom, DPR, fonts and frozen clock on both revisions. Fresh browser document per fixture/variant clears the module-level cache; do not rely on localStorage clearing to reset it. Preserve cache within each sequence. Use actual application, not the mocked component test.

Record browser/version, OS, DPR, font family/files and readiness, CSS viewport, panel dimensions, zoom, frozen ISO time, source commit and fixture/seed manifest/hash. Clock freezing must leave layout, requestAnimationFrame and controlled asset completion functional. Record actual action timestamps rather than inventing elapsed times. Suggested recording markers: T0 seeded/start, T1 initial 1800, T2 switch/source/identity change, T3 return/restore, T4 delayed asset completion or takeover, T5 final settled position. Capture pre- and post-interaction whole surfaces and numerical viewport scrollTop/scrollHeight/clientHeight alongside the video. Use ±1 CSS px for scrollTop acceptance (implementation restores when difference is <1); this is independent of pixel-diff tolerance.

For every paired checkpoint deliver Diff0 (zero-tolerance pixel difference, amplified for visibility) and DiffT with declared threshold: flag a pixel when maximum RGB channel difference >16/255, report pixel count and fraction. Threshold is a diagnostic, not an automatic pass: inspect every changed region. List “what else changed,” including sidebar/thread titles, worktree labels, wrapping, clocks, loading, selection/focus and scrollbars. Keep intended restored-content differences separate from unrelated surface noise, but do not crop them out. Same revision/control checkpoints should show stable chrome; intended terminal regression differs in Markdown body because after restores 1800 while before resets 0. Timing recordings must show any transient jump, not just final images.

Capturer owns committing reproducible seed/fixture definitions if needed, not PR-only PNG/video/diff assets. Upload evidence externally. This audit creates only this /tmp artifact. Existing data must be inspected read-only; any later fixture preparation belongs to the lead in isolated sandbox state, never live userdata.

## Fixture matrix

Common real-app path S: use the existing isolated thread on each origin; resolve its actual scoped thread/environment reference and workspace root; use `useRightPanelStore.getState().openFile(ref, path)` through the app module (or the Files explorer/chat file link that calls it). Enable rendered Markdown with the real source/preview toggle, hide explorer unless specified, keep two file tabs. Do not substitute synthetic DOM for the panel. Each row below states its additional real-app seed path. All rows are affected and require the four width/theme variants on both revisions. All evidence remains PENDING.

| ID | Changed branch/caller and affected state | Real-app seed path | Interaction/checkpoints and required outcome | Recording |
|---|---|---|---|---|
| F1 | Affected: ordinary mount, cache miss/hit, scroll listener, file-key remount; primary regression | S; existing workspace AGENTS.md and README.md, confirm both are long enough for scrollTop 1800 at each viewport; freeze identical file bytes | AGENTS.md at 1800 → README.md first visit 0 → AGENTS.md. Before returns 0; after returns 1800. Then set README to a distinct offset (e.g. 900), revisit both to prove independent entries and no inherited offset. Record exact viewport metrics. | REQUIRED; full sequence and settled checkpoints |
| F2 | Affected: rendered subtree unmount through source preference and line-reveal gate; alternate source branches | S; same AGENTS.md; read-write sandbox for editable source, plus existing read-only linked scope or host Markdown for read-only source subcase | At rendered 1800 toggle source → preview; restored 1800. Separately call openFile(ref, 'AGENTS.md', 80): source reveals requested line despite saved rendered offset; explicit preview toggle restores rendered offset. Verify source state is not overwritten. Collapse editable/read-only into subcases of one fixture, not separate content matrices. If no read-only connection/host seed is available, mark that subcase cannot-verify. | REQUIRED; both transitions and reveal |
| F3 | Affected: JSON [environmentId,cwd,path] identity; parent workspace key; module cache shared across threads | S; second sandbox workspace with identical relative AGENTS.md and distinguishable heading/bytes; second linked environment with same cwd/path if already available; second thread in same environment/cwd | Save A=1800, switch to B with same relative path: first visit 0, save B=900, return A=1800 and B=900. Repeat environment collision subcase where available. Same environment/cwd/path in another thread intentionally shares 1800 (thread ID is not key). No workspace/environment data bleed. Missing second environment is cannot-verify, not implied coverage from cwd. | REQUIRED; identity and return markers |
| F4 | Affected: restoration initially clamped, observeResize retry, stop at target; content/layout timing | S; lead prepares sandbox Markdown fixture with a local authenticated image lacking reserved height plus long tail; same bytes/assets on both; controlled delayed response via capture harness, not server edits | First allow full content, save 1800, switch away; remount with image withheld so max scroll <1800; release asset after T3. After must reach 1800 as child height grows without overwriting saved value. Also return away while clamped, then revisit loaded content: original 1800 remains. No user action during retry. A cached image that never clamps is invalid proof. Math/code settling can share this row; one demonstrated actual height-growth path suffices. | REQUIRED; clamped metrics, release timestamp, final restore |
| F5 | Affected: restoring=true user takeover and listener removal on root, including scrollbar sibling | Same real-app delayed-layout seed and controlled response as F4; fresh document/subcase | While target is clamped, exercise separately wheel, touchstart (touch-capable browser context), pointerdown on actual scrollbar/thumb, and keyboard navigation with viewport focus; move to reachable user offset U. Release delayed image: no snap back to old 1800; switch away/back restores U. Mere synthetic dispatch proves listener wiring only; use real input where possible. Mark unavailable touch/scrollbar automation cannot-verify. | REQUIRED per input subcase; show input, U, release, revisit |
| F6 | Affected: cleanup save before pending scroll event; detached/observer cleanup | S; ordinary long AGENTS.md/README.md; no new data required | Establish restoration complete; in one browser evaluation/action task set real viewport scrollTop=1800 and invoke store.openFile(ref,'README.md') without dispatching scroll or awaiting a frame; return AGENTS.md → 1800. Instrument timing/scroll events in harness transcript to prove pending-event premise; recording alone cannot establish same-frame order. Also switch away during F4 pending resize and release old asset: inactive view must not move active README or emit post-unmount effects. | REQUIRED + same-task transcript; cleanup late-release may reuse F4 recording |
| F7 | Affected: same rendered surface content update and layout changes after restoration stopped | S; lead sandbox fixture with task checkbox and sufficient Markdown; explorer available through real Files surface; seed identical text | At restored 1800, show/hide explorer (width/wrapping), resize/maximize panel, toggle checkbox where writable, and refresh content through existing app refresh path. No old-target snap after user scroll to U; normal browser clamping for shorter content is acceptable. Close/reopen file tab or right panel, then revisit: preserve current reachable offset while page remains loaded. Do not require survival across full reload or >200-entry eviction. | REQUIRED; transitions and stable endpoints |

## Scope derivation and collapsed branches

The sole production change is the keyed RenderedMarkdownSurface: layout effect acquires ScrollArea viewport, cache get/default 0, immediate success vs resize-assisted restoration, scroll saving, four takeover event types, and connected cleanup. Key now includes environment and cwd as well as relative path. F1–F7 cover these branches without multiplying every content type/theme/entry point into a new behavior fixture.

Caller chain: ChatView → RightPanelSurfaceContent (`file`/`files`, workspace/environment parent key) → FilePreviewPanel → rendered Markdown when path/data exist, isMarkdown and renderMarkdownPreferred and revealHandled. Files explorer, breadcrumbs, Markdown file links and direct store open converge on openFile/activation; use F1 with real tab clicks and one Files/link entry to establish integrated wiring. No Settings/command palette/provider/backend/API/schema feature changed. F2 covers the revealLine condition and both source alternatives. Read-only and host Markdown still render the changed component; attachments go to AttachmentFilePreview instead. Loading/error/folder/no-read-access paths do not mount it: F4 uses a real loading-to-content transition, no need for separate screenshots of every unchanged spinner/error. Media, PDF/HTML, CSV/table and generic source are unchanged siblings; switching to README/source already establishes the affected boundary. No independent broad media matrix.

Rendered task changes affect content without remounting; F7 covers that input path. LRU is bounded to 200 positions for the current document lifetime; eviction and full reload may reset to 0 and are not promised persistence. Pure cache unit enumeration is not a visual fixture. No provider-specific cases: scroll state is frontend-local. Local/remote/relay use the same component; second-environment identity and delayed authenticated assets are the relevant differences, not a provider/transport cross-product.

## Surface and width disposition

Web: both locally hosted and hosted/remote client run this component; supplied localhost origins are lead-owned integrated targets. Remote connection behavior is not verified by this audit. Desktop: shares web component, but native Electron is unavailable on this Linux setup per brief; native desktop = CANNOT-VERIFY. Browser proof must not be labeled native desktop proof. Mobile: native React Native has no shared FilePreviewPanel, NOT AFFECTED; 390 is narrow web, not native mobile verification.

390 CSS px is required, not automatically unsupported: ChatView supplies the same content to ChatRightPanel with responsive sheet presentation. Capture the real narrow sheet and tab controls; hide explorer for core rows so its min-width does not invalidate available space. In F7 explicitly capture explorer behavior. If a row cannot be reached at 390, preserve the whole-surface failure and name the precise UI constraint; do not replace 390 with 1440 or assert the component is desktop-only. Heights above are declared capture defaults, not preexisting verified browser settings.

## Proof gaps and handoff

The new mocked browser test covers F1's basic AGENTS 1800 → README 0 → AGENTS 1800 only. It does not establish source/reveal transitions, workspace/environment identity, same-frame cleanup, late layout, user takeover, whole-app integration, narrow layouts, theme parity, or Electron. Test presence is source-inspected; no test execution claimed. Existing prior code verdicts do not close these evidence gaps. Lead should capture core F1 first, then F2–F7; record explicit cannot-verify subcases instead of silently widening PASS. No evidence URLs supplied or discovered in this read-only round.

Friction | problem | prevention
--- | --- | ---
Scope | Prior cancelled brief used stale base and included unrelated changes | Pin both full SHAs and verify two-file diff before deriving matrix
Evidence standard | /home/ubuntu/t3code-ops/docs/EVIDENCE.md absent per brief | Use the supplied operational standard explicitly; do not claim to have read the missing file
Native capability | Electron unavailable; environment/touch subcases may need infrastructure | Mark cannot-verify precisely; never infer native or cross-environment coverage from browser/cwd tests
Timing proof | A screenshot or synthetic scroll event cannot prove pending-event cleanup or user takeover | Pair real-app recordings with action timestamps, viewport metrics and same-task transcript
