Repository navigation
Cloud: share repository discovery, attachment and recovery across agent tools and CLI - #30361
Conversation
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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() { |
There was a problem hiding this comment.
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(|_| { |
There was a problem hiding this comment.
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.)
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() |
There was a problem hiding this comment.
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>
| || 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 |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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>
| "\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>), |
There was a problem hiding this comment.
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| { |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
Review (claude-opus-5-5): SHIP at cb8d889
Review (subagent claude-opus-5-5 medium): SHIP at cb8d889
Checked:
- Paths and URLs:
valid_nameblocks 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:
- 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 orcmux mcpcaller can add a repo into another chat's folder (same user, no clobber). The comment incloud_repo_mcp.rs:11-12overstates the protection. - The
cloud_repo_*tools are listed on everycmux mcp(cli/mcp/cloud_repo_tools.rs:732), although the daemon refuses them off Cloud. - A connector
repo.needs_githubis flattened toseed.clone_failed(runtime.rs:136-158), which loses the Connect GitHub card and auto-retry on that path. Fix this first. - Finished entries in
jobs[session][name]are never cleared (runtime.rs:268-273). If the record step fails, a retry hitsseed.folder_not_empty. - The worst-case daemon time (~630 s) is longer than the CLI/MCP 610 s wait.
github.com/owner/namewith no scheme answersseed.bad_request(engine.rs:96-102).- The engine tests use a fake backend, so the real seed status loop is untested.
There was a problem hiding this comment.
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}` | |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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)?; |
There was a problem hiding this comment.
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> { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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>
| args.set("ref", json!(reference)); | |
| args.set("ref", json!(reference.trim())); |
| } | ||
| input.to_owned() | ||
| }; | ||
| if valid_repo(&candidate) || valid_name(&candidate) { |
There was a problem hiding this comment.
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>
| 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, |
There was a problem hiding this comment.
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>
| 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, |
| 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)); | ||
| } | ||
| } |
There was a problem hiding this comment.
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>
| 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| { |
There was a problem hiding this comment.
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>
|
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)
red: cmux-next checks (god files, concurrency, crash safety, l10n); base red: the same failure on the base 2a2aa0c; does not block Shipped in:
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Found 13 test failures on Blacksmith runners: Failures
...and 3 more test failures. View all on Blacksmith
|
|
This PR made the Rust panic ratchet red on feat-cmux-next. The |
…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.
…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>
|
This PR left |
…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.
…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.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
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 throughcmux cloud repo,cmux mcp, and Cloud-only acpmuxlist_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_reposkey. 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 replaysretry.argsaftercloud.repo.wait_githubreports 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 step67595b9ab2eccde8a50507da). 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