carry of #27073: NEXT: Cloud and peer chat uploads take files of any size
Carry of #27073 (its owner's host can't be reached; idle since 2026-10-30T04:06Z). It keeps the original commits and their authorship, merges current feat-cmux-next, and fixes what blocked the last two reviews.
Leo's words this serves: "arbitarrily large file sizes" (2026-10-25) and "why the F*** SHOULD THERE BE A F***ING SIZE CAP ANYWAYS ... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE" (2026-10-26).
What the original does (still missing on base)
Base already streams Home bucket multipart uploads of any size (#26673), phone uploads (#26954) and native staged uploads into local chats (#26409). Uploads into Cloud and ssh-peer chats still had caps until this PR:
- Cloud chats (
CloudUploads.swift): the 32 MiB per-file and 64 MiB per-batch caps are gone, along with the "won't send that file" refusal text (cloud.upload.refusedremoved from Cloud.xcstrings).cmux link access upload(access_cli/uploads.rs) drops its 1 GiBMAX_FILE_BYTESand prints a progress heartbeat after each remote write.CloudAccessCLIruns the upload under a 30 s idle deadline (ProcessRunnerprogressIdle, withtimeoutnow optional) and keeps only a bounded tail of its output. Each staged transfer gets an app-chosen--upload-id, and a newupload-cancelremoves exactly that remote directory when the CLI fails or is cancelled. - Peer chats (
AgentPeerUploads.swift,AgentPaneFilePut.swift):maximumPeerFileBytesand the 1 GiB guards are gone. Only the peer can refuse a file as too large. The peer-side cancel runs from a fresh task, so a cancelled put still reaches the peer. - Decoder (
AgentPaneUpload.swift):chat.uploaddropsmaximumFileBytes/maximumTotalBytes. A batch (AgentPaneUploadBatch) stays authorized while any of its stages lives. - Page (
cloudUploads.ts,attachmentIntake.tsx,intakeFiles.ts): the chip's × aborts the finish while it is in flight. Finish has no page deadline because native owns the idle deadline. The page awaits the cancel until the host has removed its stage.
What this carry fixes
-
Compile (finding 1,
AgentPeerUploads.swift:76) and compile (finding 2,CloudUploads.swift:159): theStageinitializers left outexpiry/cleanup. Both stagers now build their stages in one shared store (item 4), so those initializers no longer exist. Commit 77a24bb8 first named every field. -
Compile after the base merge: base's #24473 (recording drops from the macOS capture thumbnail) read
AgentPaneUpload.maximumFileBytes/maximumTotalBytes, which this PR removes. That snapshot reads the recording into memory before the save, so it keeps the same 1 GiB as its own named in-memory budget:AgentPaneRecordingSnapshot.maximumPinnedBytes/maximumPinnedTotalBytes(AgentPaneRecordingSnapshot.swift,AgentPaneDroppedPaths.swift:58,AgentPaneRecordingDropTests.swift:167). Ordinary files dropped into a local chat still go as links to their real paths, with no cap. -
Attachment cap guard (
scripts/cmux-next/check-attachment-caps.py): it still exempted the caps this PR removes (Cloud, peer, decoder, CLIMAX_FILE_BYTES). The new testtest_retained_bounds_name_only_symbols_their_file_still_declares(tests/test_attachment_caps_guard.py) fails while an exemption names a cap its file no longer declares. Those exemptions are now gone, so a removed cap can't come back unreviewed. -
One staged-upload store (
CmuxNextAgentPane/AgentPaneStagedUploads.swift): each App stager kept its own stages, batches, expiry deadlines and cleanup tasks. That added +131 CmuxNextApp lines, which the pre-push arch ratchet refuses, plus a static mutable singleton.AgentPaneStagedUploads<Target>now owns all of it once:- a stage of any size;
- batch authorization;
- one
DemandTimerfor abandoned stages and batches (the peer stager used to sleep a task per stage); - a transfer that owns its file until it returns;
- a cancel that stops the transfer before the staged file is removed.
Each stager keeps only its own target checks, file layout and transfer. The peer store lives on
CloudService.peerUploads, besideuploads. Net effect: CmuxNextApp is -48 lines and has 2 fewer global mutable statics. -
Review of this carry (Astra, BLOCK at 6ff0fa8d), fixed in f6edd0e5:
- The older one-frame upload sent every file in one CLI call under no upload id. So more than 64 files were refused, and a failed or cancelled call left files on the machine.
CloudAccessCLI.uploadnow sends each file in its own call under an id of the app's. A failure or cancel removes every file of that upload (removeUploads) before it answers. - A cancel that arrived after the file was delivered (the chip's × while the answer is on its way) found no stage. The store now keeps a finished stage's undo for its lifetime, and the Cloud undo removes that upload id. A finished peer put has no remote delete, so the peer path has no undo.
- Second round (BLOCK at f6edd0e5), fixed in f35ace88 / b33d5cd1:
removeUploadsnow tries every id and throws after all of them, and the undo test comparesundonebefore and after each wrong-target cancel.
- The older one-frame upload sent every file in one CLI call under no upload id. So more than 64 files were refused, and a failed or cancelled call left files on the machine.
Review: codex gpt-6-astra (high, fast) SHIP at 9d4b5d8de360c661a31cf906fe98870e33ddc097 (after a conflicting base merge with #27012, #28360 and #27768), receipts in the comments.
Not in this carry: findings 3 (backend/apps/home-server/src/fs-bucket.ts:345, /dev/fd/<dir>/child on macOS) and 4 (cmux-tui/crates/acpmux/src/cli/cloud_join.rs, a repeated join resetting meshRelay). Both are real on base, but this PR doesn't change either file. They came in through base merges (#26673 and the acpmux join work), so each goes in its own PR.
Overlap: open #27200 (cap guard / AgentChatUploads), #26958 and #27768 (intakeFiles.ts) change some of the same files for other fixes; whichever lands second takes a base merge.
Verification
python3 tests/test_attachment_caps_guard.py: the new test was red at 472d2cd1 (9 stale exemptions) and is green at the head (14 tests).python3 scripts/cmux-next/check-attachment-caps.py: clean.python3 scripts/cmux-next/prepush-gate.py: no new violations. arch-ratchet reports app_lines -48 and global_mutable_statics -2.- New Swift tests:
AgentPaneStagedUploadsTests(begin of any size, batch order and single use, chunk ownership and order, cancel during transfer stops it before the file goes). The original'sCloudUploadsTests,AgentPeerUploadsTests,ProcessRunner*Testsanduploads_tests.rsare kept.
Observed / Not run
- Observed: the guard test red → green, the guard itself, the pre-push gate.
- Not run here: Swift compile and suites (Linux lane VM). They come from this PR's fleet CI, and the result is in a comment below. The Rust
uploads_tests.rsandscripts/ci/proofs/upload-cancel.pyrun through CI.
No capture: no UI change. The only visible change is that a large file now uploads where it used to be refused; the composer chip is the same.
Changelog
- Changed: files of any size can be dropped or pasted into chats on a Cloud machine or an ssh peer.
risk: normal (upload paths; no auth or relay policy change).
Agent session: agent session
🤖 Generated with Claude Code