fix(web): paused sidebar videos keep their place during commands#23127
Problem
A local MP4 paused in the sidebar reloads when unrelated read-only agent commands complete. A video paused at five seconds resets to the first-frame preview even though its bytes have not changed.
Change
Return an optional revision from the media file's existing open-file stats and use it for the sidebar video's revision. Keep the loaded video source across unchanged revisions and URL re-signing, while retaining real-file updates, active-playback deferral, retry, and stale-source recovery. Older servers retain the existing workspace-change fallback. HTML, PDF, audio, and image refresh behavior is unchanged.
Scope and approval
Fixes #23089 GitCafe: https://git.cafe/pingdotgg/t3code/issues/23089 Triaged bug via the automated issue flow. Reproduced in the real web app on Linux using a fake provider that executes two actual read-only shell commands. The fix changes media refresh state and optional result metadata; signing claims and authorization are unchanged.
Verification
- Red-first Chromium regression failed on main: a new URL for unchanged paused media replaced its loaded source. It passes on the fixed head, including stale-source fallback and changed revisions.
- Whole focused files:
AssetAccess.test.ts(25), client-runtimeassets.test.ts(1),FilePreviewPanel.test.ts(13), andMediaVideoPlayer.browser.test.tsx(1): 40 tests pass. - Scoped typechecks: contracts, client-runtime, server, web: pass.
- Targeted lint: pass with one pre-existing
no-inline-schema-compilewarning outside the changed test. Scoped Knip exports check andgit diff --check: pass. - Two independent code reviews: Codex GPT-6.1-Sol and Claude Opus 5.5 returned
MERGE; evidence is checked separately below.
Independent proof audit: SUFFICIENT against the six-row matrix. Full evidence index. Before is merge-base 968dd51704441138559a3fe9c348719f2b11e0c6; after is d8d674784e4279e847b450e5908dc29db17a6be0. Real Linux web app, Chrome154, DPR2, 1440×900 and390×844, light and dark. Separate worktrees and isolated state; same MP4 and scripted commands.
Core result: unchanged paused video remains at5s with zero emptied/loadstart events after four read-only turns; baseline resets to0.1s. Real edits and atomic replacements update the preview; active playback defers the update until pause.
Limitations: desktop/Electron and native mobile were not run. Signed/direct PR video callers, ended release, true signed-token expiry/retry, older-server no-revision fallback and remote/tunnel propagation are runtime-unverified. R5 has focused object-URL test coverage; R6 has no dedicated compatibility test. Caller desktop-light frames differ despite equal paused2s event state; no decoded-frame equality is claimed. These gaps were disclosed to the proof auditor.
R1–R3 recordings and full-viewport milestone sheets
Every row below covers R1, R2 and R3 as chapters in the same recording. SHA under Before is 968dd51704441138559a3fe9c348719f2b11e0c6; under After is d8d674784e4279e847b450e5908dc29db17a6be0. The sheets stack four whole frames: initialized, after unchanged paused5 commands, changed-file refresh, active-play edit. They supplement the recordings; a still cannot prove reload absence.
| Settings / R1–R3 | Before | After | Diff |
|---|---|---|---|
| 1440×900 light | before-1440-light.mp4 before-1440-light-sheet.png 968dd51704441138559a3fe9c348719f2b11e0c6 | after-1440-light.mp4 after-1440-light-sheet.png d8d674784e4279e847b450e5908dc29db17a6be0 | core-1440-light-diff0.png core-1440-light-diff16.png |
| 1440×900 dark | before-1440-dark.mp4 before-1440-dark-sheet.png 968dd51704441138559a3fe9c348719f2b11e0c6 | after-1440-dark.mp4 after-1440-dark-sheet.png d8d674784e4279e847b450e5908dc29db17a6be0 | core-1440-dark-diff0.png core-1440-dark-diff16.png |
| 390×844 light | before-390-light.mp4 before-390-light-sheet.png 968dd51704441138559a3fe9c348719f2b11e0c6 | after-390-light.mp4 after-390-light-sheet.png d8d674784e4279e847b450e5908dc29db17a6be0 | core-390-light-diff0.png core-390-light-diff16.png |
| 390×844 dark | before-390-dark.mp4 before-390-dark-sheet.png 968dd51704441138559a3fe9c348719f2b11e0c6 | after-390-dark.mp4 after-390-dark-sheet.png d8d674784e4279e847b450e5908dc29db17a6be0 | core-390-dark-diff0.png core-390-dark-diff16.png |
Actual unchanged-stage event counts (after initial load, four read-only turns):
| Settings | Before emptied / loadstart | After emptied / loadstart | Paused position before → after |
|---|---|---|---|
| 1440-light | 16 / 10 | 0 / 0 | 0.1s → 5s |
| 1440-dark | 16 / 8 | 0 / 0 | 0.1s → 5s |
| 390-light | 16 / 8 | 0 / 0 | 0.1s → 5s |
| 390-dark | 16 / 8 | 0 / 0 | 0.1s → 5s |
The baseline event totals include intermediate source churn and canceled/coalesced loads from completed commands and checkpoints. They are observations from these runs, not a claim of exactly two loads per command. All final after traces retain source-1 through R1; actual changes adopt sources2/3/4, and source4 persists throughout active playback until pause releases it to source5. Stat hashes remain original across the unchanged stage.
R4 caller recordings
R4 sweeps start before the user upload/send, then local and remote markdown playback/seek2/pause, attachment offscreen→visible, expanded autoplay/seek2/pause/close/reopen. The attachment remains muted, controls=false, preload=metadata; markdown players controls=true. Draft recording opens Files→.t3/fixture.mp4 in an unsent draft and seeks/pauses2s. Draft smoke is one equivalent layout at1440/light as the matrix permits. PR container variants cannot be certified by these chat callers.
Core chapter index (visible caption clock)
| Recording | paused0 commands | seek5 / paused5 commands | in-place / atomic edit | active edit / pause |
|---|---|---|---|---|
| after-1440-dark | 2.4s, 7.5s | 12.39s, 12.64s, 17.05s | 21.61s, 34.01s | 39.3s, 44.43s |
| after-1440-light | 2.09s, 6.71s | 11.35s, 11.62s, 16.2s | 20.77s, 30.86s | 35.97s, 41.13s |
| after-390-dark | 2.24s, 6.95s | 11.35s, 11.56s, 16.01s | 20.67s, 30.25s | 35.32s, 39.83s |
| after-390-light | 2.17s, 6.67s | 11.13s, 11.32s, 16.25s | 20.76s, 31.48s | 36.43s, 41.34s |
| before-1440-dark | 2.13s, 7.02s | 11.94s, 12.23s, 16.95s | 21.74s, 32.18s | 37.35s, 42.39s |
| before-1440-light | 2.46s, 7.16s | 11.94s, 12.2s, 16.98s | 21.89s, 32.72s | 37.89s, 42.75s |
| before-390-dark | 2.15s, 7.09s | 11.59s, 11.81s, 16.28s | 20.77s, 30.43s | 35.7s, 40.22s |
| before-390-light | 2.22s, 6.65s | 11.28s, 11.67s, 16.83s | 21.79s, 32.2s | 37.14s, 42.29s |
Full caller and draft chapter timestamps: chapters.md.
What else changed
Diff algorithm: per pixel maximum absolute R/G/B channel difference; bright magenta if >0 or >16 8-bit levels (16/255), black otherwise. No alignment, masking, crop, or subthreshold-equality claim. SHA headers themselves differ and are included. Exact sampled times, native frame sizes and differing-pixel counts are in the linked manifest/bundle.
- Intended R1 difference: baseline transient blank/loading frames and resets to first preview frame; after stays on the selected flower frame at5s. Source IDs differ after baseline churn. This changes video contents/playhead/controls timing, not panel styling. See each core recording’s R1 captions and first two sheet frames.
- R2: both replace flower with Sintel and duration5.055→52.208333; reset/loading moments vary. The scene near Sintel’s opening is dark; black or spinner stills are transient refresh samples, not proof of failed decoding. Confirm loadedmetadata/ready4 in the event logs and later playback chapter.
- R3: both retain the long clip while playing, then return to flower on pause. Exact animated frames differ because playback/command completion timing is not frozen. The
endedroute was not exercised. - Sandbox UI: before breadcrumb/project is
issue-23089-before; after isissue-23089. Detached baseline versus fix branch labels and sandbox change counts can differ when their panels are visible. Main sidebar is hidden for core/draft. Caller sweeps may show sandbox file/change sidebar; after contains the seven-file +138/−5 fix while baseline has0. No maintainer projects/private threads are seeded. - R4 desktop-light: local signed markdown has currentTime2/ready4 in both logs, but the sampled after image resembles the initial flower frame while before shows the2s frame. Native controls differ too. The cause (rendering/capture timing versus player behavior) is not established; do not certify frame equality from these captures.
- Time labels, relative ages, worked durations, step elapsed captions, source-ID counts, focus/cursor/hover and native control visibility differ. Browser recording start/stream lag differs slightly. Chat scroll follows the same steps but small content/timing differences shift lines. R4 local/remote videos can show briefly loading at different encoded frames. These are unmasked in both diffs.
- The capture-only bottom caption and SHA headers are added identically but contain different elapsed times/SHAs/source observations. They can cover the lower composer edge. These are not shipped UI changes.
- No new product labels, colors, layout, typography or controls are introduced by the fix. This statement is based on the seven-file diff; unmasked image deltas are not all attributable to the feature. Non-feature differences above mean strict frozen-clock evidence equality was not achieved and requires PASS2 judgment.
Rebuildable public data and checks
Original: 23089-original.mp4 — SHA256 0cd83d944a6ca7822b4a8306cecc60a36e859b041f6702c6a1ad9ead78924451,5.055s. Changed: 23089-changed.mp4 — SHA256 b670602fa00934ca27c4351bb0efe7ea7a07fae57284e44226025eeed7c51254,52.208333s. Original is MDN flower; changed is W3 Sintel trailer.
public-evidence-bundle.zip contains public-safe provider adaptations, capture scripts, file-operation/stat/hash transcripts, media events with source IDs only, persisted read-only-command outputs, chapter timestamps, rendering manifest, test outputs and SHA256 inventory. chapters.md provides every narrated step/time. sheets-manifest.json lists every extracted frame/time/diff count. No raw signed URLs, credentials, private thread content, databases or server logs are included.
Focused commands (after checkout’s own vp, Vitest5.0.5): cd apps/server && ../../node_modules/.bin/vp test run src/assets/AssetAccess.test.ts (25 pass); cd apps/web && ../../node_modules/.bin/vp test run src/components/media/MediaVideoPlayer.browser.test.tsx (1 pass); cd apps/web && ../../node_modules/.bin/vp test run src/components/files/FilePreviewPanel.test.ts (13 pass). No repo-wide checks. The player test uses generated WebM/object URLs and asserts source retention, revision replacement, revoked-held-URL fallback src/no alert. It does not establish live signed-URL recovery/decoding.
Implemented by Claude Opus 5.5 through Claude Agent SDK; triage, verification and independent review by GPT-6.1-Sol through Codex.
Checks
- SuccessfulCodeRabbitGitHub
Review completed
- Skipped
- Skipped
- Skipped
- SkippedBuild preview JS bundleGitHub
- SkippedRemove preview downloadGitHub
- SkippedDeploy web previewGitHub
- SkippedEAS PreviewGitHub
- FailedCheckGitHub
- Skipped
- Skipped
- SkippedEAS PreviewGitHub
- Skipped
- Skipped
- SkippedRemove preview downloadGitHub
- Skipped
- Skipped
- SkippedRemove preview downloadGitHub
- SkippedBuild preview JS bundleGitHub
- SkippedDeploy web previewGitHub
- Skipped
- SkippedRustGitHub
- SuccessfulTestGitHub
- FailedBuild and TypecheckGitHub
- SuccessfulRelease SmokeGitHub
- FailedTest WebGitHub
- SkippedEAS PreviewGitHub
- Skipped
- SuccessfulLabel PR sizeGitHub
- SuccessfulLabel PR 23127GitHub
- Skipped
- Skipped
- Successful
- SuccessfulCollect PR targetsGitHub
- Successful
- SuccessfulPrepare PR size configGitHub
- SuccessfulNative fingerprint diffGitHub
- SuccessfulTest Server 7GitHub
- SuccessfulTest Server 3GitHub
- SuccessfulTest Server 6GitHub
- SuccessfulTest Server 4GitHub
- SuccessfulTest Server 5GitHub
- SuccessfulTest Server 1GitHub
- SuccessfulTest Server 2GitHub
- SuccessfulChangesGitHub
- SuccessfulLintGitHub
- SuccessfulTest Server 8GitHub
Added 3 commits
Approvability
Verdict: Approved at d8d6747
Macroscope's review found this PR approvable — This focused bug fix adds optional file revisions so unchanged signed media URLs no longer reset paused sidebar videos, while real edits still refresh and active playback remains protected. The contract change is additive and the server, client, and player behavior are covered by targeted tests.
You can add or adjust custom eligibility rules. Learn more.
Thread transfer impact
✅ Thread transfer remains within every enforced ceiling.
ℹ️ The exact PR base did not have a successful artifact. Baseline uses the latest successful
mainmeasurement shown below.
| Provider | Metric | Main baseline | This PR | Impact | PR ceiling | |
|---|---|---|---|---|---|---|
| Codex | Total thread wire | 5.0 KiB | 5.0 KiB | −7 B (−0.1%) | 6.8 KiB | ✅ |
| Codex | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ✅ |
| Codex | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | −7 B (−0.6%) | 2.0 KiB | ✅ |
| Codex | Live turn WebSocket decoded | 20.9 KiB | 20.9 KiB | 0 B (0.0%) | 29.3 KiB | ✅ |
| Codex | Live turn messages | 2 | 2 | 0 (0.0%) | 8 | ✅ |
| Claude | Total thread wire | 5.0 KiB | 5.0 KiB | 0 B (0.0%) | 6.8 KiB | ✅ |
| Claude | Thread snapshot wire | 3.8 KiB | 3.8 KiB | 0 B (0.0%) | 4.9 KiB | ✅ |
| Claude | Live turn WebSocket wire | 1.2 KiB | 1.2 KiB | 0 B (0.0%) | 2.0 KiB | ✅ |
| Claude | Live turn WebSocket decoded | 21.2 KiB | 21.2 KiB | 0 B (0.0%) | 29.3 KiB | ✅ |
| Claude | Live turn messages | 2 | 2 | 0 (0.0%) | 8 | ✅ |
Baseline: 159f2b8 · PR result: d8d6747 · Source CI: failure
Scenario and decoded snapshot size
10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
- Codex decoded thread snapshot: 108.5 KiB
- Claude decoded thread snapshot: 108.8 KiB
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.
No actionable comments were generated in the recent review. 🎉
ℹ️ Recent review info
⚙️ Run configuration
- Configuration used: Path: .coderabbit.config.ts
- Review profile: CHILL
- Plan: Team
- Run ID:
6ea1a2f7-7952-4361-9cab-58bc1999a3d4
📥 Commits
Reviewing files that changed from the base of the PR and between 70e5e384dab96a1d1e4a253ee5a9415ed5d8ce30 and d8d674784e4279e847b450e5908dc29db17a6be0.
📒 Files selected for processing: 7
apps/server/src/assets/AssetAccess.test.tsapps/server/src/assets/AssetAccess.tsapps/web/src/components/files/FilePreviewPanel.tsxapps/web/src/components/media/MediaVideoPlayer.browser.test.tsxapps/web/src/components/media/MediaVideoPlayer.tsxpackages/client-runtime/src/state/assets.tspackages/contracts/src/assets.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.
📝 Walkthrough
Asset URL results now include an optional media revision derived from file metadata. The client carries this revision to video previews, where the player uses it to identify the loaded source. Tests cover revision changes and video behavior during URL re-signing.
Changes: Media revision flow
Layer / File(s) Summary Asset URL revision contract
packages/contracts/src/assets.ts,packages/client-runtime/src/state/assets.tsThe asset URL result schema and successful client asset state now support an optional revision. The state mapper copies the revision when present. Compute and issue media revisions
apps/server/src/assets/AssetAccess.ts,apps/server/src/assets/AssetAccess.test.tsThe server derives revisions from device, inode, size, modification time, and change time. It includes revisions in applicable issued asset results. Tests check repeated signing, in-place edits, and atomic replacement. Use revisions in video previews
apps/web/src/components/files/FilePreviewPanel.tsx,apps/web/src/components/media/MediaVideoPlayer.tsx,apps/web/src/components/media/MediaVideoPlayer.browser.test.tsxThe preview prefers the asset revision and falls back to workspaceMutationId. The player records the loaded source and revision. Browser tests cover unchanged URL re-signing, expired URLs, and revision changes.Priority: ➖ Normal
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Sequence Diagram(s)
mermaid1 sequenceDiagram 2 participant AssetAccess 3 participant AssetUrlState 4 participant WorkspaceVideoPreview 5 participant MediaVideoPlayer 6 AssetAccess-->>AssetUrlState: Return signed URL and revision 7 AssetUrlState-->>WorkspaceVideoPreview: Provide URL result 8 WorkspaceVideoPreview->>MediaVideoPlayer: Pass URL and selected revision Possibly related issues
pingdotgg/t3code#23089— The issue describes paused local video previews reloading after unchanged command completions. This change adds per-file revisions and tests that unchanged video content remains loaded during URL re-signing.
Suggested reviewers:
maria-rcks,t3dotgg
Merge Risk: ⚪ Minimal · up to d8d67
Video source retention appears consistent with preserving paused playback across URL changes. No actionable merge-blocking issue remains after normal checks.
Pre-merge checks: Passed 4
Check name Status Explanation Title check Passed The title clearly and concisely describes the main user-visible fix: paused sidebar videos keep their playback position during unrelated commands. Linked Issues check Passed Check skipped because no linked issues were found for this pull request. Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request. Description check Passed The pull request description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, implementation, issue reference, approval basis, focused tests,… Full details: Description check
Explanation
The pull request description includes all required sections: Problem, Change, Scope and approval, and Verification. It explains the bug, implementation, issue reference, approval basis, focused tests, observed results, UI evidence, limitations, and the agent and harness used.
✨ Finishing Touches: 💡 1
-
🛠️ Fix failing CI checks: 💡
- Commit to this branch
- Create a new PR
📝 Generate docstrings
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
- Commit to this branch
- Create a new PR
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Comment @coderabbitai help to get the list of available commands.