Repository navigation
Cloud copy: compressed fs.write bytes (fs-write-deflate-v1) and fewer round trips per file - #34538
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
The machine daemon advertises fs-write-deflate-v1 where it serves fs-v1 and takes "encoding":"deflate" with decoded_size on the fs.write line op and the write stream (bounded decode, nothing at the path unless all of it decoded; the stream also takes permissions). link sync push, the single-file copy and the seed bundle upload compress what shrinks when the machine advertises it, and send plain base64 exactly as before otherwise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
One fs.list per folder instead of one fs.stat per file: on a link of tens of ms the per-file round trip was most of a small file's cost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Bugbot needs on-demand usage enabledBugbot 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. |
|
Owned by an agent session · session |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Shipped in:
|
|
Review (codex gpt-6.1-sol xhigh, router-review): SHIP at 6600293 Visual verdict: unchanged by this diff No correctness, regression, security, or test-breakage findings identified from the changed protocol and sync paths. Follow-ups: none from the review. Filed from this work: #34482 (raw byte stream over the Cloud link's local socket), #34483 (parallel or pipelined file writes). Reds at this head that are not this PR's: |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Bugbot needs on-demand usage enabledBugbot 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. |
|
Found 6 test failures on Blacksmith runners: Failures
|
|
Review (codex gpt-6.1-sol xhigh, router-review): SHIP at 82631ce Visual verdict: no images; unchanged UI. The only delta since the previous SHIP at 6600293 is rustfmt. Two [P2] [follow-up] findings, filed: #34691 (require the deflate stream's end before commit) and #34692 (fall back to The reds are base reds in files this PR doesn't touch (Windows |
…wire # Conflicts: # cmux-tui/crates/cmux-tui-core/src/fs_ops.rs # cmux-tui/crates/cmux-tui-core/src/server/capabilities.rs # cmux-tui/crates/cmux-tui-core/src/server/fs_wire_tests.rs # cmux-tui/crates/cmux-tui/src/link/sync_cli.rs
Bugbot needs on-demand usage enabledBugbot 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. |
|
Review (codex gpt-6-astra high, router-review): BLOCK at 6d1d75c BLOCK at 6d1d75c
Source review completed; Rust tests could not run because Cargo is unavailable. Follow-ups: none |
…again The cached listing answered it, but base's symlink protection depends on it being fresh: a file the machine swapped for a symlink mid-push would be written through the link. The listing optimization moves to a follow-up that pairs it with a no-follow write. The upload test fake answers the client's identify (an image without fs-write-deflate-v1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Bugbot needs on-demand usage enabledBugbot 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. |
A newer verdict blocks c6cac5f (no independent SHIP at the head).
|
Review (codex, router-review): SHIP at 20fff43 Reviewer: codex gpt-6-astra high, fast tier. SHIP at 20fff43 Visual verdict: unchanged by this diff Reviewed the deflate decoder, atomic stream cleanup, capability negotiation, multipart writes, permissions, and compatibility paths. Focused tests could not run because Follow-ups: none |
|
Lead: after this PR merged, |
|
Base red from this PR on owned Macs: |
…cOS (#35306) * base red: fs_wire routed() stops sending when the daemon already answered and closed a_deflate_write_stream_over_its_decoded_size_leaves_no_file (from #34538) panics with BrokenPipe at fs_wire_tests.rs:266 on every macOS run: the daemon refuses the deflate stream as soon as it decodes past decoded_size, answers params.invalid and closes. The 64 MiB-of-zeros wire is ~64 KiB; a macOS socketpair buffers ~8 KiB, so the rest of the body meets a closed socket and write_all().unwrap() panics before the test reads the answer. On Linux the buffer (~200 KiB) takes the whole body, so CI stayed green. The daemon behaves as designed (it does not read a bomb to its end); the helper now does what a client does: it stops sending on BrokenPipe or ConnectionReset and reads the answer. Any other write error still fails. * fs_wire routed(): a daemon gone before the shutdown is the same refusal; stream.rs says a refused write answers before the rest of size Review of 27d9dd0: on a short body the daemon can answer and close before the client's shutdown(Write), which macOS answers with NotConnected; the old unwrap there was the same race. stream.rs now states that a write refused after ready is answered at once without reading the rest of size, so a client reads the answer when its send fails. --------- Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo asked:
and approved: "2. Compress what's sent and stop encoding it as text."
Leo-ask: ask-6e29ca080a
What changes
Daemon (cmux-tui-core). A new capability,
fs-write-deflate-v1, is advertised only wherefs-v1is (fs_ops::advertised). It is host-dependent, so it is kept out ofbuild_capabilitieslikefs-v1. Under it,fs.writetakes"encoding":"deflate"withdecoded_sizeon both the line op and the write stream. The wire bytes (the line'sbytes_base64, or the stream'ssizeraw bytes) are one raw deflate stream (RFC 1951):decoded_size.PendingWriteand stops atdecoded_size. Decoding to more or fewer bytes, or bytes that are not deflate, isparams.invalidand leaves nothing at the path, not even the temporary file. A small payload can't fill the disk.permissions, as the line op already did.spec/commands.md(fs-v1) documents it. The contract fixture is unchanged: it records no stream and no new field.Client (
link sync push, single-file copy, seed bundle upload). The client asksidentifyonce per connection. When the machine listsfs-write-deflate-v1, it deflates each part at level 3, but only if the file's first 64 KiB shrink by more than a tenth. Packs, images and archives go as they are. It also checks that each compressed part is actually smaller. Without the capability, every request is byte-for-byte the old base64 request, so old machine images keep working. Unchanged:IO_DEADLINEand the answer budgetRound trips. The benchmark showed the push is bound by latency, not bandwidth: 490 MB at 8 MB/s needs about 61 s, but the push took 495 s. Answering each file's "what is in the way" check from the cached folder listing cut that to 308 s. Review found that this lets a symlink the machine swaps in mid-push be written through (#34362's protection), so that change was taken back out of this PR. It returns with a no-follow write in #34999.
Also fixes the stale
seed.rsheader: "Not wired intostart-chatyet." has been false since start-chat's copy started callingseed_cli::push.What this does not do yet
"Stop encoding it as text" over the raw write stream is not reachable on a Cloud copy today. The stream is routed only on the link's remote entry (
route_first_line). Theremote connect --local-socketbridge carries bounded JSON lines to the daemon's local socket, whose line path refuses"stream":true. Carrying the stream needs changes in cmux-remote: #34482. The bigger remaining win is sending files over several connections: #34483. Both are measured with the harness below.Backward compatibility
a_machine_without_the_capability_gets_plain_base64).encodingfield, so the daemon writes the bytes as before.decoded_sizewithoutencoding, or any encoding other thandeflate, is refused.Tests
fs_ops::encodingunit tests: round trip, a decode pastdecoded_sizerefused (64 MiB of zeros declared as 1 MiB), short or foreign bytes refused, the line limit applied todecoded_size.fs_wire_tests, end to end throughroute_first_line: a 40 MiB file over one deflate stream with 0755 kept; a decompressed-size overrun leaves no file or temporary; a line write decodes, keeps 0600, and refuses an overrun.remote.rs: the old-machine fallback sends plain base64; with the capability, a file larger than one part decodes from its parts and random bytes go uncompressed.tests/link_push_bench.rs:a_push_to_a_cloud_daemon_crosses_compressed_and_lands_exact, against a real Cloud-shaped daemon (exact bytes, 0755, wire bytes below the tree size).link_sync(18),link_access(5),cloud_move_journey(3),sync_clilib (21),fs_lib (32).-D warningson cmux-tui-core and cmux-tui is clean in the changed code. Two base reds incli/automation*(manual_is_multiple_of,type_complexity) are untouched by this PR.Performance
The same harness on both sides:
link_push_bench, a real headless daemon with the Cloud identity, pushed through a unix-socket proxy at 8 MB/s each way with 30 ms one-way latency. The fixture is 3,190 files (365 MB, a 172 MB.git: 3,000 source-like text files packed plus 160 MiB of random blobs left loose). "Before" ran on the commit with only the harness, 3294d76.link_push_bench, fleet Linux job b081eb17 (before) / run 41786488153 (after, at e9478e9)Measured but not shipped here: with the per-file
fs.statanswered from the cached listing, the same harness gave 308.1 s / 460.6 MB (-37.7% wall). That change is #34999. The fixture is mostly incompressible random blobs, so compression's byte gain is small here; source-heavy trees gain more (the 200-file text test crosses in well under the tree size). The packs change (#34537, merged into this branch by update-branch) is not part of either number.Reuse:
The diff is about 950 lines with tests and the benchmark harness, which another PR shares (the shipped code is about 330 lines). It is kept as one PR because the capability, its decoder and its client are one protocol change, and the benchmark's before number needed the harness on its own commit.
Agent session: agent session
🤖 Generated with Claude Code
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Speeds up Cloud copies by compressing
fs.writebytes through a newfs-write-deflate-v1capability, while machines or clients without it get plain base64 exactly as before.fs.writeline op and write streams accept"encoding":"deflate"withdecoded_size; wire bytes are one raw deflate stream, decoded as they arrive. Decoding to more or fewer bytes, or to non-deflate bytes, isparams.invalidand leaves nothing at the path; the free-space check and write limit apply todecoded_size. Write streams also takepermissions.link sync push, single-file copy, seed bundle upload) deflates parts at level 3 only when the machine advertises the capability and the file's first 64 KiB shrinks by more than a tenth. Without the capability or the fields, every request is byte-for-byte the old base64 one.fs.statright before each write, so base's symlink protection stays current; reusing the listing needs a no-follow write first.seed.rsheader, which claimed the copy path was not wired intostart-chat.Written for commit c6cac5f. Summary will update on new commits.