Skip to content

cmux-tui: start-chat plan answers the folder's repo; start-chat ensure gets the workspace machine - #30414

Merged
teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/start-chat-workspace-ensure
Nov 1, 2026
Merged

teamleaderleo merged 6 commits into
feat-cmux-nextfrom
lane/start-chat-workspace-ensure

Conversation

@teamleaderleo

Copy link
Copy Markdown
Collaborator

Summary

Client half of zero-setup hot start (cmuxterm-hq#5846, item 2): the first Cloud send of a chat gets a workspace machine that boots from the repository's set-up snapshot once it is ready.

  • cmux link start-chat plan reads the chat folder's GitHub origin remote (a new optional local field in the request) and answers repo (owner/name, or null). It also echoes environment, the cloud environment cloud.startChat chose (an optional env_... that the environment sheet sets, Cloud: edit environment setup from the chat #30359).
  • New cmux link start-chat ensure --workspace W --key K --repo OWNER/NAME [--ref R] [--environment env_...] [--platform URL], with the cmux credential on stdin. It sends POST /v1/workspaces/{W}/machine to the cmux platform, with the plan's create_key as clientKey (a retry gets the same machine), plus repo, ref and environment. It prints one JSON line: {"ok":true,"machine","team","created","snapshot","environment":{"id","status"}}, or the platform's error code (for example cloud_environment.not_found).
  • cli/machine_join.rs shares its platform client (Platform, platform_origin, CallError) with the link module, gains a post call, and lists the workspace-machine error codes as known codes.

The App still makes machines via cloud.createMachine. A follow-up wires CloudStartChat+Machine.swift to call ensure when the plan has a repo. That needs CloudMoveCLI to pass the token on stdin, as AddDeviceServices does.

Testing

  • New tests in link/start_chat_cli/tests.rs: parsing GitHub remote URLs, plan against a real git folder (no origin → null, then an scp-style origin), the ensure flags and body, and how platform answers and errors map to its line.
  • cmux-ci test -- cargo test -p cmux-tui start_chat (result in a comment below).

Observed / Not run

  • Not run yet: the end-to-end send from a DEV build (it waits on the Swift wiring).

Changelog

none

🤖 Generated with Claude Code
Agent session: agent session

teamleaderleo and others added 2 commits November 1, 2026 07:39
…e gets the workspace's machine from the platform

The plan reads the chat folder's GitHub origin as repo and echoes the
cloud environment cloud.startChat chose. ensure posts
/v1/workspaces/{W}/machine under the plan's create_key with repo, ref and
environment, so the machine boots from the repository's set-up snapshot
once it is ready (cmuxterm-hq#5846, item 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3b729bb81a4ace643a60
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3b729bb81a4ace643a60
@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 06bfb6b: 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=pr30414 -f ref=lane/start-chat-workspace-ensure -f platform=macos -f backend_mode=local

Shipped in:

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

Copy link
Copy Markdown
Collaborator Author

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

(Router-review refused: the review router already had 2 reviews running.) No finding is blocking. Checked: it compiles (visibility, cmux_process::Command, json!), existing plan callers are unaffected (extra keys only), the body matches the platform's WorkspaceMachines.ts contract, and the credential only goes on stdin and in the bearer header.

Follow-ups (fixed forward in the Swift wiring PR):

  • Refuse an empty or relative local.
  • Clear GIT_DIR, GIT_WORK_TREE and GIT_CONFIG_PARAMETERS for the git call.
  • Put a timeout on the git call.
  • Map a 404 with a generic code to unavailable.
  • Match github.com in any case, and with userinfo in the URL.

teamleaderleo and others added 4 commits November 1, 2026 07:51
…ing git

Review follow-ups on #30414: an empty or relative folder answers no repo;
no git process (no inherited GIT_* variables, no Command Line Tools prompt,
nothing to time out); subfolders and worktrees find their repository;
github.com in any case and with a user; a 404 with a generic not_found code
reads as unavailable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3b729bb81a4ace643a60
…and a generic 404

The previous commit's tests for these did not apply; add them and drop an
unneeded super:: qualification.

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

blacksmith-sh Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Found 15 test failures on Blacksmith runners:

Failures

Test View Logs
/tmp/tmp28hjlpnq/summary-missing.json (live):/summary-missing.json (live): View Logs
/tmp/tmp_4t0homi/summary-fail.json (live):/summary-fail.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
main.DeveloperIdAppTests.test_download_prints_the_active_profile_only/
test_download_prints_the_active_profile_only
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
stubs: /System/Library/Frameworks/Security.framework/Versions/A/Security has no stub/
Security has no stub
View Logs
stubs: CoreFoundation.tbd: install-name /System/Library/Frameworks/
CoreFoundation.framework/Versions/A/CoreFoundation, missing 1: ['_CFRetain']/
CoreFoundation, missing 1: ['_CFRetain']
View Logs

...and 5 more test failures. View all on Blacksmith: run 41285718699, run 41285718732

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

Supersedes the SHIP at 87cad70. That head failed to compile on CI (cmux_process::Stdio is not std::process::Stdio). e9c2cfa reads the folder's origin from its git config with no process, which addresses the earlier follow-ups. addc257 collapses origin_url into one let-chain, because clippy 1.95 (edition 2024) flagged collapsible_if. The reviewer checked each signature against its definition, ran clippy-driver 1.95 and rustfmt on the new code, and exercised every URL, config and worktree case in a scratch program.

Follow-ups: the legacy [remote.origin] form and insteadOf/include aren't read, so repo falls back to null.

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

3 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


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

<violation number="1" location="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs:336">
P2: `github_repo` rejects valid GitHub remotes with explicit transport ports, such as `https://github.com:443/owner/repo` and `ssh://git@github.com:22/owner/repo`. Parse the authority and port separately, normalizing HTTPS `443` and ignoring SSH transport ports, so `plan` can identify the repository for zero-setup boot.

(Based on your team's feedback about GitHub remote port normalization.)</violation>

<violation number="2" location="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs:361">
P2: `ensure` silently ignores unknown flags, so a misspelled `--environment` can create the machine in the default environment. Reject any flag not in the `ensure` vocabulary before sending the request.</violation>

<violation number="3" location="cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs:427">
P2: `percent_segment` leaves `.` unescaped, so workspace IDs `.` or `..` are normalized by URL parsing and can target a different platform route. Percent-encode dots (or reject dot-segment workspace IDs) before building the request path.</violation>
</file>

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

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

Some(rest) => {
let (host, path) = rest.split_once('/')?;
let host = host.rsplit('@').next()?;
host.eq_ignore_ascii_case("github.com").then_some(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.

P2: github_repo rejects valid GitHub remotes with explicit transport ports, such as https://github.com:443/owner/repo and ssh://git@github.com:22/owner/repo. Parse the authority and port separately, normalizing HTTPS 443 and ignoring SSH transport ports, so plan can identify the repository for zero-setup boot.

(Based on your team's feedback about GitHub remote port normalization.)

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/src/link/start_chat_cli.rs, line 336:

<comment>`github_repo` rejects valid GitHub remotes with explicit transport ports, such as `https://github.com:443/owner/repo` and `ssh://git@github.com:22/owner/repo`. Parse the authority and port separately, normalizing HTTPS `443` and ignoring SSH transport ports, so `plan` can identify the repository for zero-setup boot.

(Based on your team's feedback about GitHub remote port normalization.) </comment>

<file context>
@@ -227,10 +246,227 @@ pub(super) fn plan(ledger: &Path, request: &str) -> Value {
+        Some(rest) => {
+            let (host, path) = rest.split_once('/')?;
+            let host = host.rsplit('@').next()?;
+            host.eq_ignore_ascii_case("github.com").then_some(path)?
+        }
+        None => {
</file context>

}

impl Ensure {
fn of(flags: &Flags) -> Option<Self> {

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: ensure silently ignores unknown flags, so a misspelled --environment can create the machine in the default environment. Reject any flag not in the ensure vocabulary before sending the 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/src/link/start_chat_cli.rs, line 361:

<comment>`ensure` silently ignores unknown flags, so a misspelled `--environment` can create the machine in the default environment. Reject any flag not in the `ensure` vocabulary before sending the request.</comment>

<file context>
@@ -227,10 +246,227 @@ pub(super) fn plan(ledger: &Path, request: &str) -> Value {
+}
+
+impl Ensure {
+    fn of(flags: &Flags) -> Option<Self> {
+        let optional = |name: &str| match flags.all(name).as_slice() {
+            [] => Some(None),
</file context>

segment
.bytes()
.map(|byte| match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {

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: percent_segment leaves . unescaped, so workspace IDs . or .. are normalized by URL parsing and can target a different platform route. Percent-encode dots (or reject dot-segment workspace IDs) before building the request path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At cmux-tui/crates/cmux-tui/src/link/start_chat_cli.rs, line 427:

<comment>`percent_segment` leaves `.` unescaped, so workspace IDs `.` or `..` are normalized by URL parsing and can target a different platform route. Percent-encode dots (or reject dot-segment workspace IDs) before building the request path.</comment>

<file context>
@@ -227,10 +246,227 @@ pub(super) fn plan(ledger: &Path, request: &str) -> Value {
+    segment
+        .bytes()
+        .map(|byte| match byte {
+            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
+                (byte as char).to_string()
+            }
</file context>
Suggested change
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'~' => {

@lawrencecchen

Copy link
Copy Markdown
Contributor

This PR made the Rust panic ratchet red on feat-cmux-next: cmux-tui indexing_slicing 833 > 831 and string_slice 84 > 82 (tip watcher job 21c6ba0ebda787821cc7bc4b at 7e0a0f2). The hits are id[4..], &url[scheme.len()..], body["ref"] = .. and body["environment"] = .. in link/start_chat_cli.rs. #30661 fixes them forward with get/split_at_checked/JsonSet::set and does not change behavior. #30574 and #30655 also edit this file. Use the same forms there, and run cmux-ci run --class light --script scripts/ci/cmux-tui-rust-check.sh --arg panic --os linux --ref SHA before you land.

@teamleaderleo
teamleaderleo merged commit 0507fc4 into feat-cmux-next Nov 1, 2026
132 of 138 checks passed
@teamleaderleo
teamleaderleo deleted the lane/start-chat-workspace-ensure branch November 1, 2026 09:49
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…ads use get

The ratchet at the merge with feat-cmux-next e27f116 (18cef5a,
job 9d666b02dcb4c69dbfc147f0) is still red: cmux-tui indexing_slicing
849 > 831. The 18 new hits after #30414 come from:
- d8e1b7c (#29234) cli/terminal_talk/send.rs stage_failure: 2
- 9f5e2e1 (#30634) link/seed_cli.rs: 13, link/mod.rs `&args[1..]`: 1
- 7b30784 (#30664) link/machine_pin.rs: 1, link/mod.rs `&args[2..]`: 1

Each is now a get / pointer-free get read, `JsonSet::set` for the one
write, and `args.get(n..)` for the slices (the branch runs only when
`args.get(n - 1)` matched, so the slice is always there). A missing key
read through get gives None where indexing gave Null: `as_str()` and
`as_bool()` see the same, and json! writes None as null.
No behavior change; the baseline is not raised.
lawrencecchen added a commit that referenced this pull request Nov 1, 2026
…0414, #29234, #30634, #30664 and #30574 use checked forms (#30661)

* base red: panic ratchet, #30414's new index and string slices in start_chat_cli use checked forms

The Rust panic ratchet is red on feat-cmux-next at 7e0a0f2 (tip
watcher job 21c6ba0ebda787821cc7bc4b): cmux-tui indexing_slicing
833 > 831 and string_slice 84 > 82. All four hits are from 0507fc4
(#30414) in cmux-tui/src/link/start_chat_cli.rs:
- valid_environment: `id[4..]` -> `id.get(4..)`; the id already starts
  with the ASCII "env_", so the slice is always Some and the same.
- github_repo: `&url[scheme.len()..]` -> `split_at_checked`, None in
  exactly the cases where the `get(..scheme.len())` before it was None.
- Ensure::body: `body["ref"] = ..`, `body["environment"] = ..` ->
  `JsonSet::set` on the same object literal (same insert).
No behavior change; the baseline is not raised.

* base red: panic ratchet, #29234's, #30634's and #30664's new index reads use get

The ratchet at the merge with feat-cmux-next e27f116 (18cef5a,
job 9d666b02dcb4c69dbfc147f0) is still red: cmux-tui indexing_slicing
849 > 831. The 18 new hits after #30414 come from:
- d8e1b7c (#29234) cli/terminal_talk/send.rs stage_failure: 2
- 9f5e2e1 (#30634) link/seed_cli.rs: 13, link/mod.rs `&args[1..]`: 1
- 7b30784 (#30664) link/machine_pin.rs: 1, link/mod.rs `&args[2..]`: 1

Each is now a get / pointer-free get read, `JsonSet::set` for the one
write, and `args.get(n..)` for the slices (the branch runs only when
`args.get(n - 1)` matched, so the slice is always there). A missing key
read through get gives None where indexing gave Null: `as_str()` and
`as_bool()` see the same, and json! writes None as null.
No behavior change; the baseline is not raised.

* base red: panic ratchet, #30574's new index reads in start_chat_cli use get

#30574 (93772c4) landed after the e27f116 merge and added four
Value index reads in start-chat plan (line["machine"], ["created"],
["snapshot"], ["environment"]). They are now get reads: a missing key
is None where indexing gave Null, as_str() sees the same and json!
writes None as null. No behavior change.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 06bfb6be Deployed Nov 1, 2026 by teamleaderleo via cmux-tui tree preflight #95071
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants