Skip to content

Cloud: share repository discovery, attachment and recovery across agent tools and CLI - #30361

Merged
teamleaderleo merged 8 commits into
feat-cmux-nextfrom
lane/cloud-repo-tools-2
Nov 1, 2026
Merged

teamleaderleo merged 8 commits into
feat-cmux-nextfrom
lane/cloud-repo-tools-2

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Cloud chats need one repository contract for the UI and agents: discover a bare name, attach its clone to the current chat, and recover from missing GitHub access without another user message. This adds Rust daemon cloud.repo.list, cloud.repo.add, attachment/history reads and GitHub recovery waiting, exposed through cmux cloud repo, cmux mcp, and Cloud-only acpmux list_repos / add_repo.

Leo: “i want us to be able to do this too”; “and this kind of recovery”; “tho i think this kinda thing can be more ergonomic”.

Name resolution reads GitHub remotes from the Mac daemon's project registry, synchronizes only repository names over the existing authenticated machine link, then ranks recent Cloud attachments and the GitHub App catalog. Mac-authorized installation links travel with that catalog; VM credentials retain their existing read-only connector permissions. The machine calls the connector through its root agent socket's new github_repos key. Attached repo records and clone job IDs persist in versioned daemon projections; retries reuse the same clone job or attachment. Clones use only git-seed-v1 start/status, under ~/chats/<chat-id>/<name>.

The protocol and exact JSON are documented in plans/cmux-next/cloud-repo-actions.md. The matching app work synchronizes at startup and before repository actions, renders Session Info, and replays retry.args after cloud.repo.wait_github reports connected. The clone-only seed implementation and matching app forwarding are dependencies for the full DEV flow; development already enables the connector.

Validation: fake connector and Unix-socket seed tests cover resolution priority, ambiguity, public repos, every typed failure, automatic retry, job resume, persistence, and custom daemon sockets. Fleet Rust validation at 1de7dee passed all 501 tests selected by scripts/ci/cmux-tui-rust-check.sh test repo, including the connector and repository adapters (fleet step 67595b9ab2eccde8a50507da). Static prepush passes. The broader local static runner reports existing localization parity and workspace package-grouping failures in untouched files.

Review follow-ups: #30388, #30391, #30392 and #30393 track tool discovery, timeout alignment, URL spelling and additional deadline coverage. Recovery, caller binding and completed-job persistence have immediate fix-forward commits prepared.

Risk: additive daemon/link surface, durable repository metadata and remote clone orchestration. Only authenticated Cloud machine links receive the remote actions; Mac-local discovery/remember are excluded from the link gate. Requests run on bounded workers; clone waits stop after nine minutes, connection waits after at most sixty seconds plus bounded socket I/O, and sockets/workers are released on completion. There is no idle timer. This coherent contract spans the host bridge, daemon, CLI and agent tools; its size includes fake transport and persistence tests, and splitting those surfaces would expose unusable intermediate actions.

Agent session: agent session

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

@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.

20 issues found and verified against the latest diff

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/cloud_repos/runtime.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime.rs:104">
P2: `wait_github` checks its deadline only after `connector(None)` completes, so a zero or short timeout can block for the connector's 45-second scan. Check the deadline before each poll and bound discovery to the remaining duration, or use a lightweight connection-state request.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime.rs:214">
P2: Concurrent adds for the same chat and repository return `seed.clone_failed` instead of joining the in-flight clone and reusing its result. Await the per-key operation, then re-check the attachment or saved job under the lock.

(Based on your team's feedback about coalescing concurrent duplicate operations.)</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs:29">
P2: This sync reads only the first 500 projects, so repositories in projects 501–1000 never enter the local catalog and bare-name adds can incorrectly return `repo.not_found`. Load all retained projects or page the registry before scanning remotes.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs:45">
P2: `CATALOG_DEADLINE` does not bound this `std::fs::metadata` call, so a stale network or FUSE project path can block a worker indefinitely before the timed Git read. Bound the filesystem probe or remove this unbounded preflight so blocked paths cannot exhaust the cloud-repo workers.</violation>

<violation number="3" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs:120">
P2: This rejects otherwise valid GitHub remotes with standard ports, so repository discovery misses projects using `https://github.com:443/...` or `ssh://git@github.com:22/...`. Accept standard GitHub ports and normalize them away when exporting the owner/name.

(Based on your team's feedback about explicit GitHub ports.) [1cc7fee0-7938-45ba-a78a-423b41a5b010]</violation>
</file>

<file name="cmux-tui/crates/cmux-tui/src/cli/cloud_help.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/cli/cloud_help.rs:484">
P3: The Japanese repository help omits the `--socket` routing hint shown in English, leaving Japanese users without guidance for targeting a Cloud machine daemon. Add the equivalent hint to the Japanese heading.</violation>
</file>

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

<violation number="1" location="cmux-tui/crates/cmux-tui/src/cli.rs:891">
P2: `scan_globals` accepts an option token as the chat ID after `cloud repo --session`; `--session --json` therefore runs against chat `--json` instead of reporting a missing value. Reject `--`-prefixed next tokens before appending them, matching `global_value`.\n\n(Based on your team's feedback about rejecting option-like session values.)</violation>
</file>

<file name="cmux-tui/crates/cmux-host/src/cloud/github_minter.rs">

<violation number="1" location="cmux-tui/crates/cmux-host/src/cloud/github_minter.rs:50">
P3: Update the `Ask` enum documentation to include repository catalog requests; it now claims connections carry only token or hand-off asks, leaving generated docs and maintainers unaware of `GithubRepos`.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/server/fs_wire.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/server/fs_wire.rs:45">
P3: `FsGate` now admits five remote repository commands, but its contract comment still says it admits only seven `fs-v1` operations and denies every other frame. Update the documentation to include the repository allowlist so the gate’s security boundary is not misread.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui/src/cli/mcp/cloud_repo_tools.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/cli/mcp/cloud_repo_tools.rs:77">
P2: Large repository lists are reported as successful even when `success` replaced them with a `result.too_large` error envelope. Preserve the error flag returned by `success` instead of unconditionally overwriting it, otherwise clients may not retry or narrow the failed list request.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime_tests.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime_tests.rs:41">
P2: This socket helper can fail spuriously when a signal interrupts `poll`, causing the whole test to panic instead of waiting for the listener. Retry `EINTR` before asserting readiness.</violation>
</file>

<file name="cmux-tui/crates/cmux-host/src/cloud/github_repos.rs">

<violation number="1" location="cmux-tui/crates/cmux-host/src/cloud/github_repos.rs:11">
P1: Repository discovery calls routes that the current backend does not expose. Align the catalog, install, and repository paths/origin with the deployed API routes, or add the corresponding platform routes before relying on this agent action.</violation>

<violation number="2" location="cmux-tui/crates/cmux-host/src/cloud/github_repos.rs:116">
P2: The connector response can make the UI open any HTTPS host as the GitHub installation link. Validate the parsed URL and require the expected `github.com` host before returning `install_url`, matching the existing synchronized-link validation.</violation>

<violation number="3" location="cmux-tui/crates/cmux-host/src/cloud/github_repos.rs:189">
P1: Valid backend repository rows use `full_name`, but this parser only reads `fullName`, so every non-empty listing fails with `integration.bad_answer`. Read `full_name` here and keep the existing output normalization if the agent contract requires camelCase.</violation>

<violation number="4" location="cmux-tui/crates/cmux-host/src/cloud/github_repos.rs:215">
P2: A 409 GitHub relink response uses the API's `error.code = "needs_reauth"`, not `_tag = "ConnectorNeedsAuth"`; the current match therefore reports an unavailable integration instead of returning the Connect GitHub recovery action. Accept the API error code here (while retaining the legacy tag if required).</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/store.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/store.rs:86">
P2: This count limit does not protect the projection-size limit: a valid large Mac catalog can exceed 1 MiB and make synchronization fail as `seed.clone_failed` instead of updating the catalog. Validate the serialized projection size before writing, or bound/truncate the catalog to fit the projection budget.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui/src/cli/cloud_repo.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui/src/cli/cloud_repo.rs:37">
P2: This command bypasses `global.output`, so `--quiet` still prints results and JSON-mode failures go to stdout. Route both success and failure through the existing `wire::print_local_success`/`print_local_error` helpers.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui/src/cli/cloud_repo.rs:174">
P2: The 70-second client deadline is shorter than the daemon's maximum 60-second GitHub wait plus one bounded connector I/O interval, so a valid 60-second wait can be reported as a transport failure before the daemon replies. Give `cloud.repo.wait_github` enough headroom for the connector timeout.</violation>
</file>

<file name="plans/cmux-next/cloud-repo-actions.md">

<violation number="1" location="plans/cmux-next/cloud-repo-actions.md:21">
P2: The contract promises all catalog pages, but `Runtime::connector` stops at 100 pages or 45 seconds and returns an error. Document those bounds here or change the implementation so the stated all-pages behavior is true.</violation>

<violation number="2" location="plans/cmux-next/cloud-repo-actions.md:67">
P2: The plan documents Mac startup sync, Session Info rendering, automatic retry, and attachment forwarding, but this PR contains no app integration or callers for these actions. Add those app changes or mark these flows as not yet implemented instead of claiming they ship here.</violation>
</file>

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

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

let mut repos = Vec::with_capacity(rows.len());
for row in rows {
let name =
row.get("fullName").and_then(Value::as_str).filter(|name| valid_repo(name));

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: Valid backend repository rows use full_name, but this parser only reads fullName, so every non-empty listing fails with integration.bad_answer. Read full_name here and keep the existing output normalization if the agent contract requires camelCase.

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-host/src/cloud/github_repos.rs, line 189:

<comment>Valid backend repository rows use `full_name`, but this parser only reads `fullName`, so every non-empty listing fails with `integration.bad_answer`. Read `full_name` here and keep the existing output normalization if the agent contract requires camelCase.</comment>

<file context>
@@ -0,0 +1,220 @@
+            let mut repos = Vec::with_capacity(rows.len());
+            for row in rows {
+                let name =
+                    row.get("fullName").and_then(Value::as_str).filter(|name| valid_repo(name));
+                let private = row.get("private").and_then(Value::as_bool);
+                let (Some(name), Some(private)) = (name, private) else {
</file context>

use super::sender::Answer;

pub const REQUEST_KEY: &str = "github_repos";
const REPOS_PATH: &str = "/v1/connectors/github/repos";

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: Repository discovery calls routes that the current backend does not expose. Align the catalog, install, and repository paths/origin with the deployed API routes, or add the corresponding platform routes before relying on this agent action.

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-host/src/cloud/github_repos.rs, line 11:

<comment>Repository discovery calls routes that the current backend does not expose. Align the catalog, install, and repository paths/origin with the deployed API routes, or add the corresponding platform routes before relying on this agent action.</comment>

<file context>
@@ -0,0 +1,220 @@
+use super::sender::Answer;
+
+pub const REQUEST_KEY: &str = "github_repos";
+const REPOS_PATH: &str = "/v1/connectors/github/repos";
+const INSTALL_PATH: &str = "/v1/connectors/github/install";
+const CATALOG_PATH: &str = "/v1/connectors/";
</file context>

fn wait_github(&self, timeout_ms: u64) -> Result<Value, Failure> {
let deadline = Instant::now() + Duration::from_millis(timeout_ms.min(60_000));
loop {
if self.connector(None)?.install_url.is_none() {

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: wait_github checks its deadline only after connector(None) completes, so a zero or short timeout can block for the connector's 45-second scan. Check the deadline before each poll and bound discovery to the remaining duration, or use a lightweight connection-state request.

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/cloud_repos/runtime.rs, line 104:

<comment>`wait_github` checks its deadline only after `connector(None)` completes, so a zero or short timeout can block for the connector's 45-second scan. Check the deadline before each poll and bound discovery to the remaining duration, or use a lightweight connection-state request.</comment>

<file context>
@@ -0,0 +1,350 @@
+    fn wait_github(&self, timeout_ms: u64) -> Result<Value, Failure> {
+        let deadline = Instant::now() + Duration::from_millis(timeout_ms.min(60_000));
+        loop {
+            if self.connector(None)?.install_url.is_none() {
+                return Ok(json!({"connected":true}));
+            }
</file context>

locks.retain(|_, lock| Arc::strong_count(lock) > 1);
locks.entry(key).or_default().clone()
};
let _guard = lock.try_lock().map_err(|_| {

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: Concurrent adds for the same chat and repository return seed.clone_failed instead of joining the in-flight clone and reusing its result. Await the per-key operation, then re-check the attachment or saved job under the lock.

(Based on your team's feedback about coalescing concurrent duplicate operations.)

View Feedback

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/cloud_repos/runtime.rs, line 214:

<comment>Concurrent adds for the same chat and repository return `seed.clone_failed` instead of joining the in-flight clone and reusing its result. Await the per-key operation, then re-check the attachment or saved job under the lock.

(Based on your team's feedback about coalescing concurrent duplicate operations.) </comment>

<file context>
@@ -0,0 +1,350 @@
+            locks.retain(|_, lock| Arc::strong_count(lock) > 1);
+            locks.entry(key).or_default().clone()
+        };
+        let _guard = lock.try_lock().map_err(|_| {
+            Failure::new(
+                "seed.clone_failed",
</file context>

|| url.password().is_some()
|| url.query().is_some()
|| url.fragment().is_some()
|| url.port().is_some()

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: This rejects otherwise valid GitHub remotes with standard ports, so repository discovery misses projects using https://github.com:443/... or ssh://git@github.com:22/.... Accept standard GitHub ports and normalize them away when exporting the owner/name.

(Based on your team's feedback about explicit GitHub ports.) [1cc7fee0-7938-45ba-a78a-423b41a5b010]

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

<comment>This rejects otherwise valid GitHub remotes with standard ports, so repository discovery misses projects using `https://github.com:443/...` or `ssh://git@github.com:22/...`. Accept standard GitHub ports and normalize them away when exporting the owner/name.

(Based on your team's feedback about explicit GitHub ports.) [1cc7fee0-7938-45ba-a78a-423b41a5b010]</comment>

<file context>
@@ -0,0 +1,219 @@
+            || url.password().is_some()
+            || url.query().is_some()
+            || url.fragment().is_some()
+            || url.port().is_some()
+            || (url.scheme() == "https" && !url.username().is_empty())
+            || (url.scheme() == "ssh" && !matches!(url.username(), "" | "git"))
</file context>
Suggested change
|| url.port().is_some()
|| url.port().is_some_and(|port| !matches!((url.scheme(), port), ("https", 443) | ("ssh", 22)))


`session` is the chat ID, a single safe path component. It is not the daemon session selector.
A bare repo name is resolved in source order: local Mac remotes, previous Cloud chat repos,
then all pages of the GitHub installation catalog. One match attaches; multiple matches within

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 contract promises all catalog pages, but Runtime::connector stops at 100 pages or 45 seconds and returns an error. Document those bounds here or change the implementation so the stated all-pages behavior is true.

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 plans/cmux-next/cloud-repo-actions.md, line 21:

<comment>The contract promises all catalog pages, but `Runtime::connector` stops at 100 pages or 45 seconds and returns an error. Document those bounds here or change the implementation so the stated all-pages behavior is true.</comment>

<file context>
@@ -0,0 +1,68 @@
+
+`session` is the chat ID, a single safe path component. It is not the daemon session selector.
+A bare repo name is resolved in source order: local Mac remotes, previous Cloud chat repos,
+then all pages of the GitHub installation catalog. One match attaches; multiple matches within
+one source return `repo.ambiguous` with `choices` in the same repo-row shape. An explicit
+GitHub URL or owner/name can clone publicly without asking for credentials first.
</file context>

request owns its socket and worker; no perpetual observer or timer is installed.

The feature requires the machine daemon's git-seed-v1 clone-only implementation and a platform
deployment with the GitHub connector enabled. The matching app changes provide startup sync,

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 plan documents Mac startup sync, Session Info rendering, automatic retry, and attachment forwarding, but this PR contains no app integration or callers for these actions. Add those app changes or mark these flows as not yet implemented instead of claiming they ship here.

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 plans/cmux-next/cloud-repo-actions.md, line 67:

<comment>The plan documents Mac startup sync, Session Info rendering, automatic retry, and attachment forwarding, but this PR contains no app integration or callers for these actions. Add those app changes or mark these flows as not yet implemented instead of claiming they ship here.</comment>

<file context>
@@ -0,0 +1,68 @@
+request owns its socket and worker; no perpetual observer or timer is installed.
+
+The feature requires the machine daemon's git-seed-v1 clone-only implementation and a platform
+deployment with the GitHub connector enabled. The matching app changes provide startup sync,
+Session Info rendering and automatic card replay. Tests use fake connector and seed sockets.
</file context>

}
}
text.push_str(if japanese {
"\nチャットのリポジトリ\n repo list --session CHAT [--query NAME]\n repo add --session CHAT --repo NAME [--ref REF]\n repo wait-github --session CHAT [--timeout-ms N]\n repo attached --session CHAT\n repo local\n repo sync --repos-json JSON [--recent-json JSON] [--install-url URL] [--complete true|false]\n"

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: The Japanese repository help omits the --socket routing hint shown in English, leaving Japanese users without guidance for targeting a Cloud machine daemon. Add the equivalent hint to the Japanese heading.

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/cli/cloud_help.rs, line 484:

<comment>The Japanese repository help omits the `--socket` routing hint shown in English, leaving Japanese users without guidance for targeting a Cloud machine daemon. Add the equivalent hint to the Japanese heading.</comment>

<file context>
@@ -480,6 +480,11 @@ pub(super) fn overview(japanese: bool) -> String {
         }
     }
+    text.push_str(if japanese {
+        "\nチャットのリポジトリ\n  repo list --session CHAT [--query NAME]\n  repo add --session CHAT --repo NAME [--ref REF]\n  repo wait-github --session CHAT [--timeout-ms N]\n  repo attached --session CHAT\n  repo local\n  repo sync --repos-json JSON [--recent-json JSON] [--install-url URL] [--complete true|false]\n"
+    } else {
+        "\nCHAT REPOSITORIES (use --socket for the Cloud machine)\n  repo list --session CHAT [--query NAME]\n  repo add --session CHAT --repo NAME [--ref REF]\n  repo wait-github --session CHAT [--timeout-ms N]\n  repo attached --session CHAT\n  repo local\n  repo sync --repos-json JSON [--recent-json JSON] [--install-url URL] [--complete true|false]\n"
</file context>
Suggested change
"\nチャットのリポジトリ\n repo list --session CHAT [--query NAME]\n repo add --session CHAT --repo NAME [--ref REF]\n repo wait-github --session CHAT [--timeout-ms N]\n repo attached --session CHAT\n repo local\n repo sync --repos-json JSON [--recent-json JSON] [--install-url URL] [--complete true|false]\n"
"\nチャットのリポジトリ(Cloud マシンには --socket を使用)\n repo list --session CHAT [--query NAME]\n repo add --session CHAT --repo NAME [--ref REF]\n repo wait-github --session CHAT [--timeout-ms N]\n repo attached --session CHAT\n repo local\n repo sync --repos-json JSON [--recent-json JSON] [--install-url URL] [--complete true|false]\n"

/// checked (`Err` names what was wrong with it).
pub enum Ask {
Github(Result<TokenAsk, String>),
GithubRepos(Result<super::github_repos::ReposAsk, String>),

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: Update the Ask enum documentation to include repository catalog requests; it now claims connections carry only token or hand-off asks, leaving generated docs and maintainers unaware of GithubRepos.

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-host/src/cloud/github_minter.rs, line 50:

<comment>Update the `Ask` enum documentation to include repository catalog requests; it now claims connections carry only token or hand-off asks, leaving generated docs and maintainers unaware of `GithubRepos`.</comment>

<file context>
@@ -47,6 +47,7 @@ const REPLY_WRITE_TIMEOUT: Duration = Duration::from_secs(2);
 /// checked (`Err` names what was wrong with it).
 pub enum Ask {
     Github(Result<TokenAsk, String>),
+    GithubRepos(Result<super::github_repos::ReposAsk, String>),
     Handoff(Result<HandoffReport, String>),
 }
</file context>

impl RemoteGate for FsGate {
fn admit(&self, _peer: &RemotePeer, frame: &str) -> bool {
frame_command(frame).is_some()
|| crate::cloud_repos::frame_command(frame).is_some_and(|cmd| {

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: FsGate now admits five remote repository commands, but its contract comment still says it admits only seven fs-v1 operations and denies every other frame. Update the documentation to include the repository allowlist so the gate’s security boundary is not misread.

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/server/fs_wire.rs, line 45:

<comment>`FsGate` now admits five remote repository commands, but its contract comment still says it admits only seven `fs-v1` operations and denies every other frame. Update the documentation to include the repository allowlist so the gate’s security boundary is not misread.</comment>

<file context>
@@ -42,6 +42,9 @@ pub struct FsGate;
 impl RemoteGate for FsGate {
     fn admit(&self, _peer: &RemotePeer, frame: &str) -> bool {
         frame_command(frame).is_some()
+            || crate::cloud_repos::frame_command(frame).is_some_and(|cmd| {
+                !matches!(cmd.as_str(), "cloud.repo.local" | "cloud.repo.remember")
+            })
</file context>

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Review (claude-opus-5-5): SHIP at cb8d889

Review (subagent claude-opus-5-5 medium): SHIP at cb8d889

Checked:

  • Paths and URLs: valid_name blocks path traversal. Only github.com https/ssh URLs are accepted, and the clone URL is always rebuilt.
  • CLI bridge: arguments go as argv with no shell.
  • No clobbering: a per-chat lock covers each add, and a folder held by another repo or ref answers seed.folder_not_empty.
  • Bounds: every poll, scan and wait has a limit.
  • Credentials: no token is returned to the caller.

Follow-ups:

  1. The session caller token is not checked. The daemon checks only that some principal exists (server/cloud_repos.rs:22-25), so a local client or cmux mcp caller can add a repo into another chat's folder (same user, no clobber). The comment in cloud_repo_mcp.rs:11-12 overstates the protection.
  2. The cloud_repo_* tools are listed on every cmux mcp (cli/mcp/cloud_repo_tools.rs:732), although the daemon refuses them off Cloud.
  3. A connector repo.needs_github is flattened to seed.clone_failed (runtime.rs:136-158), which loses the Connect GitHub card and auto-retry on that path. Fix this first.
  4. Finished entries in jobs[session][name] are never cleared (runtime.rs:268-273). If the record step fails, a retry hits seed.folder_not_empty.
  5. The worst-case daemon time (~630 s) is longer than the CLI/MCP 610 s wait.
  6. github.com/owner/name with no scheme answers seed.bad_request (engine.rs:96-102).
  7. The engine tests use a fake backend, so the real seed status loop is untested.

@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.

10 existing issues remain and 10 new issues found across 37 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="plans/cmux-next/cloud-repo-actions.md">

<violation number="1" location="plans/cmux-next/cloud-repo-actions.md:16">
P1: `complete` cannot be optional while preserving the last complete catalog: callers can omit it and have a partial `repos` array replace the stored catalog. Make `complete` required and reject missing or false values, or remove the preservation guarantee and define the overwrite behavior.</violation>
</file>

<file name="cmux-tui/crates/cmux-host/src/cloud/github_repos_tests.rs">

<violation number="1" location="cmux-tui/crates/cmux-host/src/cloud/github_repos_tests.rs:39">
P3: This test does not verify its stated “does not connect” behavior: the fake returns the same stale-cursor response for every non-catalog request, so an unexpected `/install` call is invisible. Record the requested paths and assert that the stale case performs only catalog and repository-list requests.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/tests.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/tests.rs:27">
P2: This fake ignores the chat session, so the engine tests cannot catch cross-chat attachment reuse or recording under the wrong chat. Key records by session and assert the supplied session is used.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/store.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/store.rs:175">
P2: A full recent catalog prevents new Mac recents from being stored because existing entries consume all 1,000 slots before the incoming entries are considered. Merge the incoming recents with priority, while retaining existing entries only for names not present in the incoming export, then apply the cap.</violation>
</file>

<file name="cmux-tui/crates/acpmux/src/cloud_repo_mcp.rs">

<violation number="1" location="cmux-tui/crates/acpmux/src/cloud_repo_mcp.rs:253">
P2: The recovery payload preserves surrounding whitespace from the original `ref`, even though the initial add trims it. After GitHub reconnects, the automatic retry can clone a different or invalid ref; serialize the trimmed value in the retry arguments.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/server/remote_relay/mod.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/server/remote_relay/mod.rs:274">
P1: This exposes repository discovery and attachment to any authenticated paired peer, including non-owner peers, for arbitrary chat IDs. Require the remote peer to be the server owner and authorize the requested session before dispatching cloud repository actions.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/engine.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/engine.rs:106">
P2: Malformed GitHub URLs with only one path component fall through as bare repository names, so `https://github.com/acme` can attach a different `*/acme` repository. Require `valid_repo` for URL/SCP inputs and allow `valid_name` only for genuinely bare input.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/local.rs:69">
P2: A failed Git config read is not always a non-Git directory. Malformed or otherwise unreadable repository configuration is silently omitted as `Absent`, causing an incomplete catalog to be synchronized as complete; classify only the known non-Git/no-match cases as absent and mark other exits incomplete.</violation>
</file>

<file name="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime.rs">

<violation number="1" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime.rs:59">
P1: `seed_socket` points at the daemon control socket, but this daemon has no handler for `git.seed.start` or `git.seed.status`, so every production add fails before cloning. Route these calls to the actual git-seed-v1 service or add the corresponding daemon handler.</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui-core/src/cloud_repos/runtime.rs:143">
P2: The connector can return `repo.needs_github`, but this branch rewrites it to `seed.clone_failed`, preventing the add flow from attaching its retry payload. Preserve `repo.needs_github` and its synchronized `install_url` fallback just like `repo.no_access`.</violation>
</file>

Requires human review: Auto-approval blocked because this review re-detected 10 unresolved issues already reported by Cubic.

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

| `cloud.repo.attached` | `{session}` | `{repos:[{state:"done",repo,folder,head}]}` |
| `cloud.repo.wait_github` | `{session,timeout_ms?}` | `{connected:boolean}` |
| `cloud.repo.local` | `{}` | `{repos:[...],recent:[...],complete:boolean}` |
| `cloud.repo.sync` | `{repos,recent?,complete?,install_url?}` | `{synced:number}` |

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: complete cannot be optional while preserving the last complete catalog: callers can omit it and have a partial repos array replace the stored catalog. Make complete required and reject missing or false values, or remove the preservation guarantee and define the overwrite behavior.

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 plans/cmux-next/cloud-repo-actions.md, line 16:

<comment>`complete` cannot be optional while preserving the last complete catalog: callers can omit it and have a partial `repos` array replace the stored catalog. Make `complete` required and reject missing or false values, or remove the preservation guarantee and define the overwrite behavior.</comment>

<file context>
@@ -0,0 +1,68 @@
+| `cloud.repo.attached` | `{session}` | `{repos:[{state:"done",repo,folder,head}]}` |
+| `cloud.repo.wait_github` | `{session,timeout_ms?}` | `{connected:boolean}` |
+| `cloud.repo.local` | `{}` | `{repos:[...],recent:[...],complete:boolean}` |
+| `cloud.repo.sync` | `{repos,recent?,complete?,install_url?}` | `{synced:number}` |
+| `cloud.repo.remember` | `{session,attachment:{state:"done",repo,folder,head}}` | `{remembered:true}` |
+
</file context>

return keep_open;
}
#[cfg(unix)]
if let Some(keep_open) = super::cloud_repos::try_handle(mux, client, message, writer) {

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: This exposes repository discovery and attachment to any authenticated paired peer, including non-owner peers, for arbitrary chat IDs. Require the remote peer to be the server owner and authorize the requested session before dispatching cloud repository actions.

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/server/remote_relay/mod.rs, line 274:

<comment>This exposes repository discovery and attachment to any authenticated paired peer, including non-owner peers, for arbitrary chat IDs. Require the remote peer to be the server owner and authorize the requested session before dispatching cloud repository actions.</comment>

<file context>
@@ -270,6 +270,10 @@ pub(super) fn handle_frame(
         return keep_open;
     }
+    #[cfg(unix)]
+    if let Some(keep_open) = super::cloud_repos::try_handle(mux, client, message, writer) {
+        return keep_open;
+    }
</file context>

let mut request = fields;
request.set("id", json!("cloud-repo"));
request.set("cmd", json!(cmd));
let answer = socket_request(&self.seed_socket, &request)?;

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: seed_socket points at the daemon control socket, but this daemon has no handler for git.seed.start or git.seed.status, so every production add fails before cloning. Route these calls to the actual git-seed-v1 service or add the corresponding daemon handler.

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/cloud_repos/runtime.rs, line 59:

<comment>`seed_socket` points at the daemon control socket, but this daemon has no handler for `git.seed.start` or `git.seed.status`, so every production add fails before cloning. Route these calls to the actual git-seed-v1 service or add the corresponding daemon handler.</comment>

<file context>
@@ -0,0 +1,350 @@
+        let mut request = fields;
+        request.set("id", json!("cloud-repo"));
+        request.set("cmd", json!(cmd));
+        let answer = socket_request(&self.seed_socket, &request)?;
+        if answer["ok"] == false {
+            let mut error = answer.clone();
</file context>

install_url: self.install_url.borrow().clone(),
})
}
fn attached(&self, _: &str) -> Result<Vec<Value>, Failure> {

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: This fake ignores the chat session, so the engine tests cannot catch cross-chat attachment reuse or recording under the wrong chat. Key records by session and assert the supplied session is used.

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/cloud_repos/tests.rs, line 27:

<comment>This fake ignores the chat session, so the engine tests cannot catch cross-chat attachment reuse or recording under the wrong chat. Key records by session and assert the supplied session is used.</comment>

<file context>
@@ -0,0 +1,227 @@
+            install_url: self.install_url.borrow().clone(),
+        })
+    }
+    fn attached(&self, _: &str) -> Result<Vec<Value>, Failure> {
+        Ok(self.records.borrow().clone())
+    }
</file context>

let mut seen = HashSet::new();
existing
.into_iter()
.chain(incoming)

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: A full recent catalog prevents new Mac recents from being stored because existing entries consume all 1,000 slots before the incoming entries are considered. Merge the incoming recents with priority, while retaining existing entries only for names not present in the incoming export, then apply the cap.

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/cloud_repos/store.rs, line 175:

<comment>A full recent catalog prevents new Mac recents from being stored because existing entries consume all 1,000 slots before the incoming entries are considered. Merge the incoming recents with priority, while retaining existing entries only for names not present in the incoming export, then apply the cap.</comment>

<file context>
@@ -0,0 +1,231 @@
+    let mut seen = HashSet::new();
+    existing
+        .into_iter()
+        .chain(incoming)
+        .filter_map(|mut repo| {
+            let name = repo["fullName"].as_str()?;
</file context>

}
let mut args = json!({"session": session, "repo": repo});
if let Some(reference) = reference.and_then(Value::as_str).filter(|s| !s.trim().is_empty()) {
args.set("ref", json!(reference));

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 recovery payload preserves surrounding whitespace from the original ref, even though the initial add trims it. After GitHub reconnects, the automatic retry can clone a different or invalid ref; serialize the trimmed value in the retry arguments.

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/acpmux/src/cloud_repo_mcp.rs, line 253:

<comment>The recovery payload preserves surrounding whitespace from the original `ref`, even though the initial add trims it. After GitHub reconnects, the automatic retry can clone a different or invalid ref; serialize the trimmed value in the retry arguments.</comment>

<file context>
@@ -0,0 +1,327 @@
+    }
+    let mut args = json!({"session": session, "repo": repo});
+    if let Some(reference) = reference.and_then(Value::as_str).filter(|s| !s.trim().is_empty()) {
+        args.set("ref", json!(reference));
+    }
+    error.set(
</file context>
Suggested change
args.set("ref", json!(reference));
args.set("ref", json!(reference.trim()));

}
input.to_owned()
};
if valid_repo(&candidate) || valid_name(&candidate) {

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: Malformed GitHub URLs with only one path component fall through as bare repository names, so https://github.com/acme can attach a different */acme repository. Require valid_repo for URL/SCP inputs and allow valid_name only for genuinely bare input.

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/cloud_repos/engine.rs, line 106:

<comment>Malformed GitHub URLs with only one path component fall through as bare repository names, so `https://github.com/acme` can attach a different `*/acme` repository. Require `valid_repo` for URL/SCP inputs and allow `valid_name` only for genuinely bare input.</comment>

<file context>
@@ -0,0 +1,283 @@
+        }
+        input.to_owned()
+    };
+    if valid_repo(&candidate) || valid_name(&candidate) {
+        Ok(candidate)
+    } else {
</file context>
Suggested change
if valid_repo(&candidate) || valid_name(&candidate) {
if valid_repo(&candidate)
|| (!input.contains("://") && !input.starts_with("git@github.com:") && valid_name(&candidate))
{

Ok(output) if output.truncated => RemoteRead::Incomplete,
Ok(output) => RemoteRead::Config(output.stdout),
Err(GitFailure::TimedOut | GitFailure::Unavailable(_)) => RemoteRead::Incomplete,
Err(GitFailure::Exit(_)) => RemoteRead::Absent,

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: A failed Git config read is not always a non-Git directory. Malformed or otherwise unreadable repository configuration is silently omitted as Absent, causing an incomplete catalog to be synchronized as complete; classify only the known non-Git/no-match cases as absent and mark other exits incomplete.

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

<comment>A failed Git config read is not always a non-Git directory. Malformed or otherwise unreadable repository configuration is silently omitted as `Absent`, causing an incomplete catalog to be synchronized as complete; classify only the known non-Git/no-match cases as absent and mark other exits incomplete.</comment>

<file context>
@@ -0,0 +1,219 @@
+        Ok(output) if output.truncated => RemoteRead::Incomplete,
+        Ok(output) => RemoteRead::Config(output.stdout),
+        Err(GitFailure::TimedOut | GitFailure::Unavailable(_)) => RemoteRead::Incomplete,
+        Err(GitFailure::Exit(_)) => RemoteRead::Absent,
+    }
+}
</file context>
Suggested change
Err(GitFailure::Exit(_)) => RemoteRead::Absent,
Err(GitFailure::Exit(error))
if error.is_empty()
|| error.contains("not a git repository")
|| error.contains("--local can only be used inside a git repository") =>
{
RemoteRead::Absent
}
Err(GitFailure::Exit(_)) => RemoteRead::Incomplete,

Comment on lines +143 to +158
if code == "repo.no_access" { code } else { "seed.clone_failed" },
if code == "repo.no_access" {
"Install GitHub access for the repository's organization, or request access from its owner."
} else {
"Retry repository discovery. If it still fails, reconnect GitHub from Add repository."
},
);
if code == "repo.no_access" {
if let Some(url) = error["install_url"]
.as_str()
.map(str::to_owned)
.or(store::install_url(self.mux)?)
{
failure.0.set("install_url", json!(url));
}
}

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 connector can return repo.needs_github, but this branch rewrites it to seed.clone_failed, preventing the add flow from attaching its retry payload. Preserve repo.needs_github and its synchronized install_url fallback just like repo.no_access.

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/cloud_repos/runtime.rs, line 143:

<comment>The connector can return `repo.needs_github`, but this branch rewrites it to `seed.clone_failed`, preventing the add flow from attaching its retry payload. Preserve `repo.needs_github` and its synchronized `install_url` fallback just like `repo.no_access`.</comment>

<file context>
@@ -0,0 +1,350 @@
+                let error = &answer["error"];
+                let code = error["code"].as_str().unwrap_or_default();
+                let mut failure = Failure::new(
+                    if code == "repo.no_access" { code } else { "seed.clone_failed" },
+                    if code == "repo.no_access" {
+                        "Install GitHub access for the repository's organization, or request access from its owner."
</file context>
Suggested change
if code == "repo.no_access" { code } else { "seed.clone_failed" },
if code == "repo.no_access" {
"Install GitHub access for the repository's organization, or request access from its owner."
} else {
"Retry repository discovery. If it still fails, reconnect GitHub from Add repository."
},
);
if code == "repo.no_access" {
if let Some(url) = error["install_url"]
.as_str()
.map(str::to_owned)
.or(store::install_url(self.mux)?)
{
failure.0.set("install_url", json!(url));
}
}
match code {
"repo.no_access" | "repo.needs_github" => code,
_ => "seed.clone_failed",
},
if matches!(code, "repo.no_access" | "repo.needs_github") {
"Install GitHub access for the repository's organization, or request access from its owner."
} else {
"Retry repository discovery. If it still fails, reconnect GitHub from Add repository."
},
);
if matches!(code, "repo.no_access" | "repo.needs_github") {
if let Some(url) = error["install_url"]
.as_str()
.filter(|url| !url.is_empty())
.map(str::to_owned)
.or(store::install_url(self.mux)?)
{
failure.0.set("install_url", json!(url));
}
}

fn github_repos_connection_states_get_install_url_without_listing() {
for state in ["available", "needs-auth", "expired"] {
let mut calls = Vec::new();
let reply = answer(&ask(json!({})), "team-a", |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: This test does not verify its stated “does not connect” behavior: the fake returns the same stale-cursor response for every non-catalog request, so an unexpected /install call is invisible. Record the requested paths and assert that the stale case performs only catalog and repository-list requests.

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-host/src/cloud/github_repos_tests.rs, line 39:

<comment>This test does not verify its stated “does not connect” behavior: the fake returns the same stale-cursor response for every non-catalog request, so an unexpected `/install` call is invisible. Record the requested paths and assert that the stale case performs only catalog and repository-list requests.</comment>

<file context>
@@ -0,0 +1,242 @@
+fn github_repos_connection_states_get_install_url_without_listing() {
+    for state in ["available", "needs-auth", "expired"] {
+        let mut calls = Vec::new();
+        let reply = answer(&ask(json!({})), "team-a", |path| {
+            calls.push(path.to_owned());
+            if path.starts_with("/v1/connectors/?") {
</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 1de7dee: 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=pr30361 -f ref=lane/cloud-repo-tools-2 -f platform=macos -f backend_mode=local

red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 2a2aa0c; does not block

Shipped in:

@cursor

cursor Bot commented Nov 1, 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.

@teamleaderleo
teamleaderleo merged commit 72dc85d into feat-cmux-next Nov 1, 2026
75 of 81 checks passed
@teamleaderleo
teamleaderleo deleted the lane/cloud-repo-tools-2 branch November 1, 2026 07:16
@blacksmith-sh

blacksmith-sh Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Found 13 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmph1ootfws/summary-fail.json (live):/summary-fail.json (live): View Logs
/tmp/tmpjz3ys89d/summary-missing.json (live):/summary-missing.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
convention violations found (ERROR lines above)./
convention violations found (ERROR lines above).
View Logs
nightly must build and hand off the macOS 26 helper and Xcode 26.5 app before publishin
g/
nightly must build and hand off the macOS 26 helper and Xcode 26.5 app before publishin
g
View Logs
test_cmux_next_follows_the_committed_target_graph (main.FocusedTestMap.test_cmux_ne
xt_follows_the_committed_target_graph)/
test_cmux_next_follows_the_committed_target_graph (main.FocusedTestMap.test_cmux_ne
xt_follows_the_committed_target_graph)
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

...and 3 more test failures. View all on Blacksmith

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

@lawrencecchen

Copy link
Copy Markdown
Contributor

This PR made the Rust panic ratchet red on feat-cmux-next. The clippy::indexing_slicing count went over the baseline in acpmux (694 > 686), cmux-host (1 > 0), cmux-tui (836 > 831) and cmux-tui-core (880 > 853). The new serde_json v["key"] reads and command[..2] caused it. The tip watcher's job 63b147f0938fdb56741e7c83 failed at 4cd800a. #30478 fixes it forward with get/pointer reads and does not change behavior. For later changes in these crates, read JSON with get/pointer and write with JsonSet::set/set_path, and run cmux-ci run ... --script scripts/ci/cmux-tui-rust-check.sh --arg panic --os linux before you land.

lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…x, cmux-host, cmux-tui, cmux-tui-core) (#30478)

* base red: panic ratchet, #30361's new JSON index reads use get

The Rust panic ratchet is red on feat-cmux-next since 72dc85d (#30361,
Cloud repository discovery), tip watcher job 63b147f0938fdb56741e7c83 at
4cd800a: clippy::indexing_slicing over the baseline in four crates
(acpmux 694 > 686, cmux-host 1 > 0, cmux-tui 836 > 831,
cmux-tui-core 880 > 853). The other commits in 4b15acd..4cd800a
add no index or slice in production code.

This rewrites every serde_json index read #30361 added in production code
(cloud_repo_mcp.rs, github_repos.rs, cli/cloud_repo.rs, cloud_repos/
engine.rs, local.rs, runtime.rs, store.rs) as get / pointer reads, the one
IndexMut as get_mut, and the `command[..2]` slice in cli.rs as
`command.get(..2)`. Behavior is the same: a missing key read through get
compares and converts the same as the Null that indexing returned
(`v["k"] == "x"` is `as_str() == Some("x")`, `v["ok"] != true` is
`as_bool() != Some(true)`, `v["ref"] != json!(r)` keeps Null for a missing
key). The baseline is not raised.

* base red: panic ratchet, #30439's and #30464's new index and string slice use checked forms

After the merge of feat-cmux-next 1dfc4dd, the panic ratchet at
9d4a172 (job 1764d0cc925c9f12951dc4c3) is still red on two rows
that later base commits added:
- acpmux indexing_slicing 689 > 686: #30439 (229803e) added
  `summary["turn"].is_object()` in hub/views.rs and two
  `self.running[at]` in subagents.rs.
- cmux-tui string_slice 83 > 82: #30464 (1b7b01c) added
  `&text[prefix.len()..]` in link/seed/github.rs.

Now `summary.get("turn").is_some_and(Value::is_object)`,
`self.running.get_mut(at)` (`at` is a position in the same Vec, found
just above with no change between, so the slot is always there), and
`text.split_at_checked(prefix.len())` (None exactly where the old
`text.get(..prefix.len())` was None). No behavior change.
teamleaderleo added a commit that referenced this pull request Nov 1, 2026
…loud repos (#30361) (#30528)

clippy -D warnings fails on feat-cmux-next in cloud_repos/engine.rs,
cloud_repos/runtime.rs, acpmux agent_tools.rs and cloud_repo_mcp.rs
(collapsible_if x5, semicolon_if_nothing_returned x1), so the cmux-tui
clippy job is red for every PR touching cmux-tui. Let-chains, no behavior
change.


Agent-Session: https://cmux.com/deeplink/agent?id=aid-fc739fd2f4e557ae7189

Co-authored-by: Leo Li <t@e>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@lawrencecchen

Copy link
Copy Markdown
Contributor

This PR left cmux-tui-rust-check.sh clippy red on feat-cmux-next (16b7cc2): type_complexity in cmux-host cloud/github_repos_tests.rs:130, collapsible_if in acpmux agent_tools.rs:590 and cloud_repo_mcp.rs:155 and cmux-tui-core cloud_repos/engine.rs:241-242 and runtime.rs:150, and semicolon_if_nothing_returned at runtime.rs:279. #30555 fixes them forward (no behavior change). Please run clippy before landing Rust changes.

lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…bool, #30361)

!x.is_some_and(|ms| ms <= MAX) is x.is_none_or(|ms| ms > MAX), and
!s.is_some_and(|v| !v.trim().is_empty()) is s.is_none_or(|v| v.trim().is_empty()).
Same accepted and refused values.
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…30555)

* fix(cmux-tui): clear base clippy reds from #30361, #30362 and #30363

cargo clippy -D warnings is red on feat-cmux-next 16b7cc2. Fix forward
(each culprit is a whole feature; every error is a lint, not behavior):

- cmux-host identity::enrolled (#30362): its only caller is the Linux host
  loop, so it was dead code on macOS. Compile it on Linux and in tests, and
  import its wire names inside it so macOS has no unused imports.
- cmux-host github_repos_tests (#30361): name the fake's state tuple
  (type_complexity).
- acpmux agent_tools, cloud_repo_mcp and cmux-tui-core cloud_repos engine
  and runtime (#30361): collapse nested ifs into let chains; add the missing
  semicolon after the clone wait sleep. Same conditions, same order.
- cmux-tui-core agent_session_folders test (#30363): drop a redundant clone.
- acpmux remote_guard (#30363): move cloud_chat_tests to the end of the file
  (items_after_test_module). No code change in the module.

* fix(cmux-host): identity tests import the wire file names they write

The previous commit moved BIND_FILE and BOUND_FILE out of identity.rs's
module imports into enrolled(), so the tests' use super::* lost them
(E0425 on macOS and Linux test builds). Import them in the tests.

* fix(cmux-tui): cloud repo CLI validation uses is_none_or (nonminimal_bool, #30361)

!x.is_some_and(|ms| ms <= MAX) is x.is_none_or(|ms| ms > MAX), and
!s.is_some_and(|v| !v.trim().is_empty()) is s.is_none_or(|v| v.trim().is_empty()).
Same accepted and refused values.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 1de7deef Deployed Nov 1, 2026 by teamleaderleo via cmux-tui tree preflight #94514
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.

2 participants