Skip to content

cmux-next browser: a web .mp4 sent as a generic file plays in the tab instead of downloading - #24338

Merged
teamleaderleo merged 6 commits into
feat-cmux-nextfrom
browser-import-2/remote-video-plays
Oct 22, 2026
Merged

teamleaderleo merged 6 commits into
feat-cmux-nextfrom
browser-import-2/remote-video-plays

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Ready: an independent Codex review says SHIP at the head, and the fleet tests pass at the head and fail at the test-only commit (see the receipt comment).

Leo-ask: ask-76f83a7020

"this watch is kind aass also id like to be able to watch .mp4s direclty in cmux next somehow (chromium?)"

What

This PR handles the ".mp4 in cmux" half of the ask. The link to the What's New reel (Watch what changed in nightly <sha> ↗) points at raw.githubusercontent.com/.../reel.mp4. GitHub sends every .mp4 there as application/octet-stream with nosniff, so a cmux browser tab downloaded the file instead of playing it. Any other web .mp4 served the same way did too.

Now a WebKit tab plays such a video at the video's own address, in a plain <video controls autoplay> page (black background, fitted to the tab), when a main-frame response meets all of these:

  • WebKit cannot show its type;
  • it is not Content-Disposition: attachment;
  • its http(s) path ends in mp4, m4v, mov, webm or ogv;
  • the navigation was a GET and not Back or Forward.

The page loads with loadSimulatedRequest, so it is a normal back-list entry: Back returns to the page the video was opened from.

These cases still download, as before:

  • attachments;
  • every other file type;
  • a POST, because the player could not fetch the video again;
  • a Back or Forward to an entry WebKit had evicted, because that must not add a new back-list entry.

The link's look ("this watch is kind aass") is the What's New page, which the whats-new lane owns. I've sent them that half of the ask, with the screenshot.

Known limitation

A page that calls location.replace() on such a video gets a new back-list entry instead of replacing its own, because WebKit doesn't tell the app that a navigation is a replace. This is rare, and the player still plays.

Chromium tabs don't take this path. CEF downloads octet-stream responses in the shim, and changing that means a shim rebuild. This PR keeps to the WebKit change.

Tests

Tests/CmuxNextBrowserTests/RemoteVideoPageTests.swift runs a real WebKit tab against a local server. The server sends a generated H.264 .mp4 the way GitHub does: application/octet-stream, nosniff, with byte ranges. The test:

  • goes from page a to page b, then follows b's link to the video;
  • waits for the video's currentTime to pass 0;
  • checks there was no download and that the back list is exactly a, b, reel.mp4 after Back and Forward;
  • checks that a form POST for the same video still downloads.

Observed / Not run

  • Red first: on the fleet, the test-only commit cf8e824 fails. The tab downloads the video, so it never plays.
  • Green: on the fleet, RemoteVideoPageTests passes at the head (5ff010c).
  • The prepush gate passed with no new violations.
  • Not run: a click on the real What's New link in a built app.

Changelog

  • Fixed: a web .mp4 that its server sends as a generic file, such as the nightly What's New reel, plays in a cmux browser tab instead of downloading.

No capture: the player is the system <video> control on black. The test proves playback.

Agent session: cosmic-gray

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Plays web videos whose servers send them as generic files in a cmux WebKit tab instead of downloading them — the nightly What's New reel among them, since raw.githubusercontent.com serves every .mp4 as application/octet-stream with nosniff. The video now opens in a plain player page (system controls, black background) at its own address, and takes a normal spot in the back list: Back returns to the page the video was opened from.

Only a main-frame GET for an http(s) URL ending in mp4, m4v, mov, webm, or ogv becomes the player. The tab records the video URL when its newest own main-frame navigation asks for it, and only a response for that URL plays — a target=_blank click, an older held GET, or an in-flight reload cannot turn a POST's response into it. Attachments, other file types, POSTs, and Back or Forward navigations still download as before, as do Chromium tabs.

Known limitation

  • A page calling location.replace() on such a video adds a new back-list entry instead of replacing its own; the player still plays.

Tests

  • RemoteVideoPageTests plays a generated H.264 .mp4 served like GitHub does (octet-stream, nosniff, byte ranges) and checks playback, the back-list order, that a form POST still downloads, and that only the matching response becomes the player.

Written for commit 5ff010c. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 4 commits October 22, 2026 01:07
…he WebKit tab (fails: it downloads)

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
…bKit tab instead of downloading

raw.githubusercontent.com sends every .mp4 as application/octet-stream with
nosniff (the What's New nightly reel among them), which WebKit cannot show, so
the tab downloaded it. A main-frame response that would download, is not an
attachment, and names a video (mp4, m4v, mov, webm, ogv) now loads a player
page at the video's own address instead.

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
…ck list

loadHTMLString loads hidden from session history, so Back skipped the page the
video was opened from (independent review). loadSimulatedRequest makes a
normal history entry at the video's address. The test now goes a, b, then the
video, waits for the video's time to advance, and checks Back and Forward;
it holds its engine and writes the fixture off the main actor.

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
… video player

A POST's video cannot be fetched again by the player, and a traversal to an
evicted entry must not push a new one; both keep downloading as before
(independent review). The test checks the exact back list after Back and
Forward, that a posted video downloads into a temporary folder, and writes its
fixture on a background executor (@Concurrent).

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 22, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by browser-import-2 on cosmic-gray · session

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 5ff010c: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr24338 -f ref=browser-import-2/remote-video-plays -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); NEW: red check: Checks job structure (+1 more) is not red on the base 2d13b66; do not merge until green; see lane-rules

@blacksmith-sh

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 9 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmpig4s6our/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmpr5bbl1x6/summary-fail.json (live):/summary-fail.json (live): View Logs
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows/
[new_workspaces] debug.new_workspace timed 0 of 50 new workspace rows
View Logs
[new_workspaces] the window draws no frames/[new_workspaces] the window draws no frames View Logs
[tabs] debug.tab_switch timed 0 of 40 switches/
[tabs] debug.tab_switch timed 0 of 40 switches
View Logs
[tabs] the window draws no frames/[tabs] the window draws no frames View Logs
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)/
test_every_part_of_bun_run_check_runs_in_the_warn_branch (main.WebviewsWarnSplitCov
ersCheck.test_every_part_of_bun_run_check_runs_in_the_warn_branch)
View Logs
test_every_submodule_free_checkout_on_an_owned_runner_resets_submodules (main.Reuse
dWorkspaceSubmodules.test_every_submodule_free_checkout_on_an_owned_runner_resets_submo
dules)/
test_every_submodule_free_checkout_on_an_owned_runner_resets_submodules (main.Reuse
dWorkspaceSubmodules.test_every_submodule_free_checkout_on_an_owned_runner_resets_submo
dules)
View Logs
test_every_submodule_free_checkout_on_an_owned_runner_resets_submodules (main.Reuse
dWorkspaceSubmodules.test_every_submodule_free_checkout_on_an_owned_runner_resets_submo
dules) (job='web-bundles')/
test_every_submodule_free_checkout_on_an_owned_runner_resets_submodules (main.Reuse
dWorkspaceSubmodules.test_every_submodule_free_checkout_on_an_owned_runner_resets_submo
dules) (job='web-bundles')
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-945kp created for this PR by the cmux coordinator.

…owed main-frame load

A target=_blank click's action (no target frame) set the flag while a POST was
still loading, so the POST's video could become the player (independent
review). The flag is now set only when this view's own main-frame load is
allowed. The test counts only a download the gate let start, and removes its
folder off the main actor.

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
@cursor

cursor Bot commented Oct 22, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

… navigation plays

The flag was written from the certificate recheck's late callback, so an older
held GET could mark a newer POST's response playable (independent review). The
tab now records, synchronously, the video URL the newest own main-frame
navigation asks for, and only a response for that URL becomes the player.

Leo-ask: ask-76f83a7020

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-98cd-7a42-ac32-a70f43729c15&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (independent subagent): SHIP at 5ff010c

Reviewer: Codex gpt-6.1-sol on cmux16 (a different model from this lane), adversarial, fifth round. Rounds 1-4 blocked and each finding was fixed in a following commit: hidden history entry, POST responses, new-window actions, a late certificate-admission callback.

Verdict

SHIP at 5ff010c

No blocking findings in manaflow-ai/cmux, browser-import-2/remote-video-plays against feat-cmux-next.

  • Non-blocking limitation — Packages/macOS/CmuxNext/Sources/CmuxNextBrowser/WebKit/WebKitTab+Delegates.swift:60: A GET link to /reel.mp4 that redirects to an extensionless /media?id=123 clears the candidate at the redirect and downloads. Redirects ending in a supported video extension remain eligible. This preserves base behavior for the extensionless destination; it does not break the direct raw.githubusercontent.com reel case.
  • Non-blocking test coverage — Packages/macOS/CmuxNext/Tests/CmuxNextBrowserTests/RemoteVideoPageTests.swift:35: The integration test covers the direct octet-stream/nosniff GET, advancing playback, visible URL, Back/Forward history, and POST download. It does not exercise a redirect to another .mp4 or an attachment response; a regression in either scenario could escape this test. The current policy retains the attachment download branch and reevaluates redirected requests.

The main-frame, method, traversal, and response-URL guards do not reveal a remaining wrong-response conversion. Explicit downloads and synchronous cancellation paths precede player eligibility; organization filtering and certificate admission still precede the original load. The generated title and double-quoted source escape HTML delimiters, and the source is restricted to HTTP(S). Bypassing automatic-download admission here renders media rather than creating a download.

Reviewed the complete patch and complete changed files fetched with curl at the exact head above. Static review only: no checkout, clone, build, tests, or gh operations; no runtime or CI success claimed.

Tests (fleet, cmux-ci)

  • Head 5ff010c: RemoteVideoPageTests passed, 2 tests (the video plays, the back list is exactly a, b, reel.mp4 through Back and Forward, a POST still downloads).
  • Test-only commit cf8e824 (red first): failed. The video never played, because the tab downloaded it.

Reds at this head

@teamleaderleo
teamleaderleo merged commit 123a1ec into feat-cmux-next Oct 22, 2026
3 checks passed
@teamleaderleo
teamleaderleo deleted the browser-import-2/remote-video-plays branch October 22, 2026 13:11
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

Shipped in:

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 5ff010cd Deployed Oct 22, 2026 by teamleaderleo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant