Skip to content

Fix Linux iOS release origin guard - #11754

Merged
lawrencecchen merged 5 commits into
mainfrom
issue-11751-ios-release-fixture
Sep 3, 2026
Merged

lawrencecchen merged 5 commits into
mainfrom
issue-11751-ios-release-fixture

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Sep 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Honor the configured PLISTBUDDY implementation in the production iOS origin verifier, while keeping /usr/libexec/PlistBuddy as the macOS default.
  • Model the four production runtime origins in fake archives and verify them in the beta and App Store artifact tests.
  • Add a behavioral test that uses a non-plist fixture and a configured PlistBuddy implementation, proving the Linux CI path.

Closes #11751

Verification

  • python3 tests/test_ios_appstore_lane_identity.py passes with isolated Git configuration.
  • bash -n scripts/lib/verify-ios-release-origins.sh passes.
  • bun test scripts/lib/iroh-production-gate.test.mjs has one unrelated pre-existing expectation failure for the changed dev sign-in profile output.

The commits are intentionally split into test then fix so CI proves the regression.


Summary by cubic

Fixes the iOS release origin verifier so it runs on Linux CI without trusting a PLISTBUDDY environment override. The gate now uses /usr/libexec/PlistBuddy only when present and otherwise falls back to a fixed system Python plist parser, so callers can no longer swap in a replacement and bypass the gate.

Adds tests covering the four production runtime origins in the beta and App Store artifact tests, and a behavioral test proving the Linux path with a non-plist fixture and that an untrusted PlistBuddy override is rejected. Also makes the isolated repo fixture's ios-v1.0.0 tag independent of global tag.gpgSign settings. Closes #11751.

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

Review in cubic

@vercel

vercel Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cmux166 Ready Ready Preview Sep 3, 2026 3:52pm UTC
cmux41 Ready Ready Preview Sep 3, 2026 3:52pm UTC

@coderabbitai

coderabbitai Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 0ab0a9f6-9e68-4b9f-8dc8-866b99482bcb

📥 Commits

Reviewing files that changed from the base of the PR and between 6cd4765 and da869b7.

📒 Files selected for processing (2)
  • scripts/lib/verify-ios-release-origins.sh
  • tests/test_ios_appstore_lane_identity.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@lawrencecchen
lawrencecchen force-pushed the issue-11751-ios-release-fixture branch from 6ea2832 to 914d07b Compare September 3, 2026 02:15
@lawrencecchen
lawrencecchen merged commit 829c6af into main Sep 3, 2026
20 of 32 checks passed
@lawrencecchen
lawrencecchen deleted the issue-11751-ios-release-fixture branch September 3, 2026 04:56
rustybret pushed a commit to rustybret/bmux that referenced this pull request Sep 3, 2026
829c6af Fix Linux iOS release origin guard (manaflow-ai#11754)
a013ecb Guard PTY child during terminal host startup (manaflow-ai#11425)
1b0f61d cloud: lgx 12 vCPU / 24 GB size, 20260903c ladder, Freestyle preconnect, one-exec attach (manaflow-ai#11783)
d300944 Fix RemoteResumeBindingTests legacy snapshot fixture (manaflow-ai#10338)
39a2f3a Avoid cloning graphics occluders for cache checks (manaflow-ai#11746)
7a5e0c5 ci(tui): make ignored coverage and browser smoke explicit (manaflow-ai#11752)
18bec20 ci: poll Gatekeeper after stapling the Computer Use helper (manaflow-ai#11778)
e473108 ci: simplify reusable TUI package checkouts (manaflow-ai#11749)
0915892 fix(tui): reconcile every PyPI wheel after upload (manaflow-ai#11740)
1cbd743 fix: make SSH auth cleanup fork resilient (manaflow-ai#11497)

# Conflicts:
#	.github/workflows/cmux-tui-build-package.yml
#	.github/workflows/cmux-tui-nightly.yml
#	.github/workflows/cmux-tui-sdks.yml
#	.github/workflows/cmux-tui.yml
#	.github/workflows/tui-publish-pypi.yml
austinywang added a commit that referenced this pull request Sep 3, 2026
…ywhere

Main's plan-machine change (#11756/#11783) reduced cloudVMSizeAliases to
20g/20gb (or raw MB), but the error strings and usage lines still
advertised the retired 2g-32g ladder — ours worse, still carrying the
24g we added when that preset existed. vm run/route/agent unknown-size
errors, the vm new usage and unknown-flag text, docs/cli-contract.md's
vm new row (matching the freestyle-vm-primitives wording to keep that
merge clean), and every skill mention now name 20g (the 5 vCPU / 20 GB
/ 200 GB plan machine) or raw MB — matching parseCloudVMSize instead of
misleading an agent into a rejected --size 8g.

Also taken in this merge: main's #11754 landed the Linux iOS-guard fix
this branch had been carrying, so those files resolve to main's
(77/77 local pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
austinywang added a commit that referenced this pull request Sep 10, 2026
…, drift check, router prune fix (#10793)

* worktree: drop stored defaults on identity lets so Xcode 26.6 builds main

After #10781, worktreeDeviceID/worktreeFileID were both defaulted at the
declaration and assigned in the explicit init, which the current toolchain
rejects ("immutable value may only be initialized once"). The init's
parameter defaults keep the same call-site contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cli: cmux vm run/push/pull/wait and the cmux-cloud-vm agent skill

vm run routes a command to a cloud machine without naming one: sticky
per-directory binding, then an idle agent-pool machine, then a sleeper,
then a freshly provisioned pool machine. push/pull move files over the
exec channel (base64 chunks, SHA-256 verified, directories as tarballs);
wait blocks until ready and optionally wakes the machine. The skill lets
any coding agent drive machines from plain CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm push: 64 KiB argv-bound chunks, no AppleDouble sidecars, line-safe progress

Live dogfood on Blaxel: a 512 KiB chunk base64-encodes past Linux's 128 KiB
per-argument limit ("argument list too long"), macOS tar shipped ._* files
onto the machine, and chunk progress ran together when stderr was captured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: pool membership is the persisted id list, not the display label; review fixes

- The router now only drafts machines it provisioned itself (ids recorded in
  ~/.cmuxterm/vm-run-pool.json at create time, pruned when machines vanish); a
  user machine renamed agent-pool is never used. Test covers the impostor.
- Staging tarball is removed if reading it throws before the defer is armed.
- Push/pull chunk progress is localized (cli.vm.push.progress, cli.vm.pull.progress).
- vm --help, the usage contract, and the contract doc list open/ports/tools/
  handoff/promote-template, which the dispatcher already handled.
- Sticky-binding fixture uses a fixed instant, not the host clock.
- Skill recipes: --sync runs inside the synced dir (no remote $PWD), port
  readiness poll instead of sleep, eligibility filter instead of .vms[0],
  background test exit status captured to a status file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: lock the pool store across processes; idempotent, run-scoped recipes

- updateVMRunPool does the read-modify-write under flock on a sibling lock
  file, so two routers provisioning at once both land in the store; covered by
  a two-process test against two mock sockets.
- Dev-server recipe reuses a live server or starts one with a workspace pidfile
  and log; test recipe uses per-run log/status paths written atomically.
- Document that --sync is additive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: pool-store failures propagate; recipes validate the dev server and use unique run ids

- updateVMRunPool/saveVMRunPool throw on lock or write failure and createPoolVM
  reports the machine it provisioned but could not record, instead of a silent
  unlocked update.
- The dev-server recipe reuses a server only when the recorded pid is alive and
  owns :3000 (netstat -p), refuses to start a second server on a port someone
  else owns, and clears stale metadata.
- Test-run ids come from uuidgen, not the epoch second.
- Skill docs: cmux vm shell is a cmux-tui session now that machines run the
  cmux-tui remote daemon; agents keep working through vm run/exec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: regression test — pruning a stale pool id must keep an id recorded after the vm.list snapshot

The mock socket records pool-2 while answering vm.list and returns a list that
predates it; the store must end up {pool-1, pool-2} with gone-1 pruned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: prune only ids the pre-list snapshot saw as gone; product-level pool-store error

- Load the pool store before vm.list and subtract only the ids that snapshot
  lacks in the live list, instead of intersecting the locked set with a stale
  live snapshot, so a machine another vm run recorded meanwhile is never
  dropped from the pool.
- The provisioned-but-unrecorded error no longer interpolates the raw
  pool-store error (lock path, OS text); it keeps the machine id and the
  recovery commands.
- The unknown-size errors for vm run/route/agent list 24g, which
  parseCloudVMSize already accepts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cli: cmux vm <verb> --help prints the verb's own usage, offline

--help/-h short-circuited to the cmux vm overview for every verb, so the
option lists for run, route, agent, push, pull, wait, open, tree, workspace,
terminal, tui, prompt, and base (--size, --timeout, placement flags, --json
shapes) were unreachable without a running app and a usage error. A new
CLI/CMUXCLI+VMHelp.swift maps those verbs to their usage strings; the prompt
and base usages move out of the handler so they can be shared.

Also: the overview lists workspace and terminal, points at per-verb help,
no longer claims vm prompt --open accepts pi (the app supports
claude|codex|opencode), and the shell/desktop lines read in order.

docs/cli-contract.md: the vm/cloud usage probe now matches the binary (it
lacked prompt, so the no-socket contract lane was red), plus one offline
probe per routed verb and cmux surface --help.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmux-cloud-vm skill: the complete cmux Cloud CLI set, with a CI drift check

references/commands.md is now the single reference for every cmux vm verb
(and cmux cloud alias): usage, aliases, flags, --json shape, exit codes, the
socket method it calls, and the sidebar action it mirrors, grouped machine /
files / execution / routing / workspaces & terminals / surfaces & display /
checkpoints & forks / networking & ports / account & plan, plus the app's
vm.* socket table. Verbs that exist only in open PRs sit in one labeled
"In flight" section (#11324 cmux fork, #11347), so the skill never names
something an agent cannot run today; #11345 (vm terminal send|read|wait, the
single sidebar Close Workspace…) merged during this work and is folded in.

SKILL.md leads with vm run, then the glossary, cloud-vs-local, a need→verb
table, headless terminal loops, agent policy, and troubleshooting.
agent-workflows.md gains the headless-terminal recipe; openai.yaml describes
the same scope for Codex; Resources/cloud-agent-skill.md (the copy vm prompt
installs) no longer disagrees with the CLI (24g, vm base open, plain-terminal
shell, ~30 s exec, vm wait/handoff/prompt/ssh-info/promote-template,
fork/restore flags, per-verb --help).

tests/test_cloud_vm_skill_coverage.py (workflow-guard-tests lane) parses the
vm dispatcher, the workspace/terminal/surface sub-verbs, the usage line, the
docs/cli-contract.md probe, and the advertised vm.* methods, and fails when
the skill and the CLI disagree in either direction or when an in-flight verb
has already shipped.

Localization audit: CLI help/usage text follows the English-only CLI help
convention; no Settings, menu, or web strings touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: re-read the pool after pruning so a concurrently recorded machine is eligible; full -h probe needles

A machine another vm run recorded between this run's pool load and vm.list
(and that the list carries) was not in the pre-list snapshot, so it was
ineligible for this run and could push it toward a needless provision or a
false would_provision from vm route. The eligible set is now the post-prune
store intersected with the live list.

docs/cli-contract.md: the -h / cloud run / upload probes name the full usage
line, same as their --help siblings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* test_cloud_vm_skill_coverage: fail loudly when the unknown-verb usage line cannot be found

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* tests: carry #11346's two-line cmuxTests compile fix so the test bundle builds on this branch

Same lines as #11346 (the CloudTreeNodeActions fixture gained
projectInLocalWorkspace in #11345; SidebarFileDropFindRoutingTests needs
import Bonsplit after #11059). Whichever lands first, the other merges clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmuxTests: align CFFIXED_USER_HOME with a test's HOME whenever the child inherits a CF home redirect

On the hosted e2e lane the console session forwards CFFIXED_USER_HOME without
CMUX_APP_HOST_ISOLATION_REQUIRED, so every CLI the process harness spawned
resolved NSHomeDirectory() to the runner's home and ignored the test's HOME:
the vm run pool/binding stores, SSH ~ expansion, and hook installs all landed
outside the per-test home (126 failures across the class, including main's
own testVMRunReusesIdlePoolMachine). The harness now aligns
CFFIXED_USER_HOME with HOME when either the isolation flag is set or a
CFFIXED_USER_HOME redirect is already present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmux-cloud-vm skill: provisioning is gated to paid plans (vm_requires_pro) after #11332

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: resolve the router's state home from $HOME; harness pins CFFIXED_USER_HOME to a test's HOME

NSHomeDirectory() resolves through Core Foundation (CFFIXED_USER_HOME, then
the passwd entry) and ignores a HOME override — the comment claiming it honors
$HOME was wrong. So the pool and binding stores, documented as HOME-relative,
went to the real ~/.cmuxterm in every redirected run, and the router tests
(main's own included) only passed under CI's app-host isolation, where the
harness aligned CFFIXED_USER_HOME. Reproduced on a fleet Mac's GUI session
(274 tests, 120 failures) with the same signature as the hosted lane.

- CLI: vmRunStateHomeDirectory() prefers a non-empty $HOME, else
  NSHomeDirectory(); both store URLs use it.
- cmuxTests: isolatedCLIChildEnvironment pins CFFIXED_USER_HOME to the
  supplied HOME unconditionally (XDG_CONFIG_HOME still only under the
  app-host isolation flag), so every spawned CLI agrees with the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: mark the CLA policy guard's GitHub-hosted runner as required so the self-hosted guard passes

#11387/#11407 added cla-policy-guard.yml on a bare ubuntu-24.04 runner, which
tests/test_ci_self_hosted_guard.sh forbids without the github-hosted-required
marker; workflow-guard-tests has been red on main since. The guard is a
base-controlled pull_request_target workflow, so a GitHub-hosted runner is
the intended trust boundary — same marker the browser, npm-provenance, and
attestation jobs carry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: reword the CLA policy guard runner marker so the fleet-label rule does not match its comment

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill + vm new help: no desktop image ships today; Freestyle default; paid plans uncapped

#11566 removed Blaxel and flipped vm new to shell-only-by-default but left
the cmux vm overview claiming desktop-by-default — the overview now matches
the dispatcher. The skill (SKILL.md, commands.md, agent-workflows.md, the
bundled cloud-agent-skill.md) drops the xfce/noVNC/CUA desktop claims,
documents --desktop failing closed until a desktop image lands, the
e2b|freestyle|daytona provider set with Freestyle as the server-side default,
and #11580's uncapped paid plans (the 'no limit' plan meter line).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* web: carry the main-CI fixes for the Blaxel removal so this PR's merge ref is green

Verbatim from open #11586 (Blaxel-removal migration applies on a fresh
database via ::text enum comparisons — same fix as #11582 — plus the
cmuxTuiDaemon shell wiring and the freestyle shell-repair test removal it
replaces with vm-cmux-tui coverage), and the pricing-page test updated to
the 'Unlimited' concurrent-VMs copy #11580 shipped. Whichever lands first,
the rest merge clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: mark the port-URL verbs dormant — no driver implements open-port on any current deployment

web/services/vms/desktopWrapper.ts and the workflow answer 'open-port is not
supported by this deployment'; the CLI verbs exist and are kept documented,
but the skill no longer implies a working port URL today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: list #11609's vm link and port-preview TLS edge as in flight

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: spell out the full #11609 surface under In flight (vm link, live port previews, attach_transports, tree workspaces, placement hardening)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: restore main's cla-policy-guard.yml verbatim — the base-controlled guard rejects PR-side edits, and the runner guard now exempts the file by path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cloud: clear the three main-actor isolation warnings #11421 left over budget

tests-build-and-lag has been red since #11421: finishedUserInfoKey referenced
from the notification observer's Sendable closure, and .shared used as a
default argument (default values evaluate in a nonisolated context) in
MachinesPanelViewModel.init and NewMachineSheetPresenter.presentNewMachine.
The string constant becomes nonisolated; the default arguments become
optional and resolve to .shared inside the main-actor bodies. Verified on a
fleet builder: cmux-unit build-for-testing succeeds with zero warnings in
these files. No behavior change; explicit-coordinator callers (tests)
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: note #11609's grow-only sizing under In flight

* ci: make the #11524 release-origins gate pass the Linux guard harness (fixes #11757)

Three gaps broke workflow-guard-tests on every merge ref since #11524:
- verify-ios-release-origins.sh read plists only via /usr/libexec/PlistBuddy,
  which does not exist on the Linux guard lane, so every key read <absent>
  and the gate failed closed. It now falls back to python3 plistlib when
  PlistBuddy is missing; the absolute path stays first so PATH can never
  shadow the reader in a release lane.
- The fake archives in tests/test_ios_appstore_lane_identity.py never baked
  the production-origin keys a real Release build carries; both fixture
  writers now stamp CMUXAuthEnvironment/CMUXApiBaseURL/CMUXIrohBrokerBaseURL/
  CMUXPresenceBaseURL.
- The isolated-repo fixture copied upload-testflight.sh but not the new lib
  script it calls, so the auto-version lane failed on a missing file.

tests/test_ios_appstore_lane_identity.py: 77/77 ok, exit 0 locally (macOS
PlistBuddy path); the plistlib path verified standalone and by CI's Linux lane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cloud: Sendable ISO8601 parsing in VMClient; nonisolated presence URL resolver

Newest main marked two ISO8601DateFormatter statics nonisolated (a warning:
the type is not Sendable) and left PresenceHeartbeatClient.resolvedServiceURL
main-actor-isolated while PresenceHeartbeatClientTests calls it from
nonisolated Swift Testing contexts, which stops cmuxTests compiling on every
app-host shard. The formatters become Date.ISO8601FormatStyle constants
(Sendable, same accepted formats) parsed via Date(_:strategy:), and the
resolver — a pure function of its environment/defaults arguments over
nonisolated PresenceSettings/AuthEnvironment statics — becomes nonisolated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: document cmux vpn hosts, extend the drift check to the vpn dispatcher, refresh the in-flight facts from freestyle-vm-primitives

cmux vpn hosts landed with #11626 but the skill's vpn section stopped at
revoke; the coverage check only parsed the vm dispatcher, so nothing
caught it. The check now parses runVPNCommand the same way and fails on
a vpn verb the reference misses or invents (it flagged the in-flight
section's own wording during this change).

The in-flight section also claimed a hosts verb family was arriving with
the guest-CLI work — wrong on both ends: vpn hosts already ships here,
and freestyle-vm-primitives has no vm hosts verb. Replaced with what
that branch actually adds today: the guest cmux shim + in-VM notify
bridge, vm help, the screen->display catalog kind rename, and the
vm tree --refresh fleet re-read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: re-ground on the desktop image and live private-path port opens from newest main

#11776 baked the TigerVNC desktop into the devbox image and #11756/#11776
gave the Freestyle driver its first openPort — the URL is the machine's
private VPC address behind the WireGuard tunnel, never a public ingress.
So --desktop no longer fails closed, vm desktop works on desktop-kind
machines (private address on 6901, vpn required, base machines exit 1),
and the port verbs are no longer dormant. The reference, SKILL.md,
agent-workflows, and the bundled cloud-agent-skill now say so, and the
in-flight notes shrink to what freestyle-vm-primitives still adds: the
public TLS-edge previews on tokened subdomains and the vm-new
desktop-by-default flip (vm base open has been desktop-default since
#10948 — the CLI's two kind parsers differ today, which docs/cli-contract.md
already papers over by describing the flipped default).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: teach the delegation mission — persistence past the closed laptop, staged machine workspaces

The point of the CLI is a local agent delegating work to the cloud, so
the skill now says so up front (sessions live in the machine's daemon
and survive the Mac disconnecting; reattach from any signed-in Mac) and
gains the staged-workspace recipe: compose a named machine workspace's
terminals headlessly with surface new-terminal --remote-workspace,
verify with vm tree --json, and hand the user one click that opens the
whole thing. Honest about today's two edges: vm workspace new always
opens a local workspace as a side effect, and vm agent cannot target a
workspace (use surface new-terminal with a login shell instead).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cli+skill: the 20g plan machine is the only size preset — say so everywhere

Main's plan-machine change (#11756/#11783) reduced cloudVMSizeAliases to
20g/20gb (or raw MB), but the error strings and usage lines still
advertised the retired 2g-32g ladder — ours worse, still carrying the
24g we added when that preset existed. vm run/route/agent unknown-size
errors, the vm new usage and unknown-flag text, docs/cli-contract.md's
vm new row (matching the freestyle-vm-primitives wording to keep that
merge clean), and every skill mention now name 20g (the 5 vCPU / 20 GB
/ 200 GB plan machine) or raw MB — matching parseCloudVMSize instead of
misleading an agent into a rejected --size 8g.

Also taken in this merge: main's #11754 landed the Linux iOS-guard fix
this branch had been carrying, so those files resolve to main's
(77/77 local pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: note headless staging flags coming in freestyle-vm-primitives

cmux176 implemented the two staging gaps flagged earlier — vm workspace
new --no-open and vm agent --remote-workspace — so the in-flight section
now names them and points §6b's workarounds at their replacement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: silence the guard-condition trailing-closure warning Xcode 26.3 added

The critical-pressure teardown hardening (via main) left two compactMap
trailing closures inside postAggregateMemoryPressureWarning's guard
condition; Xcode 26.3's compiler warns 'trailing closure in this context
is confusable with the body of the statement' on both (76:41, 77:41),
which fails the warning-budget lane with actual=2 budget=0 — on main's
own runs too (run 33716921978 shows the same +2). Parenthesized closure
arguments are the fix the diagnostic prescribes; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: adapt the Base create launch to the 3-argument coordinator Launch

Two green PRs crossed on main: #10773 added a 2-argument
MachineCreateCoordinator.start call in the Base sheet flow while #11773
changed Launch to (arguments, progress, completion) for the pending
row's live output — main has not built the combination yet, and the
first tree containing both fails with 'contextual closure type expects
3 arguments'. The Base flow now takes the progress handler and threads
it through launchCloudVMBaseOpen into CloudVMActionLauncher's existing
onOutput, so Base creates stream output to the pending row exactly like
the New Machine sheet's flow in NewMachineSheetPresenter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: make MachineCreateCoordinatorTests compile again after #11773

Two fixes for main's own test file (byte-identical there, so main's
cmuxTests target does not compile either): #expect took the Bool? from
optional-chained isSuperseded (== true resolves it), and the new
MachinesPanelPendingCreateTests suite called Self.newMachineRequest for
a helper that lives on MachineCreateCoordinatorTests — qualifying the
type fixes the lookup and gives the trailing 'name: nil' its context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: reconcile cloud CLI branch with current main

* fix: import workspace group test model

* docs: keep Cloud skill metadata within UI contract

* docs: align Cloud VM lifecycle and tree guidance

* chore: drop accidental web test diff

* docs: clarify Cloud surface rollout behavior

* test: align Freestyle SDK fixture

* cli: keep Cloud VM help lists complete

* fix: resolve Swift 6 callback isolation warnings

* fix(ssh): signal stopped auth descendants reliably

(cherry picked from commit d73ecd7)

* fix(ssh): start cleanup deadline after snapshot

(cherry picked from commit 875c68a)

* fix(ssh): keep cleanup signal paths fork-free

* test(cloud): use explicit issue comments in port regression

* fix(ssh): keep frozen auth cleanup fork-free

* docs(cloud): document VM disk resize

* fix(ssh): deduplicate frozen cleanup journal

* fix(ssh): recover from fork-starved cleanup

* fix(ssh): normalize completed cleanup status

* fix(ssh): finish cleanup without marker discovery

* test(ssh): explain cleanup exit failures

* test(cloud): wire resize action fixture

* test(ssh): isolate deadline fixture process group

* test(terminal): stub bounded selection clipboard read

* test(ssh): make backoff signal fixture deterministic

* test: refresh merged web fixtures

* docs: sync cloud VM skill with CLI parity

* Revert the test-only half of #11929 so the unit test bundle compiles

#11929 merged 265 lines of
SurfaceCatalogTests that call beginCloudWorkspaceRename,
commitCloudWorkspaceRename, rollbackCloudWorkspaceRename,
replaceCloudResources and pendingCloudWorkspaceRenameName. None of those
exist in the app: the PR landed only its test file. Since that merge
(2026-09-06) every cmuxTests build on main fails, so no hosted unit test
run can pass. Austin authored this revert on another branch
(68e2dbc) but it never reached main. Re-land the feature with tests
and implementation together.

(cherry picked from commit 68e2dbc)

Claude-Session: https://claude.ai/code/session_01BhWEaLQcb61c4Q6dnjv3e5

* fix: wire local tmux helpers into unit tests

(cherry picked from commit 2a9ca7b)

* fix: share CLI error with local tmux tests

(cherry picked from commit fc1dc8a)

* fix: always terminate the recorded SSH auth root

* fix: type the Bun script entrypoint

* fix: require a frozen tree before journal backstop

* test(web): type mock call assertions

* docs(cloud): sync bundled vm kind guidance

* fix: restore terminal test stubs and frozen SSH cleanup

* test(web): type observability mocks

* docs(cloud): align agent recipes with current devbox sessions

* chore: preserve main Bonsplit revision after reconciliation

* fix: finish transfer progress lines and repair image test typecheck

* fix(cloud): localize pool recovery guidance and correct desktop recipe

* test(cloud): keep transfer progress error regression in CI

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
aerickson pushed a commit to aerickson/cmux that referenced this pull request Sep 13, 2026
…, drift check, router prune fix (manaflow-ai#10793)

* worktree: drop stored defaults on identity lets so Xcode 26.6 builds main

After manaflow-ai#10781, worktreeDeviceID/worktreeFileID were both defaulted at the
declaration and assigned in the explicit init, which the current toolchain
rejects ("immutable value may only be initialized once"). The init's
parameter defaults keep the same call-site contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cli: cmux vm run/push/pull/wait and the cmux-cloud-vm agent skill

vm run routes a command to a cloud machine without naming one: sticky
per-directory binding, then an idle agent-pool machine, then a sleeper,
then a freshly provisioned pool machine. push/pull move files over the
exec channel (base64 chunks, SHA-256 verified, directories as tarballs);
wait blocks until ready and optionally wakes the machine. The skill lets
any coding agent drive machines from plain CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm push: 64 KiB argv-bound chunks, no AppleDouble sidecars, line-safe progress

Live dogfood on Blaxel: a 512 KiB chunk base64-encodes past Linux's 128 KiB
per-argument limit ("argument list too long"), macOS tar shipped ._* files
onto the machine, and chunk progress ran together when stderr was captured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: pool membership is the persisted id list, not the display label; review fixes

- The router now only drafts machines it provisioned itself (ids recorded in
  ~/.cmuxterm/vm-run-pool.json at create time, pruned when machines vanish); a
  user machine renamed agent-pool is never used. Test covers the impostor.
- Staging tarball is removed if reading it throws before the defer is armed.
- Push/pull chunk progress is localized (cli.vm.push.progress, cli.vm.pull.progress).
- vm --help, the usage contract, and the contract doc list open/ports/tools/
  handoff/promote-template, which the dispatcher already handled.
- Sticky-binding fixture uses a fixed instant, not the host clock.
- Skill recipes: --sync runs inside the synced dir (no remote $PWD), port
  readiness poll instead of sleep, eligibility filter instead of .vms[0],
  background test exit status captured to a status file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: lock the pool store across processes; idempotent, run-scoped recipes

- updateVMRunPool does the read-modify-write under flock on a sibling lock
  file, so two routers provisioning at once both land in the store; covered by
  a two-process test against two mock sockets.
- Dev-server recipe reuses a live server or starts one with a workspace pidfile
  and log; test recipe uses per-run log/status paths written atomically.
- Document that --sync is additive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: pool-store failures propagate; recipes validate the dev server and use unique run ids

- updateVMRunPool/saveVMRunPool throw on lock or write failure and createPoolVM
  reports the machine it provisioned but could not record, instead of a silent
  unlocked update.
- The dev-server recipe reuses a server only when the recorded pid is alive and
  owns :3000 (netstat -p), refuses to start a second server on a port someone
  else owns, and clears stale metadata.
- Test-run ids come from uuidgen, not the epoch second.
- Skill docs: cmux vm shell is a cmux-tui session now that machines run the
  cmux-tui remote daemon; agents keep working through vm run/exec.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* vm run: regression test — pruning a stale pool id must keep an id recorded after the vm.list snapshot

The mock socket records pool-2 while answering vm.list and returns a list that
predates it; the store must end up {pool-1, pool-2} with gone-1 pruned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: prune only ids the pre-list snapshot saw as gone; product-level pool-store error

- Load the pool store before vm.list and subtract only the ids that snapshot
  lacks in the live list, instead of intersecting the locked set with a stale
  live snapshot, so a machine another vm run recorded meanwhile is never
  dropped from the pool.
- The provisioned-but-unrecorded error no longer interpolates the raw
  pool-store error (lock path, OS text); it keeps the machine id and the
  recovery commands.
- The unknown-size errors for vm run/route/agent list 24g, which
  parseCloudVMSize already accepts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cli: cmux vm <verb> --help prints the verb's own usage, offline

--help/-h short-circuited to the cmux vm overview for every verb, so the
option lists for run, route, agent, push, pull, wait, open, tree, workspace,
terminal, tui, prompt, and base (--size, --timeout, placement flags, --json
shapes) were unreachable without a running app and a usage error. A new
CLI/CMUXCLI+VMHelp.swift maps those verbs to their usage strings; the prompt
and base usages move out of the handler so they can be shared.

Also: the overview lists workspace and terminal, points at per-verb help,
no longer claims vm prompt --open accepts pi (the app supports
claude|codex|opencode), and the shell/desktop lines read in order.

docs/cli-contract.md: the vm/cloud usage probe now matches the binary (it
lacked prompt, so the no-socket contract lane was red), plus one offline
probe per routed verb and cmux surface --help.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmux-cloud-vm skill: the complete cmux Cloud CLI set, with a CI drift check

references/commands.md is now the single reference for every cmux vm verb
(and cmux cloud alias): usage, aliases, flags, --json shape, exit codes, the
socket method it calls, and the sidebar action it mirrors, grouped machine /
files / execution / routing / workspaces & terminals / surfaces & display /
checkpoints & forks / networking & ports / account & plan, plus the app's
vm.* socket table. Verbs that exist only in open PRs sit in one labeled
"In flight" section (manaflow-ai#11324 cmux fork, manaflow-ai#11347), so the skill never names
something an agent cannot run today; manaflow-ai#11345 (vm terminal send|read|wait, the
single sidebar Close Workspace…) merged during this work and is folded in.

SKILL.md leads with vm run, then the glossary, cloud-vs-local, a need→verb
table, headless terminal loops, agent policy, and troubleshooting.
agent-workflows.md gains the headless-terminal recipe; openai.yaml describes
the same scope for Codex; Resources/cloud-agent-skill.md (the copy vm prompt
installs) no longer disagrees with the CLI (24g, vm base open, plain-terminal
shell, ~30 s exec, vm wait/handoff/prompt/ssh-info/promote-template,
fork/restore flags, per-verb --help).

tests/test_cloud_vm_skill_coverage.py (workflow-guard-tests lane) parses the
vm dispatcher, the workspace/terminal/surface sub-verbs, the usage line, the
docs/cli-contract.md probe, and the advertised vm.* methods, and fails when
the skill and the CLI disagree in either direction or when an in-flight verb
has already shipped.

Localization audit: CLI help/usage text follows the English-only CLI help
convention; no Settings, menu, or web strings touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: re-read the pool after pruning so a concurrently recorded machine is eligible; full -h probe needles

A machine another vm run recorded between this run's pool load and vm.list
(and that the list carries) was not in the pre-list snapshot, so it was
ineligible for this run and could push it toward a needless provision or a
false would_provision from vm route. The eligible set is now the post-prune
store intersected with the live list.

docs/cli-contract.md: the -h / cloud run / upload probes name the full usage
line, same as their --help siblings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* test_cloud_vm_skill_coverage: fail loudly when the unknown-verb usage line cannot be found

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* tests: carry manaflow-ai#11346's two-line cmuxTests compile fix so the test bundle builds on this branch

Same lines as manaflow-ai#11346 (the CloudTreeNodeActions fixture gained
projectInLocalWorkspace in manaflow-ai#11345; SidebarFileDropFindRoutingTests needs
import Bonsplit after manaflow-ai#11059). Whichever lands first, the other merges clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmuxTests: align CFFIXED_USER_HOME with a test's HOME whenever the child inherits a CF home redirect

On the hosted e2e lane the console session forwards CFFIXED_USER_HOME without
CMUX_APP_HOST_ISOLATION_REQUIRED, so every CLI the process harness spawned
resolved NSHomeDirectory() to the runner's home and ignored the test's HOME:
the vm run pool/binding stores, SSH ~ expansion, and hook installs all landed
outside the per-test home (126 failures across the class, including main's
own testVMRunReusesIdlePoolMachine). The harness now aligns
CFFIXED_USER_HOME with HOME when either the isolation flag is set or a
CFFIXED_USER_HOME redirect is already present.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cmux-cloud-vm skill: provisioning is gated to paid plans (vm_requires_pro) after manaflow-ai#11332

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* vm run: resolve the router's state home from $HOME; harness pins CFFIXED_USER_HOME to a test's HOME

NSHomeDirectory() resolves through Core Foundation (CFFIXED_USER_HOME, then
the passwd entry) and ignores a HOME override — the comment claiming it honors
$HOME was wrong. So the pool and binding stores, documented as HOME-relative,
went to the real ~/.cmuxterm in every redirected run, and the router tests
(main's own included) only passed under CI's app-host isolation, where the
harness aligned CFFIXED_USER_HOME. Reproduced on a fleet Mac's GUI session
(274 tests, 120 failures) with the same signature as the hosted lane.

- CLI: vmRunStateHomeDirectory() prefers a non-empty $HOME, else
  NSHomeDirectory(); both store URLs use it.
- cmuxTests: isolatedCLIChildEnvironment pins CFFIXED_USER_HOME to the
  supplied HOME unconditionally (XDG_CONFIG_HOME still only under the
  app-host isolation flag), so every spawned CLI agrees with the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: mark the CLA policy guard's GitHub-hosted runner as required so the self-hosted guard passes

manaflow-ai#11387/manaflow-ai#11407 added cla-policy-guard.yml on a bare ubuntu-24.04 runner, which
tests/test_ci_self_hosted_guard.sh forbids without the github-hosted-required
marker; workflow-guard-tests has been red on main since. The guard is a
base-controlled pull_request_target workflow, so a GitHub-hosted runner is
the intended trust boundary — same marker the browser, npm-provenance, and
attestation jobs carry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: reword the CLA policy guard runner marker so the fleet-label rule does not match its comment

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill + vm new help: no desktop image ships today; Freestyle default; paid plans uncapped

manaflow-ai#11566 removed Blaxel and flipped vm new to shell-only-by-default but left
the cmux vm overview claiming desktop-by-default — the overview now matches
the dispatcher. The skill (SKILL.md, commands.md, agent-workflows.md, the
bundled cloud-agent-skill.md) drops the xfce/noVNC/CUA desktop claims,
documents --desktop failing closed until a desktop image lands, the
e2b|freestyle|daytona provider set with Freestyle as the server-side default,
and manaflow-ai#11580's uncapped paid plans (the 'no limit' plan meter line).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* web: carry the main-CI fixes for the Blaxel removal so this PR's merge ref is green

Verbatim from open manaflow-ai#11586 (Blaxel-removal migration applies on a fresh
database via ::text enum comparisons — same fix as manaflow-ai#11582 — plus the
cmuxTuiDaemon shell wiring and the freestyle shell-repair test removal it
replaces with vm-cmux-tui coverage), and the pricing-page test updated to
the 'Unlimited' concurrent-VMs copy manaflow-ai#11580 shipped. Whichever lands first,
the rest merge clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: mark the port-URL verbs dormant — no driver implements open-port on any current deployment

web/services/vms/desktopWrapper.ts and the workflow answer 'open-port is not
supported by this deployment'; the CLI verbs exist and are kept documented,
but the skill no longer implies a working port URL today.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: list manaflow-ai#11609's vm link and port-preview TLS edge as in flight

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: spell out the full manaflow-ai#11609 surface under In flight (vm link, live port previews, attach_transports, tree workspaces, placement hardening)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* ci: restore main's cla-policy-guard.yml verbatim — the base-controlled guard rejects PR-side edits, and the runner guard now exempts the file by path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cloud: clear the three main-actor isolation warnings manaflow-ai#11421 left over budget

tests-build-and-lag has been red since manaflow-ai#11421: finishedUserInfoKey referenced
from the notification observer's Sendable closure, and .shared used as a
default argument (default values evaluate in a nonisolated context) in
MachinesPanelViewModel.init and NewMachineSheetPresenter.presentNewMachine.
The string constant becomes nonisolated; the default arguments become
optional and resolve to .shared inside the main-actor bodies. Verified on a
fleet builder: cmux-unit build-for-testing succeeds with zero warnings in
these files. No behavior change; explicit-coordinator callers (tests)
unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: note manaflow-ai#11609's grow-only sizing under In flight

* ci: make the manaflow-ai#11524 release-origins gate pass the Linux guard harness (fixes manaflow-ai#11757)

Three gaps broke workflow-guard-tests on every merge ref since manaflow-ai#11524:
- verify-ios-release-origins.sh read plists only via /usr/libexec/PlistBuddy,
  which does not exist on the Linux guard lane, so every key read <absent>
  and the gate failed closed. It now falls back to python3 plistlib when
  PlistBuddy is missing; the absolute path stays first so PATH can never
  shadow the reader in a release lane.
- The fake archives in tests/test_ios_appstore_lane_identity.py never baked
  the production-origin keys a real Release build carries; both fixture
  writers now stamp CMUXAuthEnvironment/CMUXApiBaseURL/CMUXIrohBrokerBaseURL/
  CMUXPresenceBaseURL.
- The isolated-repo fixture copied upload-testflight.sh but not the new lib
  script it calls, so the auto-version lane failed on a missing file.

tests/test_ios_appstore_lane_identity.py: 77/77 ok, exit 0 locally (macOS
PlistBuddy path); the plistlib path verified standalone and by CI's Linux lane.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* cloud: Sendable ISO8601 parsing in VMClient; nonisolated presence URL resolver

Newest main marked two ISO8601DateFormatter statics nonisolated (a warning:
the type is not Sendable) and left PresenceHeartbeatClient.resolvedServiceURL
main-actor-isolated while PresenceHeartbeatClientTests calls it from
nonisolated Swift Testing contexts, which stops cmuxTests compiling on every
app-host shard. The formatters become Date.ISO8601FormatStyle constants
(Sendable, same accepted formats) parsed via Date(_:strategy:), and the
resolver — a pure function of its environment/defaults arguments over
nonisolated PresenceSettings/AuthEnvironment statics — becomes nonisolated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PEWn6ZZoGTAi67X3RSJ5aB

* skill: document cmux vpn hosts, extend the drift check to the vpn dispatcher, refresh the in-flight facts from freestyle-vm-primitives

cmux vpn hosts landed with manaflow-ai#11626 but the skill's vpn section stopped at
revoke; the coverage check only parsed the vm dispatcher, so nothing
caught it. The check now parses runVPNCommand the same way and fails on
a vpn verb the reference misses or invents (it flagged the in-flight
section's own wording during this change).

The in-flight section also claimed a hosts verb family was arriving with
the guest-CLI work — wrong on both ends: vpn hosts already ships here,
and freestyle-vm-primitives has no vm hosts verb. Replaced with what
that branch actually adds today: the guest cmux shim + in-VM notify
bridge, vm help, the screen->display catalog kind rename, and the
vm tree --refresh fleet re-read.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: re-ground on the desktop image and live private-path port opens from newest main

manaflow-ai#11776 baked the TigerVNC desktop into the devbox image and manaflow-ai#11756/manaflow-ai#11776
gave the Freestyle driver its first openPort — the URL is the machine's
private VPC address behind the WireGuard tunnel, never a public ingress.
So --desktop no longer fails closed, vm desktop works on desktop-kind
machines (private address on 6901, vpn required, base machines exit 1),
and the port verbs are no longer dormant. The reference, SKILL.md,
agent-workflows, and the bundled cloud-agent-skill now say so, and the
in-flight notes shrink to what freestyle-vm-primitives still adds: the
public TLS-edge previews on tokened subdomains and the vm-new
desktop-by-default flip (vm base open has been desktop-default since
manaflow-ai#10948 — the CLI's two kind parsers differ today, which docs/cli-contract.md
already papers over by describing the flipped default).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: teach the delegation mission — persistence past the closed laptop, staged machine workspaces

The point of the CLI is a local agent delegating work to the cloud, so
the skill now says so up front (sessions live in the machine's daemon
and survive the Mac disconnecting; reattach from any signed-in Mac) and
gains the staged-workspace recipe: compose a named machine workspace's
terminals headlessly with surface new-terminal --remote-workspace,
verify with vm tree --json, and hand the user one click that opens the
whole thing. Honest about today's two edges: vm workspace new always
opens a local workspace as a side effect, and vm agent cannot target a
workspace (use surface new-terminal with a login shell instead).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* cli+skill: the 20g plan machine is the only size preset — say so everywhere

Main's plan-machine change (manaflow-ai#11756/manaflow-ai#11783) reduced cloudVMSizeAliases to
20g/20gb (or raw MB), but the error strings and usage lines still
advertised the retired 2g-32g ladder — ours worse, still carrying the
24g we added when that preset existed. vm run/route/agent unknown-size
errors, the vm new usage and unknown-flag text, docs/cli-contract.md's
vm new row (matching the freestyle-vm-primitives wording to keep that
merge clean), and every skill mention now name 20g (the 5 vCPU / 20 GB
/ 200 GB plan machine) or raw MB — matching parseCloudVMSize instead of
misleading an agent into a rejected --size 8g.

Also taken in this merge: main's manaflow-ai#11754 landed the Linux iOS-guard fix
this branch had been carrying, so those files resolve to main's
(77/77 local pass).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* skill: note headless staging flags coming in freestyle-vm-primitives

cmux176 implemented the two staging gaps flagged earlier — vm workspace
new --no-open and vm agent --remote-workspace — so the in-flight section
now names them and points §6b's workarounds at their replacement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: silence the guard-condition trailing-closure warning Xcode 26.3 added

The critical-pressure teardown hardening (via main) left two compactMap
trailing closures inside postAggregateMemoryPressureWarning's guard
condition; Xcode 26.3's compiler warns 'trailing closure in this context
is confusable with the body of the statement' on both (76:41, 77:41),
which fails the warning-budget lane with actual=2 budget=0 — on main's
own runs too (run 33716921978 shows the same +2). Parenthesized closure
arguments are the fix the diagnostic prescribes; no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: adapt the Base create launch to the 3-argument coordinator Launch

Two green PRs crossed on main: manaflow-ai#10773 added a 2-argument
MachineCreateCoordinator.start call in the Base sheet flow while manaflow-ai#11773
changed Launch to (arguments, progress, completion) for the pending
row's live output — main has not built the combination yet, and the
first tree containing both fails with 'contextual closure type expects
3 arguments'. The Base flow now takes the progress handler and threads
it through launchCloudVMBaseOpen into CloudVMActionLauncher's existing
onOutput, so Base creates stream output to the pending row exactly like
the New Machine sheet's flow in NewMachineSheetPresenter.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: make MachineCreateCoordinatorTests compile again after manaflow-ai#11773

Two fixes for main's own test file (byte-identical there, so main's
cmuxTests target does not compile either): #expect took the Bool? from
optional-chained isSuperseded (== true resolves it), and the new
MachinesPanelPendingCreateTests suite called Self.newMachineRequest for
a helper that lives on MachineCreateCoordinatorTests — qualifying the
type fixes the lookup and gives the trailing 'name: nil' its context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: reconcile cloud CLI branch with current main

* fix: import workspace group test model

* docs: keep Cloud skill metadata within UI contract

* docs: align Cloud VM lifecycle and tree guidance

* chore: drop accidental web test diff

* docs: clarify Cloud surface rollout behavior

* test: align Freestyle SDK fixture

* cli: keep Cloud VM help lists complete

* fix: resolve Swift 6 callback isolation warnings

* fix(ssh): signal stopped auth descendants reliably

(cherry picked from commit d73ecd7)

* fix(ssh): start cleanup deadline after snapshot

(cherry picked from commit 875c68a)

* fix(ssh): keep cleanup signal paths fork-free

* test(cloud): use explicit issue comments in port regression

* fix(ssh): keep frozen auth cleanup fork-free

* docs(cloud): document VM disk resize

* fix(ssh): deduplicate frozen cleanup journal

* fix(ssh): recover from fork-starved cleanup

* fix(ssh): normalize completed cleanup status

* fix(ssh): finish cleanup without marker discovery

* test(ssh): explain cleanup exit failures

* test(cloud): wire resize action fixture

* test(ssh): isolate deadline fixture process group

* test(terminal): stub bounded selection clipboard read

* test(ssh): make backoff signal fixture deterministic

* test: refresh merged web fixtures

* docs: sync cloud VM skill with CLI parity

* Revert the test-only half of manaflow-ai#11929 so the unit test bundle compiles

manaflow-ai#11929 merged 265 lines of
SurfaceCatalogTests that call beginCloudWorkspaceRename,
commitCloudWorkspaceRename, rollbackCloudWorkspaceRename,
replaceCloudResources and pendingCloudWorkspaceRenameName. None of those
exist in the app: the PR landed only its test file. Since that merge
(2026-09-06) every cmuxTests build on main fails, so no hosted unit test
run can pass. Austin authored this revert on another branch
(68e2dbc) but it never reached main. Re-land the feature with tests
and implementation together.

(cherry picked from commit 68e2dbc)

Claude-Session: https://claude.ai/code/session_01BhWEaLQcb61c4Q6dnjv3e5

* fix: wire local tmux helpers into unit tests

(cherry picked from commit 2a9ca7b)

* fix: share CLI error with local tmux tests

(cherry picked from commit fc1dc8a)

* fix: always terminate the recorded SSH auth root

* fix: type the Bun script entrypoint

* fix: require a frozen tree before journal backstop

* test(web): type mock call assertions

* docs(cloud): sync bundled vm kind guidance

* fix: restore terminal test stubs and frozen SSH cleanup

* test(web): type observability mocks

* docs(cloud): align agent recipes with current devbox sessions

* chore: preserve main Bonsplit revision after reconciliation

* fix: finish transfer progress lines and repair image test typecheck

* fix(cloud): localize pool recovery guidance and correct desktop recipe

* test(cloud): keep transfer progress error regression in CI

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

This branch was successfully deployed

2 active deployments
Preview – cmux41 — da869b7b Deployed Sep 3, 2026 by vercel[bot]
Preview – cmux166 — da869b7b Deployed Sep 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix Linux iOS App Store lane identity guard

1 participant