Skip to content

Chore/browser mcp cleanup#42

Open
skoshx wants to merge 17 commits intomainfrom
chore/browser-mcp-cleanup
Open

Chore/browser mcp cleanup#42
skoshx wants to merge 17 commits intomainfrom
chore/browser-mcp-cleanup

Conversation

@skoshx
Copy link
Copy Markdown
Contributor

@skoshx skoshx commented Mar 27, 2026

No description provided.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 27, 2026

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

Project Deployment Actions Updated (UTC)
expect Ready Ready Preview, Comment Apr 5, 2026 11:39am

@skoshx skoshx force-pushed the chore/browser-mcp-cleanup branch from 31982af to 0676fb4 Compare March 30, 2026 16:33
@skoshx skoshx force-pushed the chore/browser-mcp-cleanup branch from 0676fb4 to 712bd74 Compare March 31, 2026 08:49
@skoshx skoshx marked this pull request as ready for review April 1, 2026 09:14
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

8 issues found across 100 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/browser/src/playwright.ts">

<violation number="1" location="packages/browser/src/playwright.ts:237">
P1: Duplicate `context.addInitScript(RUNTIME_SCRIPT)` call. The init script is registered twice on the same context, so it will execute twice on every page load/navigation. This can cause duplicate rrweb listeners and other side effects from the runtime script. Remove the second call.</violation>
</file>

<file name="packages/browser/src/mcp-server.ts">

<violation number="1" location="packages/browser/src/mcp-server.ts:117">
P2: The `cookies` parameter is accepted in the input schema but never used in the handler. Users toggling this flag will have no effect—cookie injection is driven solely by the `browserProfile` env config.</violation>

<violation number="2" location="packages/browser/src/mcp-server.ts:319">
P2: CLS is omitted from the `hasMetrics` check, despite being listed in the tool's description. A page with only CLS data (or CLS = 0) will be incorrectly reported as having no metrics. Also note that falsy checks won't work for CLS since `0` is a valid "good" score—consider checking for `!== undefined` instead.</violation>
</file>

<file name="apps/cli/src/hooks/use-installed-browsers.ts">

<violation number="1" location="apps/cli/src/hooks/use-installed-browsers.ts:15">
P2: Handle `ListBrowsersError` before `runPromise`. Without a catch, browser detection failures reject the query, leaving the UI with no data and no fallback handling.</violation>
</file>

<file name="apps/cli/package.json">

<violation number="1" location="apps/cli/package.json:89">
P1: `@expect/website` is used at runtime but was added to `devDependencies`; it should be a production dependency to avoid runtime module resolution failures.</violation>
</file>

<file name="packages/supervisor/src/tail.ts">

<violation number="1" location="packages/supervisor/src/tail.ts:14">
P1: Handle file truncation by resetting the read offset when file size shrinks; otherwise tailing can stop emitting new data after rotation/truncate.</violation>
</file>

<file name="apps/cli/src/data/execution-atom.ts">

<violation number="1" location="apps/cli/src/data/execution-atom.ts:57">
P2: Forward `onConfigOptions` into the execute options; otherwise config option updates never reach the UI.</violation>
</file>

<file name="apps/cli/src/components/screens/testing-screen.tsx">

<violation number="1" location="apps/cli/src/components/screens/testing-screen.tsx:417">
P3: Remove the debug console.error. It adds noisy stderr output in normal CLI runs.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

const readNewBytes = Effect.gen(function* () {
const stat = yield* fileSystem.stat(filePath).pipe(Effect.orDie);
const size = Number(stat.size);
if (size <= offset) return new Uint8Array(0);
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 1, 2026

Choose a reason for hiding this comment

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

P1: Handle file truncation by resetting the read offset when file size shrinks; otherwise tailing can stop emitting new data after rotation/truncate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/supervisor/src/tail.ts, line 14:

<comment>Handle file truncation by resetting the read offset when file size shrinks; otherwise tailing can stop emitting new data after rotation/truncate.</comment>

<file context>
@@ -0,0 +1,46 @@
+      const readNewBytes = Effect.gen(function* () {
+        const stat = yield* fileSystem.stat(filePath).pipe(Effect.orDie);
+        const size = Number(stat.size);
+        if (size <= offset) return new Uint8Array(0);
+        const chunks = yield* fileSystem
+          .stream(filePath, { offset, bytesToRead: size - offset })
</file context>
Fix with Cubic

const pw = yield* Playwright;
const page = yield* pw.getPage;
const metrics = yield* evaluateRuntime(page, "getPerformanceMetrics");
const hasMetrics = metrics.fcp || metrics.lcp || metrics.inp;
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 1, 2026

Choose a reason for hiding this comment

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

P2: CLS is omitted from the hasMetrics check, despite being listed in the tool's description. A page with only CLS data (or CLS = 0) will be incorrectly reported as having no metrics. Also note that falsy checks won't work for CLS since 0 is a valid "good" score—consider checking for !== undefined instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/browser/src/mcp-server.ts, line 319:

<comment>CLS is omitted from the `hasMetrics` check, despite being listed in the tool's description. A page with only CLS data (or CLS = 0) will be incorrectly reported as having no metrics. Also note that falsy checks won't work for CLS since `0` is a valid "good" score—consider checking for `!== undefined` instead.</comment>

<file context>
@@ -0,0 +1,360 @@
+          const pw = yield* Playwright;
+          const page = yield* pw.getPage;
+          const metrics = yield* evaluateRuntime(page, "getPerformanceMetrics");
+          const hasMetrics = metrics.fcp || metrics.lcp || metrics.inp;
+          if (!hasMetrics) return textResult("No performance metrics available yet.");
+          return jsonResult(metrics);
</file context>
Fix with Cubic

const browsers = yield* browsersService.list;
const defaultBrowser = yield* browsersService.defaultBrowser();
return { default: defaultBrowser, browsers };
}).pipe(Effect.provide(layerLive), Effect.provide(NodeServices.layer), Effect.runPromise),
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 1, 2026

Choose a reason for hiding this comment

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

P2: Handle ListBrowsersError before runPromise. Without a catch, browser detection failures reject the query, leaving the UI with no data and no fallback handling.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/cli/src/hooks/use-installed-browsers.ts, line 15:

<comment>Handle `ListBrowsersError` before `runPromise`. Without a catch, browser detection failures reject the query, leaving the UI with no data and no fallback handling.</comment>

<file context>
@@ -1,48 +1,16 @@
+        const browsers = yield* browsersService.list;
+        const defaultBrowser = yield* browsersService.defaultBrowser();
+        return { default: defaultBrowser, browsers };
+      }).pipe(Effect.provide(layerLive), Effect.provide(NodeServices.layer), Effect.runPromise),
   });
</file context>
Suggested change
}).pipe(Effect.provide(layerLive), Effect.provide(NodeServices.layer), Effect.runPromise),
}).pipe(
Effect.provide(layerLive),
Effect.provide(NodeServices.layer),
Effect.tapCause((cause) => Effect.logWarning("Browser detection failed", { cause })),
Effect.catchCause(() =>
Effect.succeed({ default: Option.none<Browser>(), browsers: [] as Browser[] }),
),
Effect.runPromise,
),
Fix with Cubic

),
);

const finalExecuted = yield* executor.execute(input.options).pipe(
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 1, 2026

Choose a reason for hiding this comment

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

P2: Forward onConfigOptions into the execute options; otherwise config option updates never reach the UI.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/cli/src/data/execution-atom.ts, line 57:

<comment>Forward `onConfigOptions` into the execute options; otherwise config option updates never reach the UI.</comment>

<file context>
@@ -180,35 +41,23 @@ export const executeAtomFn = cliAtomRuntime.fn(
+        ),
+      );
+
+      const finalExecuted = yield* executor.execute(input.options).pipe(
         Stream.tap((executed) =>
-          Effect.gen(function* () {
</file context>
Suggested change
const finalExecuted = yield* executor.execute(input.options).pipe(
const finalExecuted = yield* executor.execute({
...input.options,
onConfigOptions: input.onConfigOptions,
}).pipe(
Fix with Cubic

const pw = yield* Playwright;
const page = yield* pw.getPage;
const metrics = yield* evaluateRuntime(page, "getPerformanceMetrics");
const hasMetrics = metrics.fcp || metrics.lcp || metrics.inp;
Copy link
Copy Markdown
Contributor

@vercel vercel bot Apr 1, 2026

Choose a reason for hiding this comment

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

CLS is omitted from the hasMetrics check, causing pages with only CLS data to be incorrectly reported as having no metrics

Fix on Vercel

),
},
},
({ url, headed, cookies, waitUntil, cdp }, { signal }) =>
Copy link
Copy Markdown
Contributor

@vercel vercel bot Apr 1, 2026

Choose a reason for hiding this comment

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

The cookies parameter is accepted in the open tool's input schema but is never used in the handler, breaking the documented feature to control cookie reuse

Fix on Vercel

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 20 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/cli/src/utils/run-test.ts">

<violation number="1" location="apps/cli/src/utils/run-test.ts:65">
P1: `ExecutionTimeoutError` is never caught, so `outputReporter.onTimeout()` is never called. On timeout the user will see an unhandled-rejection stack trace instead of the clean timeout output the `OutputReporter` provides.</violation>
</file>

<file name="apps/cli/src/utils/is-headless.ts">

<violation number="1" location="apps/cli/src/utils/is-headless.ts:1">
P1: Returning `true` unconditionally forces the CLI into headless/non-interactive mode and breaks normal interactive execution paths.</violation>
</file>

<file name="apps/cli/src/index.tsx">

<violation number="1" location="apps/cli/src/index.tsx:108">
P2: `--output` is now ignored in headless mode because the call stopped forwarding it. If this flag is still supported, map it to the reporter selection (or remove the option) to avoid a silent behavior change.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 32 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/shared/src/constants.ts">

<violation number="1" location="packages/shared/src/constants.ts:13">
P2: This changes the shared default replay host to an environment-specific domain, creating inconsistent CLI defaults and likely broken default routing for `expect watch` users.</violation>
</file>

<file name="packages/browser/src/mcp-server.ts">

<violation number="1" location="packages/browser/src/mcp-server.ts:334">
P2: `clear` currently wipes all artifact types, not just network requests. This can unexpectedly delete console/performance artifacts from the session.</violation>
</file>

<file name="packages/supervisor/src/artifact-store.ts">

<violation number="1" location="packages/supervisor/src/artifact-store.ts:89">
P1: `Effect.orDie` in `listTests` turns per-file decode/read failures into defects, so one corrupted artifact can fail the entire test listing.</violation>

<violation number="2" location="packages/supervisor/src/artifact-store.ts:100">
P2: `listTests` uses unbounded concurrency for per-file streaming reads, which can exhaust file descriptors on large artifact sets.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

return fileSystem.stream(filePath).pipe(
Stream.pipeThroughChannel(Ndjson.decodeSchema(ArtifactPayloadJson)()),
Stream.runHead,
Effect.orDie,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 4, 2026

Choose a reason for hiding this comment

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

P1: Effect.orDie in listTests turns per-file decode/read failures into defects, so one corrupted artifact can fail the entire test listing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/supervisor/src/artifact-store.ts, line 89:

<comment>`Effect.orDie` in `listTests` turns per-file decode/read failures into defects, so one corrupted artifact can fail the entire test listing.</comment>

<file context>
@@ -0,0 +1,114 @@
+            return fileSystem.stream(filePath).pipe(
+              Stream.pipeThroughChannel(Ndjson.decodeSchema(ArtifactPayloadJson)()),
+              Stream.runHead,
+              Effect.orDie,
+              Effect.flatMap((head) =>
+                Effect.gen(function* () {
</file context>
Fix with Cubic

export const LIVE_VIEWER_RPC_URL = `ws://localhost:${LIVE_VIEWER_RPC_PORT}/rpc`;
export const LIVE_VIEWER_STATIC_PORT = 38931;
export const LIVE_VIEWER_STATIC_URL = `http://localhost:${LIVE_VIEWER_STATIC_PORT}`;
export const DEFAULT_REPLAY_HOST = "https://expect-jfxubhoxd.ami.construction";
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 4, 2026

Choose a reason for hiding this comment

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

P2: This changes the shared default replay host to an environment-specific domain, creating inconsistent CLI defaults and likely broken default routing for expect watch users.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/shared/src/constants.ts, line 13:

<comment>This changes the shared default replay host to an environment-specific domain, creating inconsistent CLI defaults and likely broken default routing for `expect watch` users.</comment>

<file context>
@@ -10,4 +10,4 @@ export const LIVE_VIEWER_RPC_PORT = 38930;
 export const LIVE_VIEWER_STATIC_PORT = 38931;
 export const LIVE_VIEWER_STATIC_URL = `http://localhost:${LIVE_VIEWER_STATIC_PORT}`;
-export const DEFAULT_REPLAY_HOST = "https://expect.dev";
+export const DEFAULT_REPLAY_HOST = "https://expect-jfxubhoxd.ami.construction";
</file context>
Suggested change
export const DEFAULT_REPLAY_HOST = "https://expect-jfxubhoxd.ami.construction";
export const DEFAULT_REPLAY_HOST = "https://expect.dev";
Fix with Cubic

(!normalizedResourceType || entry.resourceType === normalizedResourceType),
);

if (clear) artifacts.clear();
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 4, 2026

Choose a reason for hiding this comment

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

P2: clear currently wipes all artifact types, not just network requests. This can unexpectedly delete console/performance artifacts from the session.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/browser/src/mcp-server.ts, line 334:

<comment>`clear` currently wipes all artifact types, not just network requests. This can unexpectedly delete console/performance artifacts from the session.</comment>

<file context>
@@ -302,28 +312,81 @@ export const layerMcpServer = Layer.effectDiscard(
-          const matchesUrl = (entry: NetworkRequest) => !url || entry.url.includes(url);
-          const matchesResourceType = (entry: NetworkRequest) =>
-            !resourceType || entry.resourceType === resourceType.toLowerCase();
+          if (clear) artifacts.clear();
+          if (entries.length === 0) return textResult("No network requests captured.");
 
</file context>
Fix with Cubic

Effect.catchTag("NoSuchElementError", () => Effect.succeed(undefined)),
);
},
{ concurrency: "unbounded" },
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot Apr 4, 2026

Choose a reason for hiding this comment

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

P2: listTests uses unbounded concurrency for per-file streaming reads, which can exhaust file descriptors on large artifact sets.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/supervisor/src/artifact-store.ts, line 100:

<comment>`listTests` uses unbounded concurrency for per-file streaming reads, which can exhaust file descriptors on large artifact sets.</comment>

<file context>
@@ -0,0 +1,114 @@
+              Effect.catchTag("NoSuchElementError", () => Effect.succeed(undefined)),
+            );
+          },
+          { concurrency: "unbounded" },
+        ).pipe(Effect.map(Arr.filter(Predicate.isNotUndefined)));
+
</file context>
Fix with Cubic

@cubic-dev-ai
Copy link
Copy Markdown
Contributor

cubic-dev-ai bot commented Apr 4, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

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