Skip to content

Mac Vm Updates#324

Merged
arul28 merged 6 commits into
mainfrom
ade/mac-vm-updates-9d9cfd88
May 20, 2026
Merged

Mac Vm Updates#324
arul28 merged 6 commits into
mainfrom
ade/mac-vm-updates-9d9cfd88

Conversation

@arul28
Copy link
Copy Markdown
Owner

@arul28 arul28 commented May 20, 2026

Summary

Describe the change.

What Changed

Key files and behaviors.

Validation

How you tested.

Risks

Anything to watch.

Summary by CodeRabbit

  • New Features
    • Full macOS VM workspace: VM tab/page with lifecycle controls (provision/start/stop/restart/wipe), embedded VNC display sessions, first-boot guided steps, runtime install, save/load credentials, detach lanes, storage info, VM badges and VM-backed lane creation.
  • Bug Fixes
    • Hardened lane deletion recovery from guest-created unreadable files; clarified and extended VM operation timeouts and reliability.
  • Documentation
    • Added Plan and Source-Command-Audit skill docs and extended CLI examples for macOS VM commands.

Review Change Stack

Greptile Summary

This PR adds full macOS VM support to the desktop app: provision/start/stop/wipe/restart lifecycle, IPSW-based provisioning with resume, SSH-backed lane execution, a WebSocket VNC display proxy, Keychain-backed credential storage, phase-based onboarding UI, and hardened lane deletion for guest-created unreadable files.

  • VM launch context cache (laneLaunchContext.ts): TTL raised to 30 minutes and the comment claims lifecycle events drive invalidation, but only placement-changed events actually call refreshVmLaneLaunchCache. Credential saves and VM stop/start do not invalidate the cache, leaving agents blocked or pointing at stale SSH targets.
  • Credential storage (credentialsStore.ts): New Keychain-backed store shells out to security add-generic-password -w <json>, passing the full credential payload as a CLI argument where it is briefly visible in the process table.
  • IPC timeouts (ipcTimeouts.ts): All three new VM operations now carry correct long timeouts, resolving previous review findings on that file.

Confidence Score: 3/5

The VM feature is partially gated by a runtime-install stub that prevents runtime_ready from being reached in production, but the cache wiring gaps will bite immediately once the stub is replaced.

Two concrete correctness gaps exist in the launch-context cache: credential saves and VM lifecycle changes (stop/start) do not call refreshVmLaneLaunchCache or invalidateVmLaneLaunchCache, meaning a not-ready entry can block agent turns for up to 30 minutes after credentials are saved, and a ready entry can persist after the VM stops. These gaps are currently masked by the runtime-install stub, but will cause immediate workflow breakage when the stub is completed. The onRuntimeReady hook is wired in main.ts but never called in the service, adding another gap in the same critical path.

apps/desktop/src/main/main.ts (cache invalidation for setCredentials and VM lifecycle events), apps/desktop/src/main/services/macosVm/credentialsStore.ts (credential in CLI args), apps/desktop/src/main/services/macosVm/macosVmService.ts (onRuntimeReady never called)

Security Review

  • Credential JSON in CLI args (credentialsStore.ts line 173): The full credential payload — {"username":"…","password":"…","savedAt":"…"} — is passed as the -w argument to /usr/bin/security add-generic-password. It is briefly visible in the process table to any process running as the same macOS user. The SSH password is kept out of argv via the SSHPASS env var, but no equivalent protection exists here.

Important Files Changed

Filename Overview
apps/desktop/src/main/main.ts Wires macosVmService, laneService VM hooks, and the launch-context provider. Two gaps: setCredentials never calls refreshVmLaneLaunchCache, and onEvent for macosVmService doesn't invalidate the cache on VM lifecycle changes.
apps/desktop/src/main/services/macosVm/credentialsStore.ts New Keychain-backed store using security CLI. Well-structured with SIGKILL fallback, but the full credential JSON (including plaintext password) is passed as -w payload on the CLI.
apps/desktop/src/main/services/macosVm/macosVmService.ts Large expansion adding display proxy, global lease, credential integration, IPSW download with resume, and wipe/restart/installRuntime. VNC password correctly excluded from argv. onRuntimeReady hook declared but never called.
apps/desktop/src/main/services/lanes/laneLaunchContext.ts TTL increased to 30 minutes. Comment claims lifecycle events drive invalidation but only placement-changed is wired.
apps/desktop/src/main/services/ipc/ipcTimeouts.ts All new VM operations now have correct long timeouts, resolving previous review findings.
apps/desktop/src/main/services/macosVm/macosVmRecovery.ts New recovery module for lane deletion with best-effort lume delete using a 1-minute timeout.
apps/desktop/src/main/services/lanes/laneService.ts Adds VM lane attach/detach, hardened directory removal, and LaneMacosVmHooks interface. Symlink handling correctly prevents loop traversal.
apps/desktop/src/renderer/components/vm/MacVmPage.tsx New 1551-line VM management page with phase-based onboarding, credential prompting, and lifecycle controls.

Comments Outside Diff (4)

  1. apps/desktop/src/main/services/macosVm/macosVmService.ts, line 1029-1031 (link)

    P1 security VNC password exposed in process argument list

    The VNC password is embedded directly in the vnc:// URL and passed as a command-line argument to /usr/bin/open. Any local process can read it from the process table via ps aux or /proc/<pid>/cmdline for the brief time the open command runs. This is the exact threat the codebase already mitigates for SSH passwords (using SSHPASS env var so the secret never appears in argv). The same precaution is missing here.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/macosVm/macosVmService.ts
    Line: 1029-1031
    
    Comment:
    **VNC password exposed in process argument list**
    
    The VNC password is embedded directly in the `vnc://` URL and passed as a command-line argument to `/usr/bin/open`. Any local process can read it from the process table via `ps aux` or `/proc/<pid>/cmdline` for the brief time the `open` command runs. This is the exact threat the codebase already mitigates for SSH passwords (using `SSHPASS` env var so the secret never appears in argv). The same precaution is missing here.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  2. apps/desktop/src/main/services/ipc/ipcTimeouts.ts, line 38-95 (link)

    P1 New VM operations missing from ipcTimeouts — will always time out

    macosVmInstallRuntime, macosVmRestart, and macosVmWipe are not listed in the switch statement, so they all fall through to the 30-second default. Each of these operations far exceeds 30 s: installRuntime runs an SSH run-script phase with a 120-second internal timeout, restart sequences through stop (2 min) + start (up to 120 min), and wipe calls deleteVm with a 10-minute lume-delete budget. Because the main process wraps every ipcMain.handle in Promise.race([listener, timeoutPromise]), all three handlers will always reject with "IPC handler timed out" on any non-trivial guest or VM, while the underlying SSH/lume process continues running in the background — leaving the record state out of sync with what the renderer observed.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/ipc/ipcTimeouts.ts
    Line: 38-95
    
    Comment:
    **New VM operations missing from ipcTimeouts — will always time out**
    
    `macosVmInstallRuntime`, `macosVmRestart`, and `macosVmWipe` are not listed in the switch statement, so they all fall through to the 30-second default. Each of these operations far exceeds 30 s: `installRuntime` runs an SSH `run-script` phase with a 120-second internal timeout, `restart` sequences through `stop` (2 min) + `start` (up to 120 min), and `wipe` calls `deleteVm` with a 10-minute lume-delete budget. Because the main process wraps every `ipcMain.handle` in `Promise.race([listener, timeoutPromise])`, all three handlers will always reject with "IPC handler timed out" on any non-trivial guest or VM, while the underlying SSH/lume process continues running in the background — leaving the record state out of sync with what the renderer observed.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  3. apps/desktop/src/main/services/ipc/ipcTimeouts.ts, line 46-54 (link)

    P1 Three new VM operations fall through to the 30-second default timeout

    macosVmInstallRuntime, macosVmRestart, and macosVmWipe are not in the switch, so they all get 30 s. Each exceeds that trivially: installRuntime drives a 120-second SSH run-script phase; restart sequences through stop (2 min) then start (up to 120 min); wipe calls deleteVm which has a 10-minute budget. Because main.ts wraps every ipcMain.handle in Promise.race([listener, timeoutPromise]), all three handlers will always reject with "IPC handler timed out" on any real VM while the underlying SSH / lume process keeps running in the background, leaving record state out of sync with what the renderer observed.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/services/ipc/ipcTimeouts.ts
    Line: 46-54
    
    Comment:
    **Three new VM operations fall through to the 30-second default timeout**
    
    `macosVmInstallRuntime`, `macosVmRestart`, and `macosVmWipe` are not in the switch, so they all get 30 s. Each exceeds that trivially: `installRuntime` drives a 120-second SSH `run-script` phase; `restart` sequences through `stop` (2 min) then `start` (up to 120 min); `wipe` calls `deleteVm` which has a 10-minute budget. Because `main.ts` wraps every `ipcMain.handle` in `Promise.race([listener, timeoutPromise])`, all three handlers will always reject with "IPC handler timed out" on any real VM while the underlying SSH / lume process keeps running in the background, leaving record state out of sync with what the renderer observed.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

  4. apps/desktop/src/main/main.ts, line 3374-3380 (link)

    P1 VM lifecycle events don't invalidate the launch cache

    onEvent for macosVmService only forwards payloads to the renderer via emitProjectEvent. When the VM stops, a ready cache entry (containing the SSH target) stays valid for up to 30 minutes. An agent starting a turn on a stopped VM will attempt SSH and hang until timeout, rather than getting a fast VmNotReadyError. The comment in laneLaunchContext.ts claims "lifecycle events (start, stop, restart, placement-changed, deleted) drive explicit invalidation" but only placement-changed actually does. The onEvent handler should call invalidateVmLaneLaunchCache() on vm-updated events where the new state is not running/runtime_ready.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/main/main.ts
    Line: 3374-3380
    
    Comment:
    **VM lifecycle events don't invalidate the launch cache**
    
    `onEvent` for `macosVmService` only forwards payloads to the renderer via `emitProjectEvent`. When the VM stops, a `ready` cache entry (containing the SSH target) stays valid for up to 30 minutes. An agent starting a turn on a stopped VM will attempt SSH and hang until timeout, rather than getting a fast `VmNotReadyError`. The comment in `laneLaunchContext.ts` claims "lifecycle events (start, stop, restart, placement-changed, deleted) drive explicit invalidation" but only `placement-changed` actually does. The `onEvent` handler should call `invalidateVmLaneLaunchCache()` on `vm-updated` events where the new state is not `running`/`runtime_ready`.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Claude Code

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
apps/desktop/src/main/main.ts:3378-3380
**`setCredentials` never refreshes the VM launch cache**

After a user saves guest credentials via `setCredentials`, the `vmLaunchContextCache` is never invalidated. The most likely setup flow is: (1) create VM lane while VM is `runtime_ready` but credentials haven't been saved yet → `refreshVmLaneLaunchCache` caches `not-ready`; (2) user saves credentials; (3) agent turn fires → `getCachedVmLaunchContextSync` returns `not-ready``VmNotReadyError` thrown for every turn for up to 30 minutes until the TTL expires. `invalidateVmLaneLaunchCache(laneId)` (or a full `refreshVmLaneLaunchCache`) needs to be called from the `setCredentials` IPC path after a successful credential save.

### Issue 2 of 4
apps/desktop/src/main/main.ts:3374-3380
**VM lifecycle events don't invalidate the launch cache**

`onEvent` for `macosVmService` only forwards payloads to the renderer via `emitProjectEvent`. When the VM stops, a `ready` cache entry (containing the SSH target) stays valid for up to 30 minutes. An agent starting a turn on a stopped VM will attempt SSH and hang until timeout, rather than getting a fast `VmNotReadyError`. The comment in `laneLaunchContext.ts` claims "lifecycle events (start, stop, restart, placement-changed, deleted) drive explicit invalidation" but only `placement-changed` actually does. The `onEvent` handler should call `invalidateVmLaneLaunchCache()` on `vm-updated` events where the new state is not `running`/`runtime_ready`.

### Issue 3 of 4
apps/desktop/src/main/services/macosVm/credentialsStore.ts:173-182
**Credential JSON exposed as a CLI argument**

The full `payload` string — which contains the username and plaintext password serialised as JSON — is passed as the `-w` argument to `security add-generic-password`. It is briefly visible in the process table (e.g. via `ps aux`) to any process running as the same user. The SSH password is deliberately hidden from argv by using `SSHPASS` (env var), but no equivalent protection is applied here. The macOS `security` CLI has no stdin path for `-w`, so a secure alternative would require a native Keychain binding (keytar) or a small Swift helper that calls the C Security framework API directly.

### Issue 4 of 4
apps/desktop/src/main/services/macosVm/macosVmService.ts:194-199
**`onRuntimeReady` callback is never invoked**

`onRuntimeReady` is declared in `CreateMacosVmServiceArgs` and wired up in `main.ts` with a log statement, but there is no call site inside `macosVmService.ts` that invokes `args.onRuntimeReady(...)`. Any code in `main.ts` that relies on this hook to fire (e.g. wiring up `RemoteConnectionPool.registerMacosVmTarget`) will never execute. The `TODO` comment next to the hook in `main.ts` acknowledges this, but the dead parameter surface increases risk of the hook being missed when the runtime install stub is replaced.

Reviews (6): Last reviewed commit: "ship: iter 5 — match wipe/delete IPC bud..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

… install, and credentials

Move the macOS VM workflow out of the inline lane panel into a dedicated /vm
tab. Add lifecycle/menu UI (MacVmPage, VmLifecycleMenu, FirstBootCard,
CredentialsPromptDialog, PhaseStepper), a credentials store, ADE runtime
bootstrap for guest VMs, and recovery for stale guest-created worktree dirs.
Wire 20+ macos-vm adeActions and CLI subcommands; update docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 20, 2026

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
ade Ignored Ignored Preview May 20, 2026 10:10pm

@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@copilot review but do not make fixes

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

Adds singleton macOS VM support and VM-backed lanes: new public types, IPC channels, DB migration, services (credentials, recovery, runtime bootstrap, VNC), lane attach/detach and launch-context SSH routing, preload/IPC routing, renderer /vm UI, CLI subcommands, tests, and skill docs.

Changes

Singleton macOS VM and VM-backed lanes

Layer / File(s) Summary
All changes (review path)
apps/desktop/*, apps/ade-cli/*, apps/ios/*, apps/desktop/src/renderer/*, .agents/skills/*
Comprehensive end-to-end implementation: types/contracts and IPC channels, DB migration adding runtime_placement, lane service/attach-detach and launch-context SSH routing, macOS VM services (credentials store, recovery, runtime bootstrap, resilient VNC with imageState), preload and IPC routing changes, renderer /vm workspace and components, CLI surface/help and README examples, test additions/updates, and skill documentation.
  • Estimated code review effort:
    🎯 5 (Critical) | ⏱️ ~120 minutes

  • Possibly related PRs:

    • arul28/ADE#315 — Also modifies rfbDirectClient blank-frame handling and screenshot metadata in the macOS VM path.
    • arul28/ADE#274 — Modifies CreateLaneDialog and lane creation UI flow related to VM-backed lane changes.
    • arul28/ADE#185 — Related changes to ADE CLI guidance and prompt content.
  • Suggested labels:
    desktop, ios

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ade/mac-vm-updates-9d9cfd88

@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 20, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

@copilot review but do not make fixes

Here's a review of the changes in this PR.


Overall

This is a large, well-structured PR that moves the macOS VM workflow into a dedicated /vm tab. The architecture (service layer → IPC → preload → renderer) is sound and follows existing app conventions. Below are findings organized by severity.


🔴 Bugs / Latent Issues

1. runtimeBootstrap.ts is a stub that silently succeeds (runtimeBootstrap.ts:119-129)
The GUEST_BOOTSTRAP_SCRIPT only writes a timestamp marker. It has a TODO acknowledging this, but the full installRuntime() IPC flow is already wired into macosVmService, the phase stepper, and the renderer's "Install agent runtime" button. A user clicking that button will see phase 9 → phase 10 advance, but nothing is actually installed in the guest. This should either block the "install runtime" action behind a feature flag or clearly degrade the phase to failed with an explanatory message until the real download/install script lands.

2. credentialsStore.ts hangs on security SIGTERM (credentialsStore.ts:88-90)
The defaultRunCommand in the credentials store fires SIGTERM on timeout but never follows up with SIGKILL, and never settles the Promise. If security add-generic-password hangs (e.g. permission dialog blocking), the call site will await indefinitely. Compare: macosVmService.ts's defaultRunCommand has a 500 ms SIGKILL follow-up. The credentials store should match that pattern.

3. recoverLargestPartialDownload is not content-addressed (macosVmService.ts:837-854)
When multiple .part-* files exist for the same base filename, the function picks the largest by size and renames it to the stable .part name. If prior downloads fetched a different IPSW URL under the same filename, the "largest" partial could be from a different (incompatible) image. curl --continue-at - will then try to resume at the wrong offset into the wrong content. A content-hash or URL-keyed cache would prevent this.


🟡 Security Observations

4. VNC password crosses the IPC boundary (macosVmService.ts:2986, global.d.ts:MacosVmDisplaySession)
getDisplaySession returns password: proxy.password in the IPC response, so the raw VNC password reaches the renderer. The WebSocket proxy token (/${token}) already provides a secondary authentication layer on 127.0.0.1, so the renderer doesn't need the password at all — noVNC's credentials option could be omitted if the proxy handles VNC auth internally. As-is, the password is accessible to any script in the renderer context.

5. VNC password in process argv (macosVmService.ts:1430)
openExternalVncClient constructs vnc://:${encodeURIComponent(connection.password)}@host:port and passes it to /usr/bin/open. Unlike the SSH path (which uses sshpass -e + SSHPASS env var specifically to avoid argv exposure), the VNC password is visible in ps aux during the brief window the process runs. This is an existing pattern in the codebase, but it's inconsistent with the SSH hardening.

6. rsync called by name, not absolute path (macosVmService.ts:1902)
All other shell utilities in this file use hardcoded absolute paths (/usr/bin/ssh, /usr/bin/curl, /usr/bin/open, /usr/bin/osascript, etc.). rsyncMirror calls runHostCommand("rsync", ...) via bare name. This relies on PATH resolution, whereas /usr/bin/rsync exists on every standard macOS install.


🟡 Design / Maintainability

7. MacVmPage.tsx is 1540 lines, macosVmService.ts is 3460 lines
Both files are well-organized internally but very large. MacVmPage.tsx embeds ~10 sub-components (SetupPreflightDialog, LumeMissingNotice, ManualCleanupBanner, MessageBanner, ConsoleArea, DownloadDetail, etc.) that are only used by the parent. Splitting them into dedicated files under components/vm/ would improve reviewability and testability. Similarly, macosVmService.ts could benefit from extracting the display proxy, mirror-sync, and AppleScript sub-systems into their own modules.

8. Module-level mutable state in laneLaunchContext.ts (laneLaunchContext.ts:155, 238)
macosVmLaunchProvider and vmLaunchContextCache are module-level singletons. There's no exported clearVmLaunchCache(), so test isolation requires re-importing the module or carefully calling setMacosVmLaunchProvider(null). The TTL (5 s) mitigates stale cache in production, but test bleed is a real risk given the test file size for this service.

9. window.confirm() for destructive operations (MacVmPage.tsx:640, 657, 688)
restartVm, wipeVm, and forceStopVm all use window.confirm(). This is a synchronous browser native dialog that blocks the renderer thread and is stylistically inconsistent with the rest of the app (which uses inline confirmation flows or custom modals). The test suite already stubs window.confirm, so at minimum it's testable, but it's worth replacing with a custom confirmation dialog for UX consistency.

10. Production gate is UI-only (MacVmPage.tsx:236-289)
MacVmProductionGate hides the tab content in packaged builds, but all 20+ macOS VM IPC handlers are still registered in registerIpc.ts regardless of packaging state. This expands the IPC attack surface in production builds where the feature is not intended to be reachable.

11. Hardcoded IPSW URL (macosVmService.ts:79)
DEFAULT_CREATE_IPSW points to a specific macOS 15.6.1 restore image URL. This will silently fail as Apple rotates/CDN-removes old IPSW URLs. A mechanism to auto-detect the latest IPSW URL (e.g. via softwareupdate --list-full-installers or a catalog lookup) would be needed for this to remain functional across macOS releases.


🟢 Positives

  • Username input is validated with assertSafeUsername in both the credentials store and runtime bootstrap, preventing SSH user@host injection.
  • VNC passwords are sanitized out of log output via sanitizeLumeRunText.
  • The MacosVmRecord type properly separates MacosVmStoredCredentialsSummary (renderer-safe) from StoredCredentials (password never leaves main process).
  • Display proxy uses a random 32-hex token per session and binds to 127.0.0.1 only.
  • credentialsStore correctly uses security -w (password flag) to avoid the payload appearing in argv.
  • The runtimeBootstrap.ts SSH path passes the password via SSHPASS env var rather than in argv.
  • Test coverage for MacVmPage, macosVmService, and laneLaunchContext is substantial.
  • No tests for credentialsStore.ts or runtimeBootstrap.ts — these shell-out paths are good candidates for unit tests with a mocked runner.

Copilot finished work on behalf of arul28 May 20, 2026 19:28
Comment thread apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts
Comment thread apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Nitpick comments (9)
apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx (1)

110-124: 💤 Low value

Consider checking all VM-related elements in both tests for consistency.

The test at lines 93-108 verifies the absence of three elements (work-vm-banner, work-vm-not-ready, and "Open VM tab" text), while this test only checks two. For symmetry and completeness, consider either checking all three in both tests or adding a comment explaining why the "Open VM tab" assertion is omitted for local lanes.

♻️ Suggested enhancement
   expect(await screen.findByTestId("agent-chat-pane")).toBeTruthy();
   expect(screen.queryByTestId("work-vm-banner")).toBeNull();
   expect(screen.queryByTestId("work-vm-not-ready")).toBeNull();
+  expect(screen.queryByText("Open VM tab")).toBeNull();
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx`
around lines 110 - 124, The local-lane test "does not render the VM banner for a
local lane" is missing the third VM-related assertion for symmetry with the
other test; update the WorkStartSurface test to also assert that the "Open VM
tab" text is not present (i.e., check that screen.queryByText("Open VM tab") is
null) or, if there's a reason to omit it, add a brief inline comment in that
test explaining why the "Open VM tab" assertion is intentionally skipped so the
test expectations remain explicit and consistent with the other VM-related
assertions ("work-vm-banner" and "work-vm-not-ready").
apps/desktop/src/renderer/types/novnc.d.ts (1)

1-24: 💤 Low value

Consider expanding the type definitions if additional noVNC features are used.

The current ambient declarations cover basic RFB construction and configuration. If your codebase uses additional noVNC features (keyboard events, clipboard, connection events like 'connect'/'disconnect', or methods like sendKey/sendCtrlAltDel), consider adding those to the type definitions for better type safety.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/types/novnc.d.ts` around lines 1 - 24, Add missing
noVNC RFB members and event typings to the ambient declaration so TypeScript
covers additional features your code uses: extend the exported class RFB
(constructor, viewOnly, etc.) with methods like sendKey, sendCtrlAltDel,
grabKeyboard, ungrabKeyboard, clipboardPasteFrom, pasteString/clipboardCopy
methods you use, and add strongly typed event names (e.g., 'connect',
'disconnect', 'clipboard', 'bell', 'clipboard', 'securityfailure',
'credentialsrequired') by augmenting the EventTarget typing or adding
addEventListener/removeEventListener overloads for those string literals; also
expand RfbOptions/RfbCredentials if you rely on extra fields. Update the
declaration for class RFB and the RfbOptions/RfbCredentials types accordingly so
callers of RFB.sendCredentials, RFB.sendKey, and event handlers are properly
typed.
apps/desktop/src/main/services/lanes/laneService.ts (2)

4247-4309: ⚡ Quick win

attachLaneToVm does not wrap the initial placement flip in a transaction.

Unlike detachVmLane, attachLaneToVm directly runs the update lanes statement without a transaction. If linkLaneToCurrentVm fails, the rollback UPDATE is issued, but a crash between the initial UPDATE and the rollback would leave the lane in an inconsistent macos-vm state without proper VM linkage.

Wrapping the initial update in a transaction (committed only after a successful link) would make the operation more robust, though the current rollback approach is acceptable for non-crash scenarios.

♻️ Wrap attachment in a transaction for atomicity
     async attachLaneToVm(args: { laneId: string }): Promise<void> {
       const laneId = String(args?.laneId ?? "").trim();
       if (!laneId.length) throw new Error("laneId is required to attach a lane to the Mac VM.");
       const row = getLaneRow(laneId);
       if (!row) throw new Error(`Lane not found: ${laneId}`);
       const previous = normalizeRuntimePlacement(row.runtime_placement);
       if (previous === "macos-vm") return;

+      db.run("begin immediate");
       db.run(
         `
           update lanes
           set runtime_placement = 'macos-vm'
           where id = ?
             and project_id = ?
         `,
         [row.id, projectId],
       );
-      invalidateLaneListCache();

       const hooks = activeMacosVmHooks;
       if (hooks?.linkLaneToCurrentVm) {
         try {
           await hooks.linkLaneToCurrentVm({ laneId: row.id });
         } catch (error) {
           logger.warn("laneService.attach_link_to_vm_failed_rolling_back", {
             laneId: row.id,
             error: error instanceof Error ? error.message : String(error),
           });
-          db.run(
-            `
-              update lanes
-              set runtime_placement = 'local'
-              where id = ?
-                and project_id = ?
-            `,
-            [row.id, projectId],
-          );
-          invalidateLaneListCache();
+          try { db.run("rollback"); } catch { /* swallow */ }
           throw error;
         }
       }
+      db.run("commit");
+      invalidateLaneListCache();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/lanes/laneService.ts` around lines 4247 -
4309, attachLaneToVm currently updates runtime_placement before linking to the
VM, leaving a crash window; wrap the initial placement flip in a DB transaction
so the UPDATE to set runtime_placement = 'macos-vm' is only committed after
hooks.linkLaneToCurrentVm succeeds. Concretely: in attachLaneToVm begin a
transaction, perform the UPDATE there (instead of the standalone db.run), call
hooks.linkLaneToCurrentVm while still in the transaction, commit the transaction
on success and then run invalidateLaneListCache and emitPlacementChanged; on
hook failure rollback the transaction (so no separate rollback UPDATE is needed)
and then run invalidateLaneListCache and rethrow the error; call
hooks.startMirrorSyncForLane after the commit. Reference: attachLaneToVm,
linkLaneToCurrentVm, startMirrorSyncForLane (and mirror detachVmLane behavior
for guidance).

4142-4240: 💤 Low value

Potential issue: detachVmLane does not acquire a transaction lock before flipping placement.

The detachVmLane method uses begin/commit for the DB update but does not use begin immediate. This could allow concurrent reads to see partial state in high-contention scenarios, though SQLite's default isolation should make this rare. For consistency with other critical updates in this file (e.g., database_cleanup step in delete), consider using begin immediate.

However, since the operation is idempotent and the window is small, this is a minor concern.

♻️ Optional: Use `begin immediate` for consistency
-      db.run("begin");
+      db.run("begin immediate");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/lanes/laneService.ts` around lines 4142 -
4240, The DB update in detachVmLane uses db.run("begin") which can allow
concurrent readers; change it to acquire a write lock by using db.run("begin
immediate") before updating runtime_placement in detachVmLane (and keep the
existing commit/rollback handling and invalidateLaneListCache flow), so the
sequence surrounding the update of row.id/projectId is executed inside an
immediate transaction to prevent concurrency races.
apps/desktop/src/renderer/components/vm/MacVmComingSoon.tsx (1)

6-10: ⚡ Quick win

Rename PREVIEW_ITEMS to camelCase to match the repo rule.

This constant is newly introduced and violates the TS/JS naming guideline.

♻️ Proposed rename
-const PREVIEW_ITEMS: ReadonlyArray<{
+const previewItems: ReadonlyArray<{
   icon: Icon;
   title: string;
   body: string;
 }> = [
@@
-              {PREVIEW_ITEMS.map(({ icon: Icon, title, body }) => (
+              {previewItems.map(({ icon: Icon, title, body }) => (
As per coding guidelines `**/*.{ts,tsx,js,jsx}`: Use camelCase for variable names in TypeScript/JavaScript files.

Also applies to: 76-76

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/components/vm/MacVmComingSoon.tsx` around lines 6 -
10, Rename the exported constant PREVIEW_ITEMS to camelCase (e.g., previewItems)
throughout the file; update the declaration of PREVIEW_ITEMS to previewItems and
update every usage/reference (including any JSX map or imports within
MacVmComingSoon.tsx) to the new identifier to satisfy the TS/JS naming guideline
and ensure all references (e.g., map calls, prop passes) compile.
apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts (1)

10-10: ⚡ Quick win

Use camelCase for the storage-key constant.

This new constant name conflicts with the TS/JS variable naming rule in this repo.

♻️ Proposed rename
-export const MACOS_VM_RUNTIME_AUTH_STORAGE_KEY = "ade.vm.runtimeAuthConfirmed.v1";
+export const macosVmRuntimeAuthStorageKey = "ade.vm.runtimeAuthConfirmed.v1";
@@
-    return window.localStorage.getItem(MACOS_VM_RUNTIME_AUTH_STORAGE_KEY) === "1";
+    return window.localStorage.getItem(macosVmRuntimeAuthStorageKey) === "1";
@@
-    if (value) window.localStorage.setItem(MACOS_VM_RUNTIME_AUTH_STORAGE_KEY, "1");
-    else window.localStorage.removeItem(MACOS_VM_RUNTIME_AUTH_STORAGE_KEY);
+    if (value) window.localStorage.setItem(macosVmRuntimeAuthStorageKey, "1");
+    else window.localStorage.removeItem(macosVmRuntimeAuthStorageKey);
As per coding guidelines `**/*.{ts,tsx,js,jsx}`: Use camelCase for variable names in TypeScript/JavaScript files.

Also applies to: 14-14, 22-23

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts` at line 10, The
constant MACOS_VM_RUNTIME_AUTH_STORAGE_KEY should be renamed to camelCase (e.g.,
macosVmRuntimeAuthStorageKey) to comply with the repo's TS/JS naming rules;
update its declaration in macosVmRuntimeReadiness.ts and replace all
references/usages (imports, local reads/writes, and any tests) to use the new
name, and ensure any export remains consistent (export const
macosVmRuntimeAuthStorageKey = ...). Also update the other related occurrences
referenced in the review (the other constants/uses in the same file) to
camelCase so naming is consistent across the module.
apps/desktop/src/main/main.ts (1)

3402-3413: 💤 Low value

Consider defining an explicit interface for the optional VM hooks.

The broad type assertion as unknown as {...} works but obscures the contract. A dedicated interface (e.g., MacosVmServiceHooks) exported alongside createMacosVmService would make the expected shape explicit and catch mismatches at compile time.

This is a style improvement for maintainability rather than a correctness issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/main.ts` around lines 3402 - 3413, The inline broad
assertion for macosVmSvcAny hides the expected optional VM hook shape; define
and export a dedicated interface (e.g., MacosVmServiceHooks) describing
markShareStale, stopMirrorSyncForLane, startMirrorSyncForLane,
linkLaneToCurrentVm, getStatus, and getCredentials, then replace the ad-hoc cast
with a typed reference (use MacosVmServiceHooks or a combined type for
macosVmService) and update createMacosVmService’s exported types so the compiler
can check the contract instead of using as unknown as {...}.
apps/desktop/src/main/services/adeActions/registry.ts (1)

783-783: ⚡ Quick win

Format the action array across multiple lines for consistency.

The macos_vm allowlist contains 20 actions on a single line, which reduces readability and makes diff reviews difficult. Other long allowlists in this file (e.g., lane, git, conflicts) use multi-line formatting.

📝 Suggested reformatting
-  macos_vm: ["getStatus", "getStorageInfo", "provision", "start", "stop", "restart", "delete", "wipe", "installRuntime", "setCredentials", "getCredentials", "detachLane", "getAgentGuide", "getSharePolicy", "focusWindow", "getDisplaySession", "captureScreenshot", "click", "selectPoint", "typeText"],
+  macos_vm: [
+    "captureScreenshot",
+    "click",
+    "delete",
+    "detachLane",
+    "getAgentGuide",
+    "getCredentials",
+    "getDisplaySession",
+    "getSharePolicy",
+    "getStatus",
+    "getStorageInfo",
+    "focusWindow",
+    "installRuntime",
+    "provision",
+    "restart",
+    "selectPoint",
+    "setCredentials",
+    "start",
+    "stop",
+    "typeText",
+    "wipe",
+  ],

Note: Actions are sorted alphabetically in the suggestion above for easier scanning and maintenance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/adeActions/registry.ts` at line 783, The
macos_vm allowlist in registry.ts is currently a single long line; split the
array for the macos_vm key into multiple lines (one action per line or grouped
sensibly) to match the multi-line style used by other allowlists like lane, git,
and conflicts; while you’re editing, alphabetize the action strings for easier
scanning and ensure you update the macos_vm entry name exactly as spelled so the
rest of the registry (and any references to macos_vm) continue to work.
apps/desktop/src/main/services/ipc/ipcTimeouts.ts (1)

7-22: ⚡ Quick win

Use camelCase for the runtime action map variable.

Rename RUNTIME_ACTION_CHANNEL to runtimeActionChannel to match repository naming rules for TypeScript variables.

As per coding guidelines, **/*.{ts,tsx,js,jsx}: Use camelCase for variable names in TypeScript/JavaScript files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/main/services/ipc/ipcTimeouts.ts` around lines 7 - 22,
Rename the constant RUNTIME_ACTION_CHANNEL to camelCase runtimeActionChannel
everywhere it is defined and referenced; update the declaration const
RUNTIME_ACTION_CHANNEL: Record<string, Record<string, string>> = { ... } to
const runtimeActionChannel: Record<string, Record<string, string>> = { ... } and
adjust any imports/uses that reference RUNTIME_ACTION_CHANNEL (e.g., in code
that reads IPC mappings) to use runtimeActionChannel instead so tests/consumers
compile.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/ade-cli/src/cli.ts`:
- Around line 6790-6804: readVmTarget currently only reads the lane from flags
via readValue(args, ["--lane", "--lane-id"]) and ignores a positional lane
argument, causing commands like "ade macos-vm wipe <lane>" to drop the lane;
update readVmTarget to also consume the positional lane when laneId is undefined
(e.g. check args._ or call the existing positional reader used by sibling macOS
VM commands) so laneId is populated from a positional argument before throwing
or returning; modify readVmTarget (and use readVmName/readValue) to prefer
explicit flags but fall back to the positional lane value.

In `@apps/desktop/src/main/services/adeActions/registry.ts`:
- Line 783: The macos_vm allowlist includes an unimplemented action "detachLane"
which will cause runtime failures; remove "detachLane" from the macos_vm array
in the registry (the array containing "getStatus", "getStorageInfo", ...
"typeText") so only methods actually exported by createMacosVmService() are
allowed; verify the registry's macos_vm entry matches the methods implemented by
createMacosVmService() and update any tests or callers accordingly.

In `@apps/desktop/src/main/services/ipc/registerIpc.ts`:
- Around line 2347-2355: resolveMacosVmProjectRootForEvent currently falls back
to getCtx().project?.rootPath without checking whether the user has actually
selected a project; change the fallback so you only return
getCtx().project.rootPath when getCtx().hasUserSelectedProject is true
(otherwise throw the same error). Locate the function
resolveMacosVmProjectRootForEvent and its use of getCtx(), add a guard that
checks getCtx().hasUserSelectedProject before reading getCtx().project.rootPath,
and keep the existing error message when the guard fails so the function fails
closed instead of returning another project's path.
- Around line 7684-7756: The handlers currently cast ensureMacosVm() and
getCtx().laneService to broad Record types which loses compile-time checking;
instead declare a narrow interface (e.g., MacosVmExtension with optional methods
restart, wipe, installRuntime, setCredentials, getCredentials, getStorageInfo)
and cast ensureMacosVm() to that interface in callMacosVmExtension so TypeScript
can verify signatures, then keep the existing runtime typeof checks before
invoking svc.restart(args) etc; likewise define a small LaneService interface
with optional detachVmLane and cast getCtx().laneService to it before checking
and calling detachVmLane inside the IPC.macosVmDetachLane handler.

In `@apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts`:
- Around line 217-219: The temporary bootstrap script and directory (tempDir and
localScriptPath created with fs.mkdtempSync and GUEST_BOOTSTRAP_SCRIPT) are
never removed; wrap the install/bootstrap flow (the block that writes
localScriptPath and the subsequent install/exec logic in the runtimeBootstrap
routine) in a try/finally so that in finally you remove localScriptPath and
remove tempDir if it was created by this function (i.e., only delete tempDir
when args.tempDir was not supplied). Use fs.unlinkSync/fs.rmSync for the file
and fs.rmdirSync or fs.rmSync for the directory, and swallow non-fatal ENOENT
errors so cleanup is best-effort; apply the same cleanup around the code paths
handling lines ~221-274 to ensure no leaks after successful or failed install
attempts.
- Around line 23-24: Hardcoded SSHPASS_BINARY path breaks on systems where
sshpass is installed elsewhere; add a resolver (e.g., resolveSshpassPath) in
runtimeBootstrap.ts that checks an ordered list of candidate locations (e.g.,
"/opt/homebrew/bin/sshpass", "/usr/local/bin/sshpass", common MacPorts/source
locations) and falls back to a PATH lookup (which/where or
child_process.execSync('which sshpass')) and return undefined or null if not
found, then replace the SSHPASS_BINARY constant with the resolver result and
update usages that reference SSHPASS_BINARY (the calls around the previous line
156 and 177) to handle a missing sshpass gracefully (error or alternate auth
flow); keep SCP_BINARY as-is or optionally resolve similarly.

In `@apps/desktop/src/preload/preload.ts`:
- Around line 5995-5998: getDisplaySession and the other macOS VM IPC methods
are currently invoking ipcRenderer.invoke(...) directly and must be routed
through the remote-runtime-first dispatcher; update getDisplaySession (and the
similar methods handling
restart/wipe/runtime/credentials/detach/storage/display) to call
callRemoteProjectRuntimeActionOr(...) (the same wrapper used elsewhere in
preload) so the request is resolved against the remote "macos_vm" target when
appropriate, passing the original IPC channel (e.g.
IPC.macosVmGetDisplaySession) and args through that helper and returning its
result.

In `@apps/desktop/src/renderer/components/app/App.tsx`:
- Around line 331-336: serializeProjectRoute currently omits the new "/vm" route
from its allowlist, so project-route persistence can drop users back to "/work";
update the allowlist used by serializeProjectRoute to include "/vm" (and any
equivalent route alias such as "/macos-vm" if you normalize there), and ensure
the corresponding deserialization logic (e.g., deserializeProjectRoute or route
deserializer) accepts/restores "/vm" so switching project tabs preserves the VM
route.

In `@apps/desktop/src/renderer/components/lanes/LanesPage.tsx`:
- Around line 2009-2019: prepareCreateDialog currently preselects "macos-vm"
even when readMacosVmRuntimeAuthConfirmed() is false, allowing downstream
VM-status fetches to bypass the auth-confirm gate; change the logic inside
prepareCreateDialog (and the other dialog-preparation handlers that preselect
macos-vm) to first read authConfirmed = readMacosVmRuntimeAuthConfirmed(), call
setCreateVmRuntimeAuthConfirmed(authConfirmed), and only call
setCreateRuntimePlacement(runtimePlacement) with the normalized "macos-vm" value
if authConfirmed is true—otherwise setCreateRuntimePlacement to a safe default
("primary") and avoid kicking off any VM-status loading/fetch (leave
setCreateVmStatusLoading false and setCreateVmRuntimeAvailable false). Ensure
the same guard is applied in the other preparer functions mentioned so
deep-links and dialog-bus opens cannot preselect macos-vm without confirmation.
- Around line 3961-3965: The onOpenVmLaneInWork callback is routing to "/work"
but the component elsewhere opens the Work page via "/project" (see onOpenVmTab
and the earlier usage at line ~3884); update the callback so after calling
selectLane(laneId) it navigates to the same Work route used elsewhere (replace
navigate("/work") with navigate("/project")) to ensure the VM CTA lands on the
correct page.

In `@apps/desktop/src/renderer/components/vm/FirstBootCard.tsx`:
- Around line 120-122: The code dereferences currentStep (used as
currentStep.number/title) without guarding an empty steps array: compute
safeIndex using Math.min/Math.max can yield -1 when steps.length === 0; update
the logic in FirstBootCard to early-handle an empty steps array (e.g., if
(steps.length === 0) return null or render an empty/placeholder state) or set
currentStep to null and guard all uses, and adjust canAdvance to check
steps.length > 0 before comparing (for example use const canAdvance =
steps.length > 0 && safeIndex < steps.length - 1); ensure all references to
currentStep.number/title are only accessed when currentStep is not null.

In `@apps/desktop/src/renderer/components/vm/MacVmPage.tsx`:
- Around line 1342-1344: The generated shell command `command` is unsafe because
wrapping paths in double quotes doesn't protect against shell metacharacters
like $(...) — update the construction used in MacVmPage (symbols: command,
paths, copy) to perform proper shell escaping: for each path, wrap it in single
quotes and escape any embedded single quotes by replacing each ' with the
three-token sequence that closes, escapes, and reopens the single-quoted string
(i.e. '\''), then join those safely-quoted path tokens with spaces and use that
safe string in the copy callback; keep the rest of the logic
(navigator.clipboard.writeText in copy) unchanged.

In `@apps/desktop/src/renderer/components/vm/PhaseStepper.tsx`:
- Around line 16-19: The type annotation React.CSSProperties will fail because
only ReactNode is imported; import CSSProperties from React (add CSSProperties
to the existing import) and change the baseStyle declaration to use
CSSProperties (e.g., const baseStyle: CSSProperties = { ... }); update any other
occurrences of React.CSSProperties in this file to CSSProperties and keep the
original baseStyle symbol and property names intact.

In `@apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts`:
- Around line 139-140: Validate vm.currentPhase before returning it: ensure it's
a number within the bounds of MACOS_VM_PHASES (e.g., 0 <= vm.currentPhase <
MACOS_VM_PHASES.length) and not NaN; if it fails validation, fall back to the
existing computed state (readiness?.state ?? vm.guestReadiness?.state ??
"not_created") or convert that state into a safe phase index. Update the logic
around vm.currentPhase usage in macosVmRuntimeReadiness (where vm.currentPhase
is checked) and any later indexing into MACOS_VM_PHASES (also referenced around
lines 169-175) to perform the same guard so lookups cannot receive an
out-of-range or invalid index.

In `@apps/desktop/src/shared/types/macosVm.ts`:
- Around line 410-411: Change the property type for the public contract from a
boolean to the literal true by replacing "confirm: boolean" with "confirm: true"
so callers must pass true at compile time; update the type declaration that
contains the confirm field (the 'confirm' property shown in macosVm types) and
leave or add runtime validation where the wipe operation is invoked to guard
against accidental use.

---

Nitpick comments:
In `@apps/desktop/src/main/main.ts`:
- Around line 3402-3413: The inline broad assertion for macosVmSvcAny hides the
expected optional VM hook shape; define and export a dedicated interface (e.g.,
MacosVmServiceHooks) describing markShareStale, stopMirrorSyncForLane,
startMirrorSyncForLane, linkLaneToCurrentVm, getStatus, and getCredentials, then
replace the ad-hoc cast with a typed reference (use MacosVmServiceHooks or a
combined type for macosVmService) and update createMacosVmService’s exported
types so the compiler can check the contract instead of using as unknown as
{...}.

In `@apps/desktop/src/main/services/adeActions/registry.ts`:
- Line 783: The macos_vm allowlist in registry.ts is currently a single long
line; split the array for the macos_vm key into multiple lines (one action per
line or grouped sensibly) to match the multi-line style used by other allowlists
like lane, git, and conflicts; while you’re editing, alphabetize the action
strings for easier scanning and ensure you update the macos_vm entry name
exactly as spelled so the rest of the registry (and any references to macos_vm)
continue to work.

In `@apps/desktop/src/main/services/ipc/ipcTimeouts.ts`:
- Around line 7-22: Rename the constant RUNTIME_ACTION_CHANNEL to camelCase
runtimeActionChannel everywhere it is defined and referenced; update the
declaration const RUNTIME_ACTION_CHANNEL: Record<string, Record<string, string>>
= { ... } to const runtimeActionChannel: Record<string, Record<string, string>>
= { ... } and adjust any imports/uses that reference RUNTIME_ACTION_CHANNEL
(e.g., in code that reads IPC mappings) to use runtimeActionChannel instead so
tests/consumers compile.

In `@apps/desktop/src/main/services/lanes/laneService.ts`:
- Around line 4247-4309: attachLaneToVm currently updates runtime_placement
before linking to the VM, leaving a crash window; wrap the initial placement
flip in a DB transaction so the UPDATE to set runtime_placement = 'macos-vm' is
only committed after hooks.linkLaneToCurrentVm succeeds. Concretely: in
attachLaneToVm begin a transaction, perform the UPDATE there (instead of the
standalone db.run), call hooks.linkLaneToCurrentVm while still in the
transaction, commit the transaction on success and then run
invalidateLaneListCache and emitPlacementChanged; on hook failure rollback the
transaction (so no separate rollback UPDATE is needed) and then run
invalidateLaneListCache and rethrow the error; call hooks.startMirrorSyncForLane
after the commit. Reference: attachLaneToVm, linkLaneToCurrentVm,
startMirrorSyncForLane (and mirror detachVmLane behavior for guidance).
- Around line 4142-4240: The DB update in detachVmLane uses db.run("begin")
which can allow concurrent readers; change it to acquire a write lock by using
db.run("begin immediate") before updating runtime_placement in detachVmLane (and
keep the existing commit/rollback handling and invalidateLaneListCache flow), so
the sequence surrounding the update of row.id/projectId is executed inside an
immediate transaction to prevent concurrency races.

In `@apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx`:
- Around line 110-124: The local-lane test "does not render the VM banner for a
local lane" is missing the third VM-related assertion for symmetry with the
other test; update the WorkStartSurface test to also assert that the "Open VM
tab" text is not present (i.e., check that screen.queryByText("Open VM tab") is
null) or, if there's a reason to omit it, add a brief inline comment in that
test explaining why the "Open VM tab" assertion is intentionally skipped so the
test expectations remain explicit and consistent with the other VM-related
assertions ("work-vm-banner" and "work-vm-not-ready").

In `@apps/desktop/src/renderer/components/vm/MacVmComingSoon.tsx`:
- Around line 6-10: Rename the exported constant PREVIEW_ITEMS to camelCase
(e.g., previewItems) throughout the file; update the declaration of
PREVIEW_ITEMS to previewItems and update every usage/reference (including any
JSX map or imports within MacVmComingSoon.tsx) to the new identifier to satisfy
the TS/JS naming guideline and ensure all references (e.g., map calls, prop
passes) compile.

In `@apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts`:
- Line 10: The constant MACOS_VM_RUNTIME_AUTH_STORAGE_KEY should be renamed to
camelCase (e.g., macosVmRuntimeAuthStorageKey) to comply with the repo's TS/JS
naming rules; update its declaration in macosVmRuntimeReadiness.ts and replace
all references/usages (imports, local reads/writes, and any tests) to use the
new name, and ensure any export remains consistent (export const
macosVmRuntimeAuthStorageKey = ...). Also update the other related occurrences
referenced in the review (the other constants/uses in the same file) to
camelCase so naming is consistent across the module.

In `@apps/desktop/src/renderer/types/novnc.d.ts`:
- Around line 1-24: Add missing noVNC RFB members and event typings to the
ambient declaration so TypeScript covers additional features your code uses:
extend the exported class RFB (constructor, viewOnly, etc.) with methods like
sendKey, sendCtrlAltDel, grabKeyboard, ungrabKeyboard, clipboardPasteFrom,
pasteString/clipboardCopy methods you use, and add strongly typed event names
(e.g., 'connect', 'disconnect', 'clipboard', 'bell', 'clipboard',
'securityfailure', 'credentialsrequired') by augmenting the EventTarget typing
or adding addEventListener/removeEventListener overloads for those string
literals; also expand RfbOptions/RfbCredentials if you rely on extra fields.
Update the declaration for class RFB and the RfbOptions/RfbCredentials types
accordingly so callers of RFB.sendCredentials, RFB.sendKey, and event handlers
are properly typed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7af9bed0-317f-4357-83de-ee60220f5572

📥 Commits

Reviewing files that changed from the base of the PR and between 999ac55 and 50b7259.

⛔ Files ignored due to path filters (9)
  • apps/desktop/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • docs/ARCHITECTURE.md is excluded by !docs/**
  • docs/features/ade-code/README.md is excluded by !docs/**
  • docs/features/computer-use/README.md is excluded by !docs/**
  • docs/features/lanes/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/README.md is excluded by !docs/**
  • docs/features/terminals-and-sessions/ui-surfaces.md is excluded by !docs/**
  • docs/perf/macos-vm-tab-action-inventory.md is excluded by !docs/**
  • docs/perf/work-tab-action-inventory.md is excluded by !docs/**
📒 Files selected for processing (68)
  • .agents/skills/plan/SKILL.md
  • .agents/skills/source-command-audit/SKILL.md
  • apps/ade-cli/README.md
  • apps/ade-cli/src/cli.ts
  • apps/ade-cli/src/tuiClient/__tests__/Drawer.test.tsx
  • apps/ade-cli/src/tuiClient/components/Drawer.tsx
  • apps/desktop/package.json
  • apps/desktop/resources/agent-skills/ade-macos-vm/SKILL.md
  • apps/desktop/src/main/main.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/chat/agentChatService.ts
  • apps/desktop/src/main/services/ipc/ipcTimeouts.test.ts
  • apps/desktop/src/main/services/ipc/ipcTimeouts.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/ipc/runtimeBridge.test.ts
  • apps/desktop/src/main/services/lanes/laneLaunchContext.test.ts
  • apps/desktop/src/main/services/lanes/laneLaunchContext.ts
  • apps/desktop/src/main/services/lanes/laneService.test.ts
  • apps/desktop/src/main/services/lanes/laneService.ts
  • apps/desktop/src/main/services/macosVm/credentialsStore.ts
  • apps/desktop/src/main/services/macosVm/macosVmRecovery.ts
  • apps/desktop/src/main/services/macosVm/macosVmService.test.ts
  • apps/desktop/src/main/services/macosVm/macosVmService.ts
  • apps/desktop/src/main/services/macosVm/rfbDirectClient.ts
  • apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts
  • apps/desktop/src/main/services/remoteRuntime/remoteConnectionPool.ts
  • apps/desktop/src/main/services/state/kvDb.ts
  • apps/desktop/src/preload/global.d.ts
  • apps/desktop/src/preload/preload.test.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/browserMock.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/app/AppShell.tsx
  • apps/desktop/src/renderer/components/app/TabNav.tsx
  • apps/desktop/src/renderer/components/chat/AgentChatPane.submit.test.tsx
  • apps/desktop/src/renderer/components/lanes/CreateLaneDialog.test.tsx
  • apps/desktop/src/renderer/components/lanes/CreateLaneDialog.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.test.ts
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.tsx
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkStartSurface.tsx
  • apps/desktop/src/renderer/components/terminals/WorkViewArea.tsx
  • apps/desktop/src/renderer/components/ui/TabBackground.tsx
  • apps/desktop/src/renderer/components/vm/CredentialsPromptDialog.tsx
  • apps/desktop/src/renderer/components/vm/CurrentVmLaneRow.tsx
  • apps/desktop/src/renderer/components/vm/FirstBootCard.tsx
  • apps/desktop/src/renderer/components/vm/MacMiniGlyph.tsx
  • apps/desktop/src/renderer/components/vm/MacVmComingSoon.tsx
  • apps/desktop/src/renderer/components/vm/MacVmPage.test.tsx
  • apps/desktop/src/renderer/components/vm/MacVmPage.tsx
  • apps/desktop/src/renderer/components/vm/PhaseStepper.tsx
  • apps/desktop/src/renderer/components/vm/VmLifecycleMenu.tsx
  • apps/desktop/src/renderer/index.css
  • apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts
  • apps/desktop/src/renderer/lib/visualContextFormatting.ts
  • apps/desktop/src/renderer/lib/workPtyContextEvents.ts
  • apps/desktop/src/renderer/state/appStore.test.ts
  • apps/desktop/src/renderer/state/appStore.ts
  • apps/desktop/src/renderer/types/novnc.d.ts
  • apps/desktop/src/shared/adeCliGuidance.ts
  • apps/desktop/src/shared/ipc.ts
  • apps/desktop/src/shared/types/lanes.ts
  • apps/desktop/src/shared/types/macosVm.ts
  • apps/ios/ADE/Resources/DatabaseBootstrap.sql
💤 Files with no reviewable changes (4)
  • apps/desktop/src/renderer/lib/workPtyContextEvents.ts
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.tsx
  • apps/desktop/src/renderer/components/terminals/MacosVmPanel.test.tsx
  • apps/desktop/src/renderer/components/terminals/WorkSidebar.tsx

Comment thread apps/ade-cli/src/cli.ts
Comment thread apps/desktop/src/main/services/adeActions/registry.ts Outdated
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Comment thread apps/desktop/src/main/services/ipc/registerIpc.ts
Comment thread apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts Outdated
Comment thread apps/desktop/src/renderer/components/vm/FirstBootCard.tsx
Comment thread apps/desktop/src/renderer/components/vm/MacVmPage.tsx Outdated
Comment thread apps/desktop/src/renderer/components/vm/PhaseStepper.tsx Outdated
Comment thread apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts Outdated
Comment thread apps/desktop/src/shared/types/macosVm.ts Outdated
- runtimeBootstrap stub: mark install state failed instead of advancing to runtime_ready
- runtimeBootstrap: resolveSshpassBinary() walks Homebrew/MacPorts/PATH
- runtimeBootstrap: try/finally cleans staged install scripts
- credentialsStore: SIGTERM → SIGKILL 500ms; settle promise on timeout
- macosVmService: .part.url sidecar so partial download recovery is URL-keyed
- macosVmService: /usr/bin/rsync absolute path
- registerIpc: hasUserSelectedProject guard on project-root fallback
- registerIpc: requireMacosVmEnabledInProduction gates VM IPC in packaged builds
- registerIpc: typed MacosVmExtensionService interface (removed unknown casts)
- adeActions/registry: drop detachLane (it lives on laneService, not macosVmService)
- preload: route new VM IPC through callRemoteProjectRuntimeActionOr
- App: /vm joins serializeProjectRoute allowedRoots
- LanesPage: auth-confirm gate on status fetch + createVmRuntimeAvailable
- LanesPage: fresh submit recheck mirrors full availability predicate
- LanesPage: open-in-Work CTA navigates to /project
- ade-cli: readVmTarget consumes positional lane arg
- FirstBootCard: empty-steps guard
- MacVmPage: shellSingleQuote() for safe sudo rm -rf cleanup string
- PhaseStepper: import CSSProperties directly
- macosVmRuntimeReadiness: validate vm.currentPhase bounds
- shared/types/macosVm: confirm: true literal on wipe args

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Comment thread apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts
Address Greptile P1: defaultRunner's per-phase timeout sent SIGTERM but
waited unconditionally for the exit event. If SSH/scp blocked in a kernel
wait, SIGTERM was ignored and the promise never settled. Mirror the
credentialsStore pattern: settle promise on timeout, schedule a SIGKILL
500ms after SIGTERM.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/ade-cli/src/cli.ts (1)

6798-6799: ⚡ Quick win

Reuse readVmLaneId(false) here to keep VM target parsing in one place.

This reimplements the lane parser that already exists in readVmLaneId, which is the same kind of split that caused the positional-lane regression before. Reusing the helper here lowers the chance these code paths drift again.

♻️ Proposed fix
-    const laneId =
-      readValue(args, ["--lane", "--lane-id"]) ?? firstPositional(args);
+    const laneId = readVmLaneId(false);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ade-cli/src/cli.ts` around lines 6798 - 6799, Replace the ad-hoc lane
parsing that sets const laneId = readValue(args, ["--lane", "--lane-id"]) ??
firstPositional(args); with a call to the existing helper readVmLaneId(false) so
VM target parsing is centralized; locate the code that declares laneId in this
block and use readVmLaneId(false) to obtain the lane id instead of
readValue/firstPositional (keeping the variable name laneId and the same
downstream usage).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/main/services/adeActions/registry.ts`:
- Around line 783-786: The macos_vm allowlist currently exposes sensitive
actions setCredentials and getDisplaySession to any caller; move these actions
behind the CTO-only gate used by run_ade_action (ADE_ACTION_CTO_ONLY) or remove
them from the generic macos_vm allowlist so only callers validated as CTO can
invoke them. Locate the macos_vm array in registry.ts and either remove
"setCredentials" and "getDisplaySession" from that list and add them to the
CTO-only action set, or annotate/route their invocation to laneService calls
that enforce ADE_ACTION_CTO_ONLY (e.g., ensure run_ade_action checks
ADE_ACTION_CTO_ONLY for those action names) so that only CTO-authorized callers
can access them.

In `@apps/desktop/src/main/services/macosVm/credentialsStore.ts`:
- Around line 88-118: The promise is clearing the SIGKILL fallback timer too
early: in finish() it clears killTimer which prevents the SIGKILL from running
if SIGTERM is ignored. Remove the clearTimeout(killTimer) call from finish (or
only clear the main timeout there), and initialize killTimer so it can be set
later (e.g., let killTimer: ReturnType<typeof setTimeout> | undefined), ensuring
only the main timeout is cleared when resolving; leave killTimer untouched so
the 500ms SIGKILL fallback scheduled in the timeout handler (which calls
child.kill("SIGKILL")) can still run.

In `@apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts`:
- Around line 105-130: The timeout handler arms a SIGKILL fallback via killTimer
then calls finish(), but finish() clears killTimer which prevents the SIGKILL
escalation; change the logic so that finish() does not unconditionally clear
killTimer (or accept a flag like finish(fromTimeout: boolean)) and only clear
killTimer when the child has actually exited normally. Specifically adjust the
functions/variables referenced (killTimer, finish, timeout, child.kill, settled,
stdout/stderr, options.timeoutMs) so the timeout path sets killTimer and calls
finish without cancelling that killTimer, while non-timeout/normal-exit paths
still clear the killTimer to avoid leaks.

---

Nitpick comments:
In `@apps/ade-cli/src/cli.ts`:
- Around line 6798-6799: Replace the ad-hoc lane parsing that sets const laneId
= readValue(args, ["--lane", "--lane-id"]) ?? firstPositional(args); with a call
to the existing helper readVmLaneId(false) so VM target parsing is centralized;
locate the code that declares laneId in this block and use readVmLaneId(false)
to obtain the lane id instead of readValue/firstPositional (keeping the variable
name laneId and the same downstream usage).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 87238303-d471-4307-bcbc-a8e0eadd54de

📥 Commits

Reviewing files that changed from the base of the PR and between 50b7259 and bd53e6f.

📒 Files selected for processing (15)
  • apps/ade-cli/src/cli.ts
  • apps/desktop/src/main/services/adeActions/registry.ts
  • apps/desktop/src/main/services/ipc/registerIpc.ts
  • apps/desktop/src/main/services/macosVm/credentialsStore.ts
  • apps/desktop/src/main/services/macosVm/macosVmService.test.ts
  • apps/desktop/src/main/services/macosVm/macosVmService.ts
  • apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts
  • apps/desktop/src/preload/preload.ts
  • apps/desktop/src/renderer/components/app/App.tsx
  • apps/desktop/src/renderer/components/lanes/LanesPage.tsx
  • apps/desktop/src/renderer/components/vm/FirstBootCard.tsx
  • apps/desktop/src/renderer/components/vm/MacVmPage.tsx
  • apps/desktop/src/renderer/components/vm/PhaseStepper.tsx
  • apps/desktop/src/renderer/lib/macosVmRuntimeReadiness.ts
  • apps/desktop/src/shared/types/macosVm.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/components/vm/PhaseStepper.tsx

Comment thread apps/desktop/src/main/services/adeActions/registry.ts Outdated
Comment thread apps/desktop/src/main/services/macosVm/credentialsStore.ts Outdated
Comment thread apps/desktop/src/main/services/macosVm/runtimeBootstrap.ts Outdated
Comment thread apps/desktop/src/main/services/macosVm/macosVmService.ts Outdated
…ighten action allowlist

- credentialsStore + runtimeBootstrap: prior fix cleared killTimer in finish()
  unconditionally, defeating the 500 ms SIGKILL fallback. Pass clearKill=false
  from the timeout path so the SIGKILL still fires if SIGTERM is ignored.
  (CodeRabbit major, credentialsStore.ts:118 + runtimeBootstrap.ts:130)

- macosVmService.openExternalVncClient: vnc:// URL no longer embeds the
  credential — it appeared in argv to /usr/bin/open (ps aux visible) for the
  brief lifetime of the child. macOS Screen Sharing prompts when no Keychain
  entry exists; matches the SIGSAFE pattern used for the SSH path with sshpass -e.
  (Greptile P1 security, macosVmService.ts:1487)

- adeActions/registry macos_vm allowlist: removed setCredentials and
  getDisplaySession from the generic agent-callable surface. setCredentials
  mutates Keychain-backed VM credentials; getDisplaySession returns the live
  VNC password. Typed CLI / IPC paths (ade macos-vm set-credentials, ade
  macos-vm display-session) still work and run under CTO-level auth.
  (CodeRabbit major, registry.ts:786)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Comment thread apps/desktop/src/main/services/ipc/ipcTimeouts.ts
Greptile P1: registry.ts allowlists macos_vm.restart/wipe/installRuntime
(plus focusWindow/click/selectPoint/typeText) for agent invocation, but the
RUNTIME_ACTION_CHANNEL map only covered provision/start/stop/delete/
captureScreenshot. Agent calls through localRuntimeCallAction /
remoteRuntimeCallAction were falling back to the 30s default — restart
sequences stop+start (up to 122 min), wipe drives deleteVm (~2 min), and
installRuntime SSHes into the guest. All three were racing-lose to 30s.

Map each agent-callable action to its IPC channel so the existing per-channel
budgets apply. Add the matching cases to the switch (restart/installRuntime
share start's 120-min budget; wipe shares delete's 2-min; UI actions share
captureScreenshot's 60s). Extend tests to cover restart / wipe /
installRuntime via the runtime channel and a UI action via the remote runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Comment thread apps/desktop/src/main/services/ipc/ipcTimeouts.ts
Comment thread apps/desktop/src/main/services/lanes/laneLaunchContext.ts
…che TTL

Greptile P1 (ipcTimeouts.ts:54): macosVmWipe was capped at 2 min IPC budget,
but wipe() and deleteVm() both call runLume("delete", 10 * 60_000) internally.
The IPC's Promise.race in main.ts was firing first — renderer saw "timed out"
while the underlying lume process kept running, leaving the store record
unchanged. Lift both wipe and delete IPC budgets to 10 min so the renderer
sees the real outcome. Update test to match.

Greptile P1 (laneLaunchContext.ts:207): VM_LAUNCH_CONTEXT_TTL_MS was 5_000 ms,
but the only refresh path is the placement-changed handler that fires once
when a lane attaches to a VM. agentChatService and ptyService call the sync
resolveLaneLaunchContext on every turn — after 5 s the cache returns null and
throws VmNotReadyError even when the VM is fine. The cached SSH target
(IP + username + vmName) is stable for the running VM lifetime; lifecycle
events explicitly invalidate the cache. Bump TTL to 30 min so the cache stays
warm across normal agent sessions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arul28
Copy link
Copy Markdown
Owner Author

arul28 commented May 20, 2026

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Comment on lines 3378 to +3380
onEvent: (payload) =>
emitProjectEvent(projectRoot, IPC.macosVmEvent, payload),
captureWindowSources: async () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 setCredentials never refreshes the VM launch cache

After a user saves guest credentials via setCredentials, the vmLaunchContextCache is never invalidated. The most likely setup flow is: (1) create VM lane while VM is runtime_ready but credentials haven't been saved yet → refreshVmLaneLaunchCache caches not-ready; (2) user saves credentials; (3) agent turn fires → getCachedVmLaunchContextSync returns not-readyVmNotReadyError thrown for every turn for up to 30 minutes until the TTL expires. invalidateVmLaneLaunchCache(laneId) (or a full refreshVmLaneLaunchCache) needs to be called from the setCredentials IPC path after a successful credential save.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/main/main.ts
Line: 3378-3380

Comment:
**`setCredentials` never refreshes the VM launch cache**

After a user saves guest credentials via `setCredentials`, the `vmLaunchContextCache` is never invalidated. The most likely setup flow is: (1) create VM lane while VM is `runtime_ready` but credentials haven't been saved yet → `refreshVmLaneLaunchCache` caches `not-ready`; (2) user saves credentials; (3) agent turn fires → `getCachedVmLaunchContextSync` returns `not-ready``VmNotReadyError` thrown for every turn for up to 30 minutes until the TTL expires. `invalidateVmLaneLaunchCache(laneId)` (or a full `refreshVmLaneLaunchCache`) needs to be called from the `setCredentials` IPC path after a successful credential save.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

@arul28 arul28 merged commit ae3255a into main May 20, 2026
28 checks passed
@arul28 arul28 deleted the ade/mac-vm-updates-9d9cfd88 branch May 20, 2026 22:37
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.

2 participants