Skip to content

Cloud copy fallback: name every skip, skip heavy folders and secrets outside git, progress, resume, disk_full, refuse system folders - #30427

Merged
teamleaderleo merged 5 commits into
feat-cmux-nextfrom
copy-fallback
Nov 1, 2026
Merged

teamleaderleo merged 5 commits into
feat-cmux-nextfrom
copy-fallback

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Leo, 2026-10-31, on the start screen's line "Your project folder is copied up.":

Your project folder is copied up. / we'll want to deal with the edge cases for this too

This hardens the streamed copy (cmux link sync push, cmux link start-chat copy), per leo-workspace design/cloud.md, "Getting the project onto the machine", rules 4, 5 and 7. The copy stays the fallback for non-git folders, repos without a GitHub remote and machines without git-seed-v1; the git seed path is being built separately (link/seed/).

What changes

Heavy folders outside git (sync_cli/skips.rs heavy). In a non-git folder, a chat's copy skips the dependency and build folders the project rebuilds on the machine. These are always skipped: node_modules, .venv, __pycache__, .next, .nuxt, DerivedData, .gradle, .tox, .mypy_cache, .pytest_cache, .cache, .turbo and .parcel-cache. The ambiguous names are skipped only next to the file that makes them a build folder:

  • target: beside Cargo.toml or pom.xml.
  • build: beside package.json, a Gradle file, setup.py or pyproject.toml.
  • dist: beside package.json, setup.py or pyproject.toml.
  • Pods: beside a Podfile.
  • venv: when it holds a pyvenv.cfg.

Inside git nothing changes, since ignored files are already skipped. A cmux link access share (push_kept) also keeps every file as before (Rules::Share). A top-level .gitignore is not read outside git (optional in the brief, left out to keep this conservative).

Secrets outside git. .env*, id_rsa*, id_ed25519*, id_ecdsa*, id_dsa*, *.pem, *.p12, *.pfx and *.key are skipped by default and named.

Named skips. The summary keeps skipped (the total) and adds skipped_files (the first 200, sorted by path) and skipped_counts:

{"ok":true,"written":N,"unchanged":N,"deleted":N,"skipped":N,"bytes":N,
 "skipped_files":[{"path":"node_modules/","kind":"heavy","why":"a heavy folder rebuilt on the machine"}, ...],
 "skipped_counts":{"heavy":N,"secret":N,"symlink":N,"special":N,"name":N,"reserved":N,"other":N}}

Each why is one of these:

  • "secret: .env files are shared only on purpose"
  • "secret: key files are shared only on purpose"
  • "a symlink (to a place inside the folder)", "a symlink (to a place outside the folder)" or "a symlink (to nothing)"
  • "a socket, FIFO or device"
  • "a name Linux can't hold (too long | control character | not UTF-8)"
  • "a heavy folder rebuilt on the machine": one entry per folder, path ending in /
  • "the name of the sync record"

other counts skips that are not named: in git, the untracked .env* files and non-UTF-8 paths, and a share's own refusals. Those git lines sit right next to #30000's change to local.rs, so naming them waits until #30000 lands (follow-up). Non-git names that are not UTF-8 used to be dropped without being counted; now they are named. Case collisions are left out because they are not a real failure on Linux.

Progress. push_inner takes an optional progress callback. cmux link sync push and cmux link start-chat copy print progress on stderr as JSON lines: the totals before anything is written, then at most one line every 250 ms, then the end. The stdout answer line keeps its shape.

{"progress":{"files_done":N,"files_total":N,"bytes_done":N,"bytes_total":N}}

files_done and bytes_done count files that were written or found unchanged.

Resume.

  • Push. The manifest on the machine is now also saved during the push: after every 500 written files (or a twentieth of the push's files, when that is more) or every 256 MiB. A saved manifest lists what was written so far plus the earlier push's records for files not reached yet. The git state stays the earlier push's until the end, and this Mac's base (what a pull trusts) is still saved only at the end. A re-push skips what already landed, through the per-file hash, permissions and revision check.
  • start-chat copy. A retry of the same send on the same machine whose copy did not finish (cut off, answer lost, process gone) reuses that copy's folder from the ledger (Ledger::copy_place) instead of a new random one.
  • Two copies of the same send at once still never share a folder. Each copy holds a Lease on its folder while its push runs: an flock on <state>/cloud-start-chat/copies/<hash>.lock, which the system releases when the process ends or is killed. A retry that finds the folder held by a live copy takes a new folder.
  • After taking the lease, the ledger is read again. A copy that finished in between is answered as finished and is never pushed over.
  • No chat opens in a folder before its copy finishes, so resuming never writes over a chat's changes. An abandoned half-copied folder (its send never retried on that machine) stays on the machine, as before, and is documented.

Disk full. The machine's fs.no_space (ENOSPC/EDQUOT in fs_ops/error.rs) answers "error_code":"disk_full","error":"The Cloud machine's disk is full", for both sync push and start-chat copy. The App already shows the error text.

Too-broad folders. start-chat copy refuses with "error_code":"folder_too_broad" these folders, compared exactly, both as spelled and as resolved:

  • / and the home folder itself.
  • /System, /Library, /Applications, /Users, /usr, /bin, /sbin, /private, /etc, /var, /tmp, /opt and /Volumes.
  • /home and /root, for Linux.

A project below one of them is still copied. Swift's CloudMoveFolder.isTooBroad is unchanged.

Swift progress: not in this PR. ProcessRunner writes the CLI's stderr to a temp file and reads it only after the process exits. Showing "Copying 1,204 of 8,310 files (312 MB of 2.1 GB)" as the step text needs a streaming reader and step plumbing in CloudStartChat, which is more than a few lines, so it is a follow-up.

Merges with

Tests

Run on the fleet with cmux-ci test: cargo test -p cmux-tui --test link_sync, the link::start_chat_cli / link::sync_cli / link::access_cli lib tests, and cargo clippy -p cmux-tui --all-targets -- -D warnings.

tests/link_sync.rs:

  • outside_git_heavy_folders_secrets_and_links_stay_and_are_named: covers heavy folders (and a build/ with no project file beside it, which is kept), .env*, keys, a symlink inside the folder, a symlink outside it, a FIFO, the reasons and the counts.
  • a_push_reports_its_progress_against_the_totals: the first line holds the totals, the last line is at the totals, and the answer keeps its shape.
  • a_retry_of_an_interrupted_push_writes_only_what_had_not_landed: the fake machine refuses writes after 700 of 1200. The retry finds 500 unchanged (the saved manifest) and writes 700.
  • a_full_disk_on_the_machine_answers_disk_full.

start_chat_cli/tests.rs:

  • a_retry_of_a_copy_that_did_not_finish_resumes_its_folder: this replaces a_copy_whose_answer_was_lost_is_never_written_over, whose assertion flips by design.
  • a_retry_resumes_its_folder_only_when_no_live_copy_holds_it.
  • two_copies_of_one_send_at_once_never_share_a_folder: unchanged, still passes.
  • a_copy_of_the_root_a_system_folder_or_home_is_refused.

Unit tests are in skips.rs.

Changelog

Improved: copying a project to a Cloud machine skips dependency and build folders and secrets outside git, names every skipped file with the reason, reports progress, resumes an interrupted copy, and says plainly when the machine's disk is full.

Risk: normal. The changes are in the copy path only. A cmux link access share keeps its file set.

Agent session: agent session

🤖 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

Hardens the streamed copy (cmux link sync push, cmux link start-chat copy) so skips are named, heavy folders and secrets stay home outside git, interrupted copies resume, and the machine's limits are reported plainly.

Skips outside git

  • Dependency and build folders (node_modules, .venv, target beside Cargo.toml, build beside package.json, etc.) and secrets (.env*, SSH keys, *.pem, *.key) are now skipped and named with a plain reason.
  • The summary adds skipped_files (first 200, sorted by path) and skipped_counts; each file carries path, kind, and why.
  • Inside git nothing changes — ignored files were already skipped — and a cmux link access share keeps its file set.

Copy resilience

  • sync push and start-chat copy print JSON progress on stderr (totals first, then at most every 250 ms, then the end); the stdout answer keeps its shape.
  • The manifest is saved during the push (every 500 files or 256 MiB), so a re-push skips what already landed.
  • A retried start-chat copy resumes its unfinished copy's folder, held by an flock lease so two live copies never share one.
  • The machine's full disk answers "error_code":"disk_full" with "The Cloud machine's disk is full".
  • start-chat copy refuses the root, system top folders, and the home folder itself with folder_too_broad; a project below one of them is still copied.

Written for commit 1541610. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 5 commits November 1, 2026 00:01
…outside git, progress, resume, disk_full; start-chat copy resumes its folder and refuses system folders

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5
…older is never pushed over

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
…(merges cleanly with #30002)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3bab30503f0b9a7059e5

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 7 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/src/link/sync_cli/skips.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/sync_cli/skips.rs:95">
P3: `canonicalize` errors are not all dangling-link errors, so inaccessible symlinks are reported as “to nothing.” Distinguish `NotFound` from other failures or add an unknown/inaccessible symlink reason.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui/src/link/sync_cli/skips.rs:146">
P2: `secret()` does not match credential files such as `.netrc`, `.npmrc`, or `.pgpass`, which hold plaintext tokens or passwords and would be pushed outside git. Add them to the matched names if they are meant to be secrets, or state the narrower scope in the doc comment.</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:428">
P2: Push progress stops for the entire duration of each file because this callback runs only after `write` completes. Pass the progress reporter into the file-write callback and report during large-file transfers so a multi-gigabyte copy does not appear stalled.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui/src/link/sync_cli/local.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/sync_cli/local.rs:25">
P2: The new skip summary still silently omits Git files rejected by `add_git`—including hooks, lock files, and non-allowlisted metadata. Name or count those exclusions too, or narrow the “every skip” guarantee.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui/src/link/sync_cli/local.rs:308">
P2: Submodules and nested repositories are directories, so this arm labels them "a socket, FIFO or device". Add a directory arm that counts them, or add a named reason for folders in skips.rs.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs:310">
P1: The broad-folder guard can be bypassed with `--local //` or `--local ////`: trimming all trailing slashes produces an empty path, while `sync_cli::push` later canonicalizes that input to `/`. Normalize an all-slash path to `/` before checking it.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs:314">
P2: The system-folder check misses canonical aliases such as `/private/etc` and `/private/var`, allowing selected folders to bypass the refusal. Compare against canonicalized `TOO_BROAD` entries as well as their literal spellings.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

/// folder, or the home folder `home` itself, as spelled or as it resolves
/// (`/etc` is `/private/etc` on a Mac).
pub(super) fn too_broad(local: &str, home: Option<&Path>) -> bool {
let spelled = PathBuf::from(if local.len() > 1 { local.trim_end_matches('/') } else { local });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The broad-folder guard can be bypassed with --local // or --local ////: trimming all trailing slashes produces an empty path, while sync_cli::push later canonicalizes that input to /. Normalize an all-slash path to / before checking 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/src/link/start_chat_cli.rs, line 310:

<comment>The broad-folder guard can be bypassed with `--local //` or `--local ////`: trimming all trailing slashes produces an empty path, while `sync_cli::push` later canonicalizes that input to `/`. Normalize an all-slash path to `/` before checking it.</comment>

<file context>
@@ -278,7 +278,43 @@ fn push(socket: &Path, local: &Path, remote: &str) -> Result<(), (String, String
+/// folder, or the home folder `home` itself, as spelled or as it resolves
+/// (`/etc` is `/private/etc` on a Mac).
+pub(super) fn too_broad(local: &str, home: Option<&Path>) -> bool {
+    let spelled = PathBuf::from(if local.len() > 1 { local.trim_end_matches('/') } else { local });
+    let resolved = std::fs::canonicalize(&spelled).ok();
+    let home_resolved = home.and_then(|home| std::fs::canonicalize(home).ok());
</file context>
Suggested change
let spelled = PathBuf::from(if local.len() > 1 { local.trim_end_matches('/') } else { local });
let trimmed = local.trim_end_matches('/');
let spelled = PathBuf::from(if trimmed.is_empty() { "/" } else { trimmed });

return Some(Why::EnvFile);
}
let lower = name.to_ascii_lowercase();
let key = ["id_rsa", "id_ed25519", "id_ecdsa", "id_dsa"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: secret() does not match credential files such as .netrc, .npmrc, or .pgpass, which hold plaintext tokens or passwords and would be pushed outside git. Add them to the matched names if they are meant to be secrets, or state the narrower scope in the doc comment.

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/skips.rs, line 146:

<comment>`secret()` does not match credential files such as `.netrc`, `.npmrc`, or `.pgpass`, which hold plaintext tokens or passwords and would be pushed outside git. Add them to the matched names if they are meant to be secrets, or state the narrower scope in the doc comment.</comment>

<file context>
@@ -0,0 +1,222 @@
+        return Some(Why::EnvFile);
+    }
+    let lower = name.to_ascii_lowercase();
+    let key = ["id_rsa", "id_ed25519", "id_ecdsa", "id_dsa"]
+        .iter()
+        .any(|prefix| lower.starts_with(prefix))
</file context>

bytes_total: set.files.values().map(|file| size(file).unwrap_or(0)).sum(),
..Progress::default()
};
if let Some(report) = progress.as_mut() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Push progress stops for the entire duration of each file because this callback runs only after write completes. Pass the progress reporter into the file-write callback and report during large-file transfers so a multi-gigabyte copy does not appear stalled.

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 428:

<comment>Push progress stops for the entire duration of each file because this callback runs only after `write` completes. Pass the progress reporter into the file-write callback and report during large-file transfers so a multi-gigabyte copy does not appear stalled.</comment>

<file context>
@@ -338,9 +414,20 @@ fn push_inner(
+        bytes_total: set.files.values().map(|file| size(file).unwrap_or(0)).sum(),
+        ..Progress::default()
+    };
+    if let Some(report) = progress.as_mut() {
+        report(done);
     }
</file context>

//! machine ([`skips::heavy`]) and secrets (`.env*`, key files: [`skips::secret`]).
//! Symlinks, special files and names the machine cannot hold (control
//! characters, over 255 bytes, not UTF-8) are skipped too. Every skip is
//! named with why ([`Skips`]), except the git path's untracked `.env*` and

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new skip summary still silently omits Git files rejected by add_git—including hooks, lock files, and non-allowlisted metadata. Name or count those exclusions too, or narrow the “every skip” guarantee.

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/local.rs, line 25:

<comment>The new skip summary still silently omits Git files rejected by `add_git`—including hooks, lock files, and non-allowlisted metadata. Name or count those exclusions too, or narrow the “every skip” guarantee.</comment>

<file context>
@@ -17,9 +17,13 @@
+//! machine ([`skips::heavy`]) and secrets (`.env*`, key files: [`skips::secret`]).
+//! Symlinks, special files and names the machine cannot hold (control
+//! characters, over 255 bytes, not UTF-8) are skipped too. Every skip is
+//! named with why ([`Skips`]), except the git path's untracked `.env*` and
+//! non-UTF-8 paths, which are counted.
 
</file context>

);
}
Ok(_) => set.skipped += 1,
Ok(meta) => set.skips.note_entry(&relative, path, &meta),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Submodules and nested repositories are directories, so this arm labels them "a socket, FIFO or device". Add a directory arm that counts them, or add a named reason for folders in skips.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/sync_cli/local.rs, line 308:

<comment>Submodules and nested repositories are directories, so this arm labels them "a socket, FIFO or device". Add a directory arm that counts them, or add a named reason for folders in skips.rs.</comment>

<file context>
@@ -284,7 +305,7 @@ fn add(set: &mut LocalSet, relative: String, path: &Path) -> anyhow::Result<()>
             );
         }
-        Ok(_) => set.skipped += 1,
+        Ok(meta) => set.skips.note_entry(&relative, path, &meta),
         Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
         Err(error) => return Err(error).with_context(|| format!("cannot read {}", path.display())),
</file context>
Suggested change
Ok(meta) => set.skips.note_entry(&relative, path, &meta),
Ok(meta) if meta.is_dir() => set.skipped += 1,
Ok(meta) => set.skips.note_entry(&relative, path, &meta),

let resolved = std::fs::canonicalize(&spelled).ok();
let home_resolved = home.and_then(|home| std::fs::canonicalize(home).ok());
[Some(spelled), resolved].into_iter().flatten().any(|path| {
TOO_BROAD.iter().any(|broad| path == Path::new(broad))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The system-folder check misses canonical aliases such as /private/etc and /private/var, allowing selected folders to bypass the refusal. Compare against canonicalized TOO_BROAD entries as well as their literal spellings.

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/start_chat_cli.rs, line 314:

<comment>The system-folder check misses canonical aliases such as `/private/etc` and `/private/var`, allowing selected folders to bypass the refusal. Compare against canonicalized `TOO_BROAD` entries as well as their literal spellings.</comment>

<file context>
@@ -278,7 +278,43 @@ fn push(socket: &Path, local: &Path, remote: &str) -> Result<(), (String, String
+    let resolved = std::fs::canonicalize(&spelled).ok();
+    let home_resolved = home.and_then(|home| std::fs::canonicalize(home).ok());
+    [Some(spelled), resolved].into_iter().flatten().any(|path| {
+        TOO_BROAD.iter().any(|broad| path == Path::new(broad))
+            || home.is_some_and(|home| path == home)
+            || home_resolved.as_deref().is_some_and(|home| path == home)
</file context>

/// whose name the machine can hold: noted with why.
pub(super) fn note_entry(&mut self, relative: &str, path: &Path, meta: &std::fs::Metadata) {
let why = if meta.file_type().is_symlink() {
match std::fs::canonicalize(path) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: canonicalize errors are not all dangling-link errors, so inaccessible symlinks are reported as “to nothing.” Distinguish NotFound from other failures or add an unknown/inaccessible symlink reason.

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/skips.rs, line 95:

<comment>`canonicalize` errors are not all dangling-link errors, so inaccessible symlinks are reported as “to nothing.” Distinguish `NotFound` from other failures or add an unknown/inaccessible symlink reason.</comment>

<file context>
@@ -0,0 +1,222 @@
+    /// whose name the machine can hold: noted with why.
+    pub(super) fn note_entry(&mut self, relative: &str, path: &Path, meta: &std::fs::Metadata) {
+        let why = if meta.file_type().is_symlink() {
+            match std::fs::canonicalize(path) {
+                Ok(target) if target.starts_with(&self.top) => Why::SymlinkInside,
+                Ok(_) => Why::SymlinkOutside,
</file context>

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 1541610: 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=pr30427 -f ref=copy-fallback -f platform=macos -f backend_mode=local

Shipped in:

@github-actions github-actions Bot added the risk:high Risk tier (docs/ci/risk-tiers.md) label Nov 1, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex, router-review): SHIP at 1541610

Reviewer: codex gpt-6.1-sol xhigh, fast tier.

Follow-ups: 3. Each lands as an issue in this repo that links this receipt; the lane that opened the PR fixes it forward.

SHIP at 1541610

Visual verdict: no images; Leo tries the build.

  1. [P1][follow-up] start_chat_cli.rs:343-349 rejects --local / during argument parsing, so the CLI emits usage and exit 64 instead of the promised JSON folder_too_broad refusal.

  2. [P2][follow-up] sync_cli/skips.rs:163 treats Podfile.lock alone as sufficient to skip Pods/, although the contract requires a neighboring Podfile. This can omit files from otherwise valid projects.

  3. [P2][follow-up] sync_cli/skips.rs:86-88,123-124 truncates skipped entries before sorting. Since read_dir order is unspecified, skipped_files is not reliably the lexicographically first 200 paths.

Follow-ups:

  • [P1][follow-up] Return folder_too_broad for /.
  • [P2][follow-up] Require Podfile specifically for Pods/.
  • [P2][follow-up] Sort all candidates or maintain the sorted first-200 set before truncation.

@cmux-bot cmux-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmux-bot approves 1541610: an independent review passed this exact head (router-review receipt by teamleaderleo). A push needs a new review; a later BLOCK at this head dismisses this approval.

This branch was previously deployed

3 inactive deployments
artifacts — 1541610f Deployed Nov 1, 2026 by teamleaderleo via add the Windows daemon to the tree #94643
sentry-dsyms — 1541610f Deployed Nov 1, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #94643
ci-routing-agent — 1541610f Deployed Nov 1, 2026 by teamleaderleo via cmux-tui tree preflight #94643
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant