Skip to content

Add Cloud VM default-provider stress check - #3697

Merged
lawrencecchen merged 2 commits into
mainfrom
task-freestyle-cloudvm-ready
May 7, 2026
Merged

lawrencecchen merged 2 commits into
mainfrom
task-freestyle-cloudvm-ready

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add a deployed Cloud VM stress script that creates, attaches, browser-proxy-smokes, and cleans up test VMs/users
  • document Freestyle as the current staging/production default with E2B kept as rollback
  • fix existing web lint blockers so production readiness checks pass

Verification

  • bun run lint
  • bun run cloud-vm:preflight -- --schema-only .
  • bun run test
  • bun run build
  • bun run cloud-vm:stress -- staging --count 8 --concurrency 4 --provider default
  • bun run cloud-vm:stress -- production --count 12 --concurrency 4 --provider default

Live checks


Note

Medium Risk
Adds a new operator-facing stress runner that creates and destroys real VMs/users against staging/production APIs, so misconfiguration or cleanup failures could consume quota or leave resources behind. Other changes are low-risk doc/link/typing fixes.

Overview
Adds a new cloud-vm:stress command that repeatedly provisions Cloud VMs via POST /api/vm, attaches via WebSocket daemon RPC, browser-proxies a /healthz request, then attempts best-effort VM + Stack test-user cleanup and emits a JSON result summary/exit code.

Docs are updated to recommend running this stress check before provider default rollouts/rollbacks and to clarify Freestyle default-provider operational guidance. Separately fixes a couple of production/lint blockers: SSR-safe DevPanel positioning, anchorable docs sections + internal links migrated to Next Link, and stronger bun:test TypeScript declarations.

Reviewed by Cursor Bugbot for commit 898fd7b. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Adds a Cloud VM default‑provider stress check and documents Freestyle as the default in staging/production (E2B kept as rollback). Also improves SSR safety and docs anchors, and tightens test types.

  • New Features

    • Added cloud-vm:stress (scripts/cloud-vm/stress-vm-api.mjs) to create VMs, attach via daemon RPC, proxy /healthz, then clean up; supports --count, --concurrency, --provider (default|e2b|freestyle), timeouts, and graceful SIGINT/SIGTERM cleanup; outputs a JSON summary with provider/image counts and cleanup stats. Uses @stackframe/js and ws.
    • Updated ops docs to recommend running the default‑provider stress check and to note Freestyle as the staging/production default with E2B as rollback.
  • Bug Fixes

    • Made DevPanel position SSR‑safe with a lazy initializer; switched internal links to Next Link and added anchor IDs to docs for deep links.
    • Expanded bun:test TypeScript types (lifecycle hooks, focused/skipped tests, matchers, mocks) to clear lint/type errors.

Written for commit 898fd7b. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • DevPanel now initializes its on-screen position correctly on first render.
  • Chores

    • Added a VM stress-test tool and a script to run it.
  • Documentation

    • Internal docs links updated to use improved routing components.
    • FAQ/notifications docs updated with anchored headings for CLI and integration sections and operational guidance for VM stress testing.
  • Tests

    • Strengthened TypeScript typings for the test framework.

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

@vercel

vercel Bot commented May 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment May 7, 2026 0:16am
cmux-staging Building Building Preview, Comment May 7, 2026 0:16am

@coderabbitai

coderabbitai Bot commented May 7, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a VM stress-test CLI and concurrent worker implementation with websocket RPC health checks, switches internal doc anchors to Next.js Link components, initializes DevPanel position from a safe helper, and replaces untyped bun:test declarations with typed interfaces.

Changes

VM Stress Testing Infrastructure

Layer / File(s) Summary
Setup & Configuration
web/package.json, web/scripts/cloud-vm/stress-vm-api.mjs
Adds cloud-vm:stress npm script and CLI parsing for count, concurrency, and provider; resolves @stackframe/js and constructs StackServerApp.
Concurrent Workers & Per-Case Workflow
web/scripts/cloud-vm/stress-vm-api.mjs
Implements concurrent worker loop, per-case VM lifecycle (create, attach), websocket RPC healthz proxy, timeout helpers, validators, result aggregation, JSON reporting, and non-zero exit on failures.
Documentation & Operational Commands
web/services/vms/README.md
Documents stress test run commands for staging/production and updates provider enablement/default snapshot notes.

UI Component & Navigation Updates

Layer / File(s) Summary
Panel Position Initialization
web/app/[locale]/components/spacing-control.tsx
Adds initialPanelPosition() to compute initial {x,y} from window dimensions with fallback and initializes DevPanel pos state from it; removes mount-only useEffect.
Internal Link Navigation
web/app/[locale]/page.tsx, web/app/[locale]/docs/notifications/page.tsx
Replaces anchor renderers and adds anchored headings; uses Next.js <Link> components for internal docs (/docs/keyboard-shortcuts, /docs/notifications#cli-usage, /docs/notifications#integration-examples), preserving linkClass.

Type System Improvements

Layer / File(s) Summary
bun:test Type Definitions
web/tests/bun-test.d.ts
Replaces any-typed exports with structured TypeScript types for test callbacks, lifecycle hooks, test function variants, matchers, and mock utilities.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Worker
  participant StackAPI
  participant VMAPI
  participant RPC
  CLI->>Worker: start N workers
  Worker->>StackAPI: create user / impersonation
  Worker->>VMAPI: POST /api/vm (create)
  Worker->>VMAPI: POST /api/vm/:id/attach-endpoint
  Worker->>RPC: websocket connect + auth
  Worker->>RPC: proxy.open / proxy.stream.subscribe / proxy.write
  RPC-->>Worker: healthz response
  Worker->>VMAPI: DELETE VM
  Worker->>StackAPI: DELETE user
  CLI->>Worker: aggregate results and exit
Loading

Estimated code review effort:

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs:

"I hopped through code with nimble paws,
spawning VMs and following claws.
Links now leap where anchors stood,
tests typed tidy — all feels good.
🐇✨"

🚥 Pre-merge checks | ✅ 13 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (13 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding a Cloud VM stress check for the default provider, which is the primary feature of this PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed Check not applicable: PR contains no Swift code. All changes are TypeScript/React, JavaScript, JSON, and Markdown files in the web directory.
Cmux Swift Blocking Runtime ✅ Passed No Swift code changes in this PR. The custom check for Swift blocking runtime is not applicable—all modifications are in web frontend/backend using TypeScript, JavaScript, JSON, and Markdown.
Cmux No Hacky Sleeps ✅ Passed All timer patterns are allowed: presentation timing in spacing-control, and cancellation-aware timeout abstractions with bounded deadlines in stress-vm-api.mjs. No hacky sleeps used to patch races.
Cmux Swift Concurrency ✅ Passed Custom check for Swift concurrency applies only to cmux-owned Swift code. This PR contains no Swift files—all changes are TypeScript/JavaScript web code. Check not applicable.
Cmux Swift @Concurrent ✅ Passed This PR contains no Swift code changes. All modifications are in TypeScript, JavaScript, JSON, and Markdown files. The Swift @concurrent annotation check is not applicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files are modified in this PR. All changes are to TypeScript/TSX, JavaScript, JSON, and Markdown files. The custom check applies only to production Swift changes, which are absent here.
Cmux Swift Logging ✅ Passed PR contains no Swift files. This is a web application PR with TypeScript/JavaScript changes only. The swift-logging check is not applicable.
Cmux Swiftui State Layout ✅ Passed No SwiftUI code changes in this PR. All changes are web application files (.tsx, .mjs, .ts, .json, .md). The check is not applicable to non-SwiftUI code.
Cmux Architecture Rethink ✅ Passed Not applicable. The check validates Swift architectural rules. PR contains only web (TypeScript/React/JavaScript) changes, no Swift code.
Description check ✅ Passed PR description provides comprehensive summary of changes, detailed verification steps, and addresses all template sections with concrete examples and completion evidence.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 task-freestyle-cloudvm-ready

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 and usage tips.

@socket-security

socket-security Bot commented May 7, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​vercel@​50.44.0809910099100
Addednpm/​@​opentui/​core@​0.1.107941009298100

View full report

@socket-security

socket-security Bot commented May 7, 2026 •

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Critical
Critical CVE: Sandbox Breakout / Arbitrary Code Execution in npm sandbox

CVE: GHSA-gc25-3vc5-2jf9 Sandbox Breakout / Arbitrary Code Execution in sandbox (CRITICAL)

Affected versions: >= 0.0.0

Patched version: No patched versions

From: ? → npm/vercel@50.44.0 → npm/sandbox@2.5.6

ℹ Read more on: This package | This alert | What is a critical CVE?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Remove or replace dependencies that include known critical CVEs. Consumers can use dependency overrides or npm audit fix --force to remove vulnerable dependencies.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/sandbox@2.5.6. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@greptile-apps

greptile-apps Bot commented May 7, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR ships a new cloud-vm:stress operator script that exercises the full VM lifecycle (create → attach → daemon RPC proxy → healthz → delete) with configurable concurrency against staging or production, and updates the runbook to reflect Freestyle as the current default provider with E2B as rollback. It also ships three lint/production-readiness fixes: an SSR-safe initializer for DevPanel, locale-aware Link replacements on the homepage, and tighter bun:test TypeScript declarations.

  • stress-vm-api.mjs: creates temporary Stack Auth users, issues POST /api/vm, POST /attach-endpoint, verifies daemon RPC via WebSocket browser-proxy (GET /healthz), then cleans up both the VM and the user; supports --count, --concurrency, --provider, interrupt-safe cleanup via SIGINT/SIGTERM, and exits non-zero on any failure.
  • spacing-control.tsx: moves window access from a post-mount useEffect into a lazy useState initializer guarded by an SSR check, eliminating the hydration flash.
  • bun-test.d.ts: replaces eight any exports with typed interfaces for lifecycle hooks, the test runner, matchers, and mocks.

Confidence Score: 5/5

Safe to merge; the new stress script is operator-only tooling and all production-path changes are small, self-contained fixes.

The stress script is well-structured: it uses bounded AbortController timeouts for HTTP calls, a 30-second ws.terminate deadline for the RPC healthz flow, a settled flag that prevents double-settle after ws.close(), and interrupt-safe cleanup via SIGINT/SIGTERM handlers. The previous thread's concern about the missing close handler has been addressed in this revision. The SSR fix in spacing-control.tsx is correct — a lazy initializer with a typeof window guard is the standard Next.js pattern. The to Link replacements use the already-imported locale-aware component. No production data paths are touched.

No files require special attention.

Important Files Changed

Filename Overview
web/scripts/cloud-vm/stress-vm-api.mjs New operator-facing stress script that creates VMs, attaches via daemon RPC, proxies healthz, and cleans up; well-structured with proper close-event handling, interrupt/signal handling, and bounded timeouts using AbortController.
web/app/[locale]/components/spacing-control.tsx SSR safety fix: replaces useEffect-based window access with a lazy useState initializer guarded by typeof window === "undefined", correctly preventing hydration mismatches.
web/app/[locale]/page.tsx Replaces bare <a> tags with the locale-aware Link component (already imported) and adds fragment anchors (#cli-usage, #integration-examples) to the notification doc links.
web/tests/bun-test.d.ts Replaces blanket any exports with proper typed interfaces for lifecycle hooks, test functions, matchers, and mocks; strictly additive and does not affect runtime behaviour.
web/app/[locale]/docs/notifications/page.tsx Adds id attributes to two heading elements to support anchor links from the homepage; no logic changes.
web/package.json Adds cloud-vm:stress script entry pointing to the new stress-vm-api.mjs.
web/services/vms/README.md Updates ops runbook: documents Freestyle as the current default, replaces the disabled-Freestyle caveat with a stress-check prerequisite, and points to the new cloud-vm:stress command.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Parse args: count / concurrency / provider] --> B[Load Stack credentials from env]
    B --> C{Run N workers in parallel}
    C --> D[Create temporary Stack user]
    D --> E[POST /api/vm]
    E --> F[POST /api/vm/:id/attach-endpoint]
    F --> G[Open daemon RPC WebSocket]
    G --> H[proxy.open port 7777]
    H --> I[proxy.write GET /healthz]
    I --> J{healthz 200 + ok:true?}
    J -- yes --> K[Record success]
    J -- no --> L[Record failure]
    K --> M[Cleanup: DELETE /api/vm/:id]
    L --> M
    M --> N{VM deleted?}
    N -- yes --> O[Delete Stack user]
    N -- no --> P[Skip user delete, log error]
    O --> Q[Print JSON summary]
    P --> Q
    Q --> R{Any failures?}
    R -- yes --> S[exit 1]
    R -- no --> T[exit 0]
Loading

Reviews (4): Last reviewed commit: "Address Cloud VM stress review feedback" | Re-trigger Greptile

Comment on lines +186 to +258
function rpcProxyHealthz(url, token, sessionId) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(url);
const chunks = [];
let streamId = "";
let capabilities = [];
const timer = setTimeout(() => {
ws.terminate();
reject(new Error("rpc proxy timeout"));
}, 30_000);
ws.on("error", (error) => {
clearTimeout(timer);
reject(error);
});
ws.on("open", () => {
ws.send(JSON.stringify({ type: "auth", token, session_id: sessionId }));
});
ws.on("message", (data, isBinary) => {
try {
if (isBinary) return;
const msg = JSON.parse(data.toString());
if (msg.type === "ready") {
ws.send(JSON.stringify({ id: 1, method: "hello", params: {} }));
return;
}
if (msg.id === 1) {
if (msg.ok !== true) throw new Error(`hello failed: ${JSON.stringify(msg)}`);
capabilities = msg.result?.capabilities ?? [];
ws.send(JSON.stringify({ id: 2, method: "proxy.open", params: { host: "127.0.0.1", port: 7777 } }));
return;
}
if (msg.id === 2) {
if (msg.ok !== true) throw new Error(`proxy.open failed: ${JSON.stringify(msg)}`);
streamId = msg.result?.stream_id;
if (!streamId) throw new Error(`proxy.open missing stream_id: ${JSON.stringify(msg)}`);
ws.send(JSON.stringify({ id: 3, method: "proxy.stream.subscribe", params: { stream_id: streamId } }));
return;
}
if (msg.id === 3) {
if (msg.ok !== true) throw new Error(`proxy.stream.subscribe failed: ${JSON.stringify(msg)}`);
const request = Buffer
.from("GET /healthz HTTP/1.1\r\nHost: 127.0.0.1\r\nConnection: close\r\n\r\n")
.toString("base64");
ws.send(JSON.stringify({
id: 4,
method: "proxy.write",
params: { stream_id: streamId, data_base64: request },
}));
return;
}
if (msg.id === 4 && msg.ok !== true) throw new Error(`proxy.write failed: ${JSON.stringify(msg)}`);
if (msg.event === "proxy.stream.data" && msg.data_base64) {
chunks.push(Buffer.from(msg.data_base64, "base64"));
}
if (msg.event === "proxy.stream.eof") {
const response = Buffer.concat(chunks).toString();
clearTimeout(timer);
ws.close();
if (!response.includes("HTTP/1.1 200 OK") || !response.includes('"ok":true')) {
reject(new Error(`unexpected proxied healthz response: ${response.slice(0, 200)}`));
} else {
resolve({ capabilities });
}
}
if (msg.event === "proxy.stream.error") throw new Error(`proxy.stream.error: ${JSON.stringify(msg)}`);
} catch (error) {
clearTimeout(timer);
ws.terminate();
reject(error);
}
});
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing close event handler leaves promise pending on unexpected disconnect

rpcProxyHealthz handles error and message events but has no ws.on("close") handler. If the server drops the connection before sending proxy.stream.eof (network blip, server-side crash, premature close frame), the promise neither resolves nor rejects until the 30-second setTimeout fires. Under concurrency, several slow-draining workers all stall at the timeout deadline simultaneously, making a partial outage look like a full 30-second hang rather than a fast failure. Adding ws.on("close", () => { clearTimeout(timer); reject(new Error("rpc proxy: connection closed unexpectedly")); }) would let failures surface immediately.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@web/app/`[locale]/page.tsx:
- Around line 179-187: The two Link renderers cliLink and hooksLink in page.tsx
both point to "/docs/notifications" but should use distinct anchors; update
cliLink (the cliLink function) to href="/docs/notifications#cli-usage" (or the
actual "CLI Usage" anchor) and update hooksLink (the hooksLink function) to
href="/docs/notifications#integration-examples" (or the actual "Integration
Examples" anchor) so each link navigates to the correct section.

In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 41-77: Add SIGINT and SIGTERM handlers that ensure in-flight case
cleanups run before exit: maintain a shared inFlight registry (e.g., a Map or
Set of cleanup callbacks) and have runCase register its per-case cleanup closure
(capturing vmId, authHeaders, user) at start and unregister it on normal
completion; on signal iterate the registry, run each cleanup callback (awaiting
or handling errors), log results, then exit non-zero. Hook the handlers up
around the concurrency loop that uses nextIndex/results/Promise.all so any
in-flight work is cleaned if the process is interrupted, and ensure the final
results/cleanupSummary reporting still runs only after signal-driven cleanup.
- Around line 240-250: The proxy.stream.eof handler currently calls resolve or
reject then continues execution, so add an early return after the EOF branch to
prevent fall-through into the proxy.stream.error check; locate the if (msg.event
=== "proxy.stream.eof") block (which builds response from Buffer.concat(chunks),
calls clearTimeout(timer), ws.close(), and then reject(...) or resolve({
capabilities })) and insert a return immediately after those resolve/reject
paths so that the subsequent if (msg.event === "proxy.stream.error") check
cannot run for the same message.
- Around line 154-164: The DELETE response handling only sets cleanup.vmDeleted
on status 200 and only records vmDeleteError when fetchWithTimeout throws;
change the post-request logic in the block that calls fetchWithTimeout (the
variable destroy/response from fetchWithTimeout to
`${project.url}/api/vm/${encodeURIComponent(vmId)}`) to treat any non-200 status
as a failure: set cleanup.vmDeleted = false and set cleanup.vmDeleteError to a
concise error string containing the HTTP status and statusText (and include
response body or text if available) so non-exceptional error responses (5xx,
4xx, 409, etc.) are captured in the cleanup summary.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 03de3702-48cf-4ddf-aa01-29b5152ea488

📥 Commits

Reviewing files that changed from the base of the PR and between 226b7a5 and df74b01.

📒 Files selected for processing (6)
  • web/app/[locale]/components/spacing-control.tsx
  • web/app/[locale]/page.tsx
  • web/package.json
  • web/scripts/cloud-vm/stress-vm-api.mjs
  • web/services/vms/README.md
  • web/tests/bun-test.d.ts

Comment thread web/app/[locale]/page.tsx Outdated
Comment thread web/scripts/cloud-vm/stress-vm-api.mjs
Comment thread web/scripts/cloud-vm/stress-vm-api.mjs Outdated
Comment thread web/scripts/cloud-vm/stress-vm-api.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
web/app/[locale]/page.tsx (1)

179-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

cliLink and hooksLink still resolve to the same URL — add distinct anchors.

Both renderers use href="/docs/notifications" with no fragment, so they navigate to the same page section despite representing semantically distinct concepts ("cmux CLI" vs "Claude Code hooks"). The docs at ./docs/notifications.md has separate headings for each. Add fragment anchors so each link lands on the relevant section.

🔗 Proposed fix
-                  cliLink: (chunks) => (
-                    <Link href="/docs/notifications" className={linkClass}>
-                      {chunks}
-                    </Link>
-                  ),
-                  hooksLink: (chunks) => (
-                    <Link href="/docs/notifications" className={linkClass}>
-                      {chunks}
-                    </Link>
-                  ),
+                  cliLink: (chunks) => (
+                    <Link href="/docs/notifications#cli-usage" className={linkClass}>
+                      {chunks}
+                    </Link>
+                  ),
+                  hooksLink: (chunks) => (
+                    <Link href="/docs/notifications#integration-examples" className={linkClass}>
+                      {chunks}
+                    </Link>
+                  ),
🤖 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 `@web/app/`[locale]/page.tsx around lines 179 - 187, The cliLink and hooksLink
renderers currently both point to "/docs/notifications"; update the hrefs in the
cliLink and hooksLink functions to include the correct fragment identifiers that
target the specific headings in the notifications doc (e.g.,
"/docs/notifications#cmux-cli" for the cmux CLI section and
"/docs/notifications#claude-code-hooks" for the Claude Code hooks section),
keeping the existing className usage (linkClass) and children rendering intact
so each link lands on its respective anchor in the document.
web/scripts/cloud-vm/stress-vm-api.mjs (2)

156-160: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Record non-200 VM destroy responses as cleanup errors.

Line 160 only flips vmDeleted. A 409 or 500 still leaves vmDeleteErrors at zero, so the JSON summary under-reports cleanup failures and hides leaked VMs from the operator.

🛠️ Minimal fix
         const destroy = await fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, {
           method: "DELETE",
           headers: authHeaders,
         }, 45_000);
         cleanup.vmDeleted = destroy.status === 200;
+        if (!cleanup.vmDeleted) {
+          const body = await destroy.text().catch(() => "");
+          cleanup.vmDeleteError = `DELETE /api/vm/${vmId} expected 200, got ${destroy.status}${body ? `: ${body.slice(0, 200)}` : ""}`;
+        }
       } catch (error) {
         cleanup.vmDeleteError = error instanceof Error ? error.message : String(error);
       }
🤖 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 `@web/scripts/cloud-vm/stress-vm-api.mjs` around lines 156 - 160, The destroy
call using fetchWithTimeout may return non-200 statuses (e.g., 409/500) but only
sets cleanup.vmDeleted and never records failures; update the destroy handling
so that after calling
fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, ...) you
check if destroy.status === 200 to set cleanup.vmDeleted, and otherwise
increment cleanup.vmDeleteErrors (and optionally capture status/text for
logging) so non-200 responses are counted as cleanup failures; reference the
variables/fns fetchWithTimeout, project.url, vmId, cleanup.vmDeleted, and
cleanup.vmDeleteErrors when making the change.

41-52: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Handle SIGINT/SIGTERM so interrupted runs still clean up in-flight cases.

Line 45 starts long-running workers, but Ctrl+C/SIGTERM will terminate the process before the in-flight runCase() cleanups finish. With --concurrency 4, one interrupted run can still leak multiple paid VMs and Stack test users.

🛡️ Minimal direction
 const startedAt = Date.now();
 const results = [];
 let nextIndex = 0;
+const inFlight = new Set();
+
+async function drainInFlight(signal) {
+  console.error(`received ${signal}; cleaning ${inFlight.size} in-flight case(s)`);
+  await Promise.allSettled([...inFlight].map((cleanupCase) => cleanupCase()));
+}
+
+for (const signal of ["SIGINT", "SIGTERM"]) {
+  process.once(signal, () => {
+    void drainInFlight(signal).finally(() => process.exit(130));
+  });
+}
 
 async function runCase(index) {
   ...
+  const cleanupCase = async () => {
+    // current finally-body cleanup
+  };
+  inFlight.add(cleanupCase);
   try {
     ...
   } finally {
-    // current cleanup
+    await cleanupCase();
+    inFlight.delete(cleanupCase);
   }
 }

Also applies to: 79-173

🤖 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 `@web/scripts/cloud-vm/stress-vm-api.mjs` around lines 41 - 52, The worker loop
can be terminated by SIGINT/SIGTERM before in-flight runCase() calls finish, so
add graceful shutdown: register process.on('SIGINT') and process.on('SIGTERM')
handlers that set a shared cancellation flag (or AbortController) and stop
starting new cases, then wait for any currently running runCase(index) to finish
so their cleanup runs. Update the worker function that uses nextIndex and
runCase to check the cancellation flag before assigning a new index and to pass
the abort signal into runCase (or have runCase check the flag) so cleanup logic
always runs; ensure the outer Promise.all resolves only after all in-flight
tasks complete. Reference runCase, nextIndex, the worker loop that builds
Array.from({ length: concurrency }, ...), and the signal handlers to locate
where to change behavior.
🤖 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 `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 154-170: The user deletion runs regardless of VM deletion outcome,
which can orphan VMs; change the logic so the call to user.delete() (and setting
cleanup.userDeleted/cleanup.userDeleteError) only executes when
cleanup.vmDeleted is true (i.e., the VM destroy succeeded) or when there was no
VM to delete (vmId is falsy); update the conditional around the user deletion
block to check cleanup.vmDeleted (or !vmId) before invoking user.delete() to
avoid removing the Stack user after a failed VM destroy.

---

Duplicate comments:
In `@web/app/`[locale]/page.tsx:
- Around line 179-187: The cliLink and hooksLink renderers currently both point
to "/docs/notifications"; update the hrefs in the cliLink and hooksLink
functions to include the correct fragment identifiers that target the specific
headings in the notifications doc (e.g., "/docs/notifications#cmux-cli" for the
cmux CLI section and "/docs/notifications#claude-code-hooks" for the Claude Code
hooks section), keeping the existing className usage (linkClass) and children
rendering intact so each link lands on its respective anchor in the document.

In `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 156-160: The destroy call using fetchWithTimeout may return
non-200 statuses (e.g., 409/500) but only sets cleanup.vmDeleted and never
records failures; update the destroy handling so that after calling
fetchWithTimeout(`${project.url}/api/vm/${encodeURIComponent(vmId)}`, ...) you
check if destroy.status === 200 to set cleanup.vmDeleted, and otherwise
increment cleanup.vmDeleteErrors (and optionally capture status/text for
logging) so non-200 responses are counted as cleanup failures; reference the
variables/fns fetchWithTimeout, project.url, vmId, cleanup.vmDeleted, and
cleanup.vmDeleteErrors when making the change.
- Around line 41-52: The worker loop can be terminated by SIGINT/SIGTERM before
in-flight runCase() calls finish, so add graceful shutdown: register
process.on('SIGINT') and process.on('SIGTERM') handlers that set a shared
cancellation flag (or AbortController) and stop starting new cases, then wait
for any currently running runCase(index) to finish so their cleanup runs. Update
the worker function that uses nextIndex and runCase to check the cancellation
flag before assigning a new index and to pass the abort signal into runCase (or
have runCase check the flag) so cleanup logic always runs; ensure the outer
Promise.all resolves only after all in-flight tasks complete. Reference runCase,
nextIndex, the worker loop that builds Array.from({ length: concurrency }, ...),
and the signal handlers to locate where to change behavior.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 67f48d22-d60a-4c1b-8ee5-acbea56274cd

📥 Commits

Reviewing files that changed from the base of the PR and between df74b01 and c4e11f8.

📒 Files selected for processing (6)
  • web/app/[locale]/components/spacing-control.tsx
  • web/app/[locale]/page.tsx
  • web/package.json
  • web/scripts/cloud-vm/stress-vm-api.mjs
  • web/services/vms/README.md
  • web/tests/bun-test.d.ts

Comment thread web/scripts/cloud-vm/stress-vm-api.mjs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 280-332: The ws message handler (ws.on("message", ...)) currently
only handles specific msg.type/ids/events and silently ignores unknown message
shapes; update the handler to detect and surface unexpected or error-shaped
messages by checking for msg.error or msg.type === "error" and by adding a final
fallback branch (after the existing ifs for type/ids/events) that throws or
calls fail() with a descriptive error including the raw msg and streamId/chunks
context; ensure this uses the existing fail(error) path so ws.terminate() is
invoked and diagnostics are captured (refer to the msg parsing block, the id
checks for 1-4, event checks like
"proxy.stream.data"/"proxy.stream.eof"/"proxy.stream.error", and the
fail/succeed calls).
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 5a4bcd65-72f5-4959-bd10-8773f4548817

📥 Commits

Reviewing files that changed from the base of the PR and between c4e11f8 and 27aa8b2.

📒 Files selected for processing (6)
  • web/app/[locale]/components/spacing-control.tsx
  • web/app/[locale]/page.tsx
  • web/package.json
  • web/scripts/cloud-vm/stress-vm-api.mjs
  • web/services/vms/README.md
  • web/tests/bun-test.d.ts

Comment thread web/scripts/cloud-vm/stress-vm-api.mjs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@web/scripts/cloud-vm/stress-vm-api.mjs`:
- Around line 77-94: The JSON summary currently computes "ok" from
failures.length only, which can be true when interruptedSignal is set; update
the console.log block to compute ok as failures.length === 0 &&
!interruptedSignal (or Boolean(interruptedSignal) negated) so interrupted runs
are marked as failed; adjust the expression where "ok" is defined in the
console.log call that includes target, project, provider, etc., to use both
failures and interruptedSignal.

In `@web/services/vms/README.md`:
- Line 230: Remove the hard-coded deployment state from the "Operational note"
(the line containing `sh-6ch5p9k23xrcx24056n8` / `freestyle-rpclease-20260502a`)
and replace it with a short runbook-style checklist that documents validation
criteria and where to look for the active provider/snapshot (e.g., which env
vars, deployment/operator objects, and commands to query current snapshot and
default provider). Ensure the new text names the concrete checks to run (e.g.,
inspect provider env var, query deployment/operator status, verify WebSocket PTY
and daemon RPC lease health) rather than embedding a specific snapshot id so the
README remains accurate across rollouts.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 2df2dc30-e6ae-4dc1-9996-ed9e9c9cbfd8

📥 Commits

Reviewing files that changed from the base of the PR and between 27aa8b2 and cebe3ec.

📒 Files selected for processing (6)
  • web/app/[locale]/components/spacing-control.tsx
  • web/app/[locale]/page.tsx
  • web/package.json
  • web/scripts/cloud-vm/stress-vm-api.mjs
  • web/services/vms/README.md
  • web/tests/bun-test.d.ts

Comment thread web/scripts/cloud-vm/stress-vm-api.mjs
Comment thread web/services/vms/README.md Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit cebe3ec. Configure here.

Comment thread web/app/[locale]/page.tsx
@lawrencecchen
lawrencecchen merged commit f325b8b into main May 7, 2026
25 checks passed
@lawrencecchen
lawrencecchen deleted the task-freestyle-cloudvm-ready branch May 7, 2026 12:22
ShubhamPatilsd pushed a commit to emergent-inc/mosaic that referenced this pull request Jul 9, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — 898fd7b5 Deployed May 7, 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.

1 participant