Repository navigation
link: cmux link seed push and seed clone, a Cloud project seed with the copy as fallback - #30634
Conversation
… with a copy fallback; agent move --sync seed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
|
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:
|
|
Owned by an agent session · session |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
|
Review (codex gpt-6-astra high, router-review): BLOCK at aad041c BLOCK at aad041c Visual verdict: no images; Leo tries the build
Validation: static review and Git permission reproduction completed. Rust tests could not run because Cargo is unavailable. Follow-ups:
|
There was a problem hiding this comment.
8 issues found across 9 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmux-tui/crates/cmux-tui-core/src/git_seed.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/git_seed.rs:52">
P3: A relative `CMUX_TEST_GIT_SEED_GITHUB` is ignored without any signal, so the seed quietly clones from real GitHub instead of the test directory. Warn or panic when the variable is set but not absolute, rather than filtering it out.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui/tests/link_seed.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui/tests/link_seed.rs:201">
P3: `files()` turns any read failure into an empty string, so binary or non-UTF-8 files compare equal regardless of content. Read as bytes (`std::fs::read`) and compare `Vec<u8>`, or unwrap the read.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui-core/src/git_seed/job.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/git_seed/job.rs:193">
P3: `with_test_github` disables the GitHub credential path and enables `protocol.file`, but it is compiled into release builds. Gate it with `#[cfg(debug_assertions)]` to match the debug-only seam in `installed()`, so release builds cannot construct this service.</violation>
<violation number="2" location="cmux-tui/crates/cmux-tui-core/src/git_seed/job.rs:194">
P3: A non-UTF-8 absolute directory accepted by the environment seam is lossy-converted by `display()`, so the rewritten `file://` URL points elsewhere and seeding fails. Encode the OS path without loss or reject it.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui/src/link/seed_cli.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/seed_cli.rs:225">
P3: The `--repo` check accepts empty owner or name parts such as `o/` and `/n`. Require both parts to be non-empty.</violation>
<violation number="2" location="cmux-tui/crates/cmux-tui/src/link/seed_cli.rs:461">
P2: A top-level REMOTE such as `/project` fails with `seed.bad_request` here instead of falling back to the copy. Parent-less folders should take the copy path, or `parse` should reject them.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui/src/link/seed_cli/record.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/seed_cli/record.rs:91">
P3: The rename is not made durable because the parent folder is never synced. After a crash the seed record can revert to an older commit set or disappear, and `--since-last` then plans from stale state or reports `seed.no_previous`. Sync the parent folder after the rename, as `agent_cli/record.rs` does.</violation>
</file>
<file name="cmux-tui/crates/cmux-tui/src/link/sync_cli.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/sync_cli.rs:326">
P2: `upload` opens the bundle with `O_NOFOLLOW` but not `O_NONBLOCK`, so a FIFO swapped in at the bundle path blocks the open indefinitely. The shared `open()` helper and `place.rs` both add `O_NONBLOCK` for this reason; add it here too.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| let (parent, name) = remote | ||
| .rsplit_once('/') | ||
| .filter(|(parent, name)| !parent.is_empty() && !name.is_empty()) | ||
| .ok_or_else(|| SeedError::new("seed.bad_request", "the machine folder has no parent"))?; |
There was a problem hiding this comment.
P2: A top-level REMOTE such as /project fails with seed.bad_request here instead of falling back to the copy. Parent-less folders should take the copy path, or parse should reject them.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/seed_cli.rs, line 461:
<comment>A top-level REMOTE such as `/project` fails with `seed.bad_request` here instead of falling back to the copy. Parent-less folders should take the copy path, or `parse` should reject them.</comment>
<file context>
@@ -0,0 +1,631 @@
+ let (parent, name) = remote
+ .rsplit_once('/')
+ .filter(|(parent, name)| !parent.is_empty() && !name.is_empty())
+ .ok_or_else(|| SeedError::new("seed.bad_request", "the machine folder has no parent"))?;
+ let target = format!("{parent}/.cmux-seed-{name}-{}.bundle", unique());
+ let total = planned.bundle_bytes;
</file context>
| } | ||
| let file = std::fs::OpenOptions::new() | ||
| .read(true) | ||
| .custom_flags(libc::O_NOFOLLOW) |
There was a problem hiding this comment.
P2: upload opens the bundle with O_NOFOLLOW but not O_NONBLOCK, so a FIFO swapped in at the bundle path blocks the open indefinitely. The shared open() helper and place.rs both add O_NONBLOCK for this reason; add it here too.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/sync_cli.rs, line 326:
<comment>`upload` opens the bundle with `O_NOFOLLOW` but not `O_NONBLOCK`, so a FIFO swapped in at the bundle path blocks the open indefinitely. The shared `open()` helper and `place.rs` both add `O_NONBLOCK` for this reason; add it here too.</comment>
<file context>
@@ -295,6 +301,43 @@ pub(super) fn copy_file_with_progress(
+ }
+ let file = std::fs::OpenOptions::new()
+ .read(true)
+ .custom_flags(libc::O_NOFOLLOW)
+ .open(local)
+ .with_context(|| format!("cannot read {}", local.display()))?;
</file context>
| .custom_flags(libc::O_NOFOLLOW) | |
| .custom_flags(libc::O_NOFOLLOW | libc::O_NONBLOCK) |
| Some(SERVICE.get_or_init(|| GitSeedService::new(fs.roots().clone()))) | ||
| Some(SERVICE.get_or_init(|| { | ||
| #[cfg(debug_assertions)] | ||
| if let Some(dir) = std::env::var_os(TEST_GITHUB_VAR) |
There was a problem hiding this comment.
P3: A relative CMUX_TEST_GIT_SEED_GITHUB is ignored without any signal, so the seed quietly clones from real GitHub instead of the test directory. Warn or panic when the variable is set but not absolute, rather than filtering it out.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui-core/src/git_seed.rs, line 52:
<comment>A relative `CMUX_TEST_GIT_SEED_GITHUB` is ignored without any signal, so the seed quietly clones from real GitHub instead of the test directory. Warn or panic when the variable is set but not absolute, rather than filtering it out.</comment>
<file context>
@@ -35,12 +35,28 @@ pub const GIT_SEED_COMMANDS: [&str; 2] = ["git.seed.start", "git.seed.status"];
- Some(SERVICE.get_or_init(|| GitSeedService::new(fs.roots().clone())))
+ Some(SERVICE.get_or_init(|| {
+ #[cfg(debug_assertions)]
+ if let Some(dir) = std::env::var_os(TEST_GITHUB_VAR)
+ .map(std::path::PathBuf::from)
+ .filter(|dir| dir.is_absolute())
</file context>
| stack.push(entry.path()); | ||
| } else { | ||
| let relative = entry.path().strip_prefix(root).unwrap().display().to_string(); | ||
| found.insert(relative, std::fs::read_to_string(entry.path()).unwrap_or_default()); |
There was a problem hiding this comment.
P3: files() turns any read failure into an empty string, so binary or non-UTF-8 files compare equal regardless of content. Read as bytes (std::fs::read) and compare Vec<u8>, or unwrap the read.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/tests/link_seed.rs, line 201:
<comment>`files()` turns any read failure into an empty string, so binary or non-UTF-8 files compare equal regardless of content. Read as bytes (`std::fs::read`) and compare `Vec<u8>`, or unwrap the read.</comment>
<file context>
@@ -0,0 +1,373 @@
+ stack.push(entry.path());
+ } else {
+ let relative = entry.path().strip_prefix(root).unwrap().display().to_string();
+ found.insert(relative, std::fs::read_to_string(entry.path()).unwrap_or_default());
+ }
+ }
</file context>
| /// wire reaches it. | ||
| #[doc(hidden)] | ||
| #[must_use] | ||
| pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self { |
There was a problem hiding this comment.
P3: with_test_github disables the GitHub credential path and enables protocol.file, but it is compiled into release builds. Gate it with #[cfg(debug_assertions)] to match the debug-only seam in installed(), so release builds cannot construct this service.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui-core/src/git_seed/job.rs, line 193:
<comment>`with_test_github` disables the GitHub credential path and enables `protocol.file`, but it is compiled into release builds. Gate it with `#[cfg(debug_assertions)]` to match the debug-only seam in `installed()`, so release builds cannot construct this service.</comment>
<file context>
@@ -183,6 +183,27 @@ impl GitSeedService {
+ /// wire reaches it.
+ #[doc(hidden)]
+ #[must_use]
+ pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self {
+ let local = format!("file://{}/", github.display());
+ let config = Config {
</file context>
| pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self { | |
| #[cfg(debug_assertions)] | |
| pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self { |
| since_last, | ||
| }), | ||
| ("clone", [remote]) if !since_last => { | ||
| let repo = repo.filter(|repo| repo.split('/').count() == 2)?; |
There was a problem hiding this comment.
P3: The --repo check accepts empty owner or name parts such as o/ and /n. Require both parts to be non-empty.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/seed_cli.rs, line 225:
<comment>The `--repo` check accepts empty owner or name parts such as `o/` and `/n`. Require both parts to be non-empty.</comment>
<file context>
@@ -0,0 +1,631 @@
+ since_last,
+ }),
+ ("clone", [remote]) if !since_last => {
+ let repo = repo.filter(|repo| repo.split('/').count() == 2)?;
+ Some(Command::Clone { socket: socket?, repo, git_ref, remote: absolute(remote)? })
+ }
</file context>
| let repo = repo.filter(|repo| repo.split('/').count() == 2)?; | |
| let repo = repo.filter(|repo| { | |
| repo.split_once('/').is_some_and(|(owner, name)| { | |
| !owner.is_empty() && !name.is_empty() && !name.contains('/') | |
| }) | |
| })?; |
| .with_context(|| format!("cannot write {}", part.display()))?; | ||
| file.write_all(&serde_json::to_vec(record)?)?; | ||
| file.sync_all()?; | ||
| std::fs::rename(&part, &path).with_context(|| format!("cannot write {}", path.display())) |
There was a problem hiding this comment.
P3: The rename is not made durable because the parent folder is never synced. After a crash the seed record can revert to an older commit set or disappear, and --since-last then plans from stale state or reports seed.no_previous. Sync the parent folder after the rename, as agent_cli/record.rs does.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/seed_cli/record.rs, line 91:
<comment>The rename is not made durable because the parent folder is never synced. After a crash the seed record can revert to an older commit set or disappear, and `--since-last` then plans from stale state or reports `seed.no_previous`. Sync the parent folder after the rename, as `agent_cli/record.rs` does.</comment>
<file context>
@@ -0,0 +1,92 @@
+ .with_context(|| format!("cannot write {}", part.display()))?;
+ file.write_all(&serde_json::to_vec(record)?)?;
+ file.sync_all()?;
+ std::fs::rename(&part, &path).with_context(|| format!("cannot write {}", path.display()))
+}
</file context>
| #[doc(hidden)] | ||
| #[must_use] | ||
| pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self { | ||
| let local = format!("file://{}/", github.display()); |
There was a problem hiding this comment.
P3: A non-UTF-8 absolute directory accepted by the environment seam is lossy-converted by display(), so the rewritten file:// URL points elsewhere and seeding fails. Encode the OS path without loss or reject it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui-core/src/git_seed/job.rs, line 194:
<comment>A non-UTF-8 absolute directory accepted by the environment seam is lossy-converted by `display()`, so the rewritten `file://` URL points elsewhere and seeding fails. Encode the OS path without loss or reject it.</comment>
<file context>
@@ -183,6 +183,27 @@ impl GitSeedService {
+ #[doc(hidden)]
+ #[must_use]
+ pub fn with_test_github(roots: Roots, github: &std::path::Path) -> Self {
+ let local = format!("file://{}/", github.display());
+ let config = Config {
+ git: Git {
</file context>
…art; LFS on clone; a machine that does not answer identify is an error Review (gpt-6-astra) at aad041c: the bundle was world-readable under umask 022. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cmux-tui/crates/cmux-tui/src/link/seed_cli.rs">
<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/seed_cli.rs:245">
P2: An unanswered `identify` now fails `Machine::read` before any seed or copy attempt, so `push`, `push --since-last` and `clone` never reach the copy fallback. `machine_gate.rs` says an unanswered `identify` should leave the verb's own error, not a gate-specific failure. Either drop this check so an unanswered identify keeps the old `seeds` behaviour, or document the exception in `machine_gate.rs`.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
| /// Refuses a machine too old for `fs-v1` (`machine_outdated`). | ||
| fn read(socket: &'a Path) -> Result<Self, SeedError> { | ||
| let identity = machine_gate::identify(socket); | ||
| if identity.is_none() { |
There was a problem hiding this comment.
P2: An unanswered identify now fails Machine::read before any seed or copy attempt, so push, push --since-last and clone never reach the copy fallback. machine_gate.rs says an unanswered identify should leave the verb's own error, not a gate-specific failure. Either drop this check so an unanswered identify keeps the old seeds behaviour, or document the exception in machine_gate.rs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/seed_cli.rs, line 245:
<comment>An unanswered `identify` now fails `Machine::read` before any seed or copy attempt, so `push`, `push --since-last` and `clone` never reach the copy fallback. `machine_gate.rs` says an unanswered `identify` should leave the verb's own error, not a gate-specific failure. Either drop this check so an unanswered identify keeps the old `seeds` behaviour, or document the exception in `machine_gate.rs`.</comment>
<file context>
@@ -242,6 +242,12 @@ impl<'a> Machine<'a> {
/// Refuses a machine too old for `fs-v1` (`machine_outdated`).
fn read(socket: &'a Path) -> Result<Self, SeedError> {
let identity = machine_gate::identify(socket);
+ if identity.is_none() {
+ return Err(SeedError::new(
+ "sync_failed",
</file context>
|
Review (codex, router-review): SHIP at 9aabf92 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 2. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward. SHIP at 9aabf92 Visual verdict: no images; Leo tries the build
Follow-ups:
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
…ads use get The ratchet at the merge with feat-cmux-next e27f116 (18cef5a, job 9d666b02dcb4c69dbfc147f0) is still red: cmux-tui indexing_slicing 849 > 831. The 18 new hits after #30414 come from: - d8e1b7c (#29234) cli/terminal_talk/send.rs stage_failure: 2 - 9f5e2e1 (#30634) link/seed_cli.rs: 13, link/mod.rs `&args[1..]`: 1 - 7b30784 (#30664) link/machine_pin.rs: 1, link/mod.rs `&args[2..]`: 1 Each is now a get / pointer-free get read, `JsonSet::set` for the one write, and `args.get(n..)` for the slices (the branch runs only when `args.get(n - 1)` matched, so the slice is always there). A missing key read through get gives None where indexing gave Null: `as_str()` and `as_bool()` see the same, and json! writes None as null. No behavior change; the baseline is not raised.
|
This PR added new |
…0414, #29234, #30634, #30664 and #30574 use checked forms (#30661) * base red: panic ratchet, #30414's new index and string slices in start_chat_cli use checked forms The Rust panic ratchet is red on feat-cmux-next at 7e0a0f2 (tip watcher job 21c6ba0ebda787821cc7bc4b): cmux-tui indexing_slicing 833 > 831 and string_slice 84 > 82. All four hits are from 0507fc4 (#30414) in cmux-tui/src/link/start_chat_cli.rs: - valid_environment: `id[4..]` -> `id.get(4..)`; the id already starts with the ASCII "env_", so the slice is always Some and the same. - github_repo: `&url[scheme.len()..]` -> `split_at_checked`, None in exactly the cases where the `get(..scheme.len())` before it was None. - Ensure::body: `body["ref"] = ..`, `body["environment"] = ..` -> `JsonSet::set` on the same object literal (same insert). No behavior change; the baseline is not raised. * base red: panic ratchet, #29234's, #30634's and #30664's new index reads use get The ratchet at the merge with feat-cmux-next e27f116 (18cef5a, job 9d666b02dcb4c69dbfc147f0) is still red: cmux-tui indexing_slicing 849 > 831. The 18 new hits after #30414 come from: - d8e1b7c (#29234) cli/terminal_talk/send.rs stage_failure: 2 - 9f5e2e1 (#30634) link/seed_cli.rs: 13, link/mod.rs `&args[1..]`: 1 - 7b30784 (#30664) link/machine_pin.rs: 1, link/mod.rs `&args[2..]`: 1 Each is now a get / pointer-free get read, `JsonSet::set` for the one write, and `args.get(n..)` for the slices (the branch runs only when `args.get(n - 1)` matched, so the slice is always there). A missing key read through get gives None where indexing gave Null: `as_str()` and `as_bool()` see the same, and json! writes None as null. No behavior change; the baseline is not raised. * base red: panic ratchet, #30574's new index reads in start_chat_cli use get #30574 (93772c4) landed after the e27f116 merge and added four Value index reads in start-chat plan (line["machine"], ["created"], ["snapshot"], ["environment"]). They are now get reads: a missing key is None where indexing gave Null, as_str() sees the same and json! writes None as null. No behavior change.
|
Found 7 test failures on Blacksmith runners: Failures
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Piece 4 of the Cloud project seed (leo-workspace
design/cloud.md, "Getting the project onto the machine"), the CLI half. Leo, on the start screen's line:This adds
cmux link seed pushandcmux link seed clone, which put a project on a Cloud machine the way the design says: a git folder with a GitHub remote is seeded (the machine clones the remote, this Mac sends one bundle of what the remote lacks), and everything else takes the streamed copy. It builds on #30464 (the Mac-side plan), #30382 (the machine'sgit-seed-v1), #30427 (the copy fallback) and #30280 (the move pump's--sync).start-chat copyand the app switch to it in the next PR.CLI
push reads
identify. A machine withoutfs-v1answersmachine_outdated. Otherwise it plans (seed::plan), uploads the bundle throughfs-v1toREMOTE/../.cmux-seed-<name>-<id>.bundle(inside the fs roots), callsgit.seed.start, and readsgit.seed.statusonce every 500 ms until the job ends or 45 minutes pass. That wait is a bounded loop inside the CLI process, not an agent polling loop. The upload is deleted afterwards either way.Fallback to the copy (
sync_cli::push, a full copy) happens when:Fallback(not_git, not_top, no_commit, no_remote, ambiguous_remote, not_github, no_base, unmerged, submodule_local_work, git_failed);git-seed-v1(machine_outdated);seed.needs_github,seed.base_missingorseed.clone_failed(orseed.unavailable).seed.base_missingfirst re-plans once from the remote default branch's tip (plan_with_basewithPlanned::bases' last). If the failed seed made the folder, the folder is removed before the copy. The answer then saysmode:"copy",fallback_codeandfallback_why. Forneeds_githubit also carriesinstall_url, so the app can offer the install while the chat goes on.--since-lastnever falls back to a full copy:plan_since). Nothing changed:unchanged:trueand no upload at all.copy), it re-pushes the copy to the same REMOTE, which the copy's manifest makes incremental.seed.no_previous. The machine'sseed.since_mismatchandseed.machine_changedcome back as errors.Record:
<state>/link-seed/<sha256(local NUL remote)>.json(0600, folder 0700), beside sync_cli's<state>/link-syncbase record. It holds{mode, machine (identify's session_id), url, head, index, work}.clone: the clone-only
git.seed.start. It answers{ok, mode:"clone", head, head_ref}, or the error code plusinstall_url.agent move
--sync seed: once the chat is frozen, the pump callsseed_cli::push_since_last. An error gives the chat back, as--sync pushdoes, withseed_cli::failure_code's code.Output
stderr progress JSON lines (throttled to one per 250 ms, first and last always):
{"progress":{"phase":"upload","bytes_done","bytes_total"}}while the bundle uploads;{"progress":{"phase":"clone|fetch|bundle|checkout|submodules|lfs","bytes_done","objects","objects_total"}}from the machine's job;{"progress":{"files_done","files_total","bytes_done","bytes_total"}}for a copy.stdout, one line:
Test seam
git-seed-v1got a debug-build-only seam,CMUX_TEST_GIT_SEED_GITHUB=<dir>. It makes the daemon's seeds clonehttps://github.com/O/N.gitfromfile://<dir>/O/N.gitwith no credential.GitSeedService::with_test_githubdoes the same for in-process tests. Release builds never read the variable, and nothing on the wire reaches it.Tests (Linux,
cmux-ci test)tests/link_seed.rs: a realFsServiceandGitSeedServicebehind a socket, with afile://bare repo as GitHub. It covers:.envskipped by name, ignored folder left out, no bundle left behind), then a delta, then an unchanged delta that makes nofs.write;--since-lasterrors:seed.no_previous(and nothing copied),seed.machine_changed,seed.since_mismatch;not_git, then a copy-mode--since-lastre-push that writes only the new file), for a GitLab remote (not_github) and for a repo the machine cannot clone (seed.clone_failed);git-seed-v1(machine_outdated);seed clone, ok andseed.clone_failed.tests/cloud_move_journey.rs(two real daemons): the journey now seeds the dirty folder withcmux link seed push(modeseed, git status identical on the VM). It then moves the chat mid-turn with--sync seed, and the turn's last edit (src/turn.txt, written after the seed) arrives through the delta bundle.Risk: the change is additive.
start-chat copyand the app still calllink sync pushuntil the next PR. The only edit to existing behaviour is the agent move's--syncparse (push and pull unchanged) and two smallsync_clihelpers (upload,is_refusal).After review: the bundle is written in a private 0700 folder (git writes it world-readable under umask 022); a failed big upload's
.cmux-sync-partis deleted too;seed cloneasks for LFS files; a machine that does not answeridentifyis an error.Before a fallback copy's own lines, stderr carries one
{"progress":{"phase":"copy","fallback_code","install_url"?}}, so the card can say why while the copy runs.Follow-ups: #30644 (
--since-lastchecks the machine's folder before answering unchanged); a full re-seed into a folder that already holds a seed fetches into it instead of starting clean; submodules with local work still fall back to the copy (plan'ssubmodule_local_work).Agent session: agent session
🤖 Generated with Claude Code