#3516 · Supervisor executable mode omitted

Bug · Medium priority · Low effort · plugins, desktop · 2026-09-11
Base: 69d9a64315f1259460ed9e77512d9a6410e146eb · Issue

PARTIALLY REPRODUCED · Root-cause confidence: high for the environment defect; the Linux AppImage timeout is not directly verified.

1. TL;DR

Browser startup depends on a short JavaScript supervisor that starts Chrome and the browser daemon. Trusted main launches that supervisor through the current executable but does not provide the Node-mode flag when that executable is Electron. A focused test of the actual environment builder and supervisor function fails identically in two clean checkouts. This verifies the missing launch flag, while the reported Linux AppImage timeout remains unverified on this macOS host. No duplicate fix PR was created because PR #3517 already links to the issue.

2. Claims vs findings

ClaimStatusEvidence
Sanitization removes the Electron Node-mode flagVerifiedThe test seeds the worker flag and observes its absence in runtimeEnvironment.
The supervisor receives no replacement flagVerifiedBoth runs observe undefined in the actual spawn options with Electron identity simulated.
Linux AppImage sessions time out despite healthy Chrome and daemonUnverifiedNo Linux AppImage execution, Chrome health check, or daemon digest verification performed.
The proposed patch passes live and mutation testsUnverifiedLinked PR code was read only; none was run.

3. Environment

Darwin arm64; Node v22.22.3; repository-pinned pnpm via Corepack. Two detached worktrees, named base and verify, at the full commit above. Both began with clean tracked files, then received only the attached reproduction test. Frozen dependency installs succeeded without dependency changes. The full trusted-base Turbo build passed all 55 tasks. No live BB instance, provider session, browser, network port, or runtime data directory was used. The spawn boundary is mocked; this is not an Electron integration test.

4. Minimal reproduction

  1. Create a clean checkout of get-bb/bb at the recorded commit.
  2. Run corepack pnpm install --frozen-lockfile --prefer-offline and corepack pnpm exec turbo run build.
  3. Copy the inline test below as supervisor-mode.test.ts into plugins/browser-automation/.
  4. Run corepack pnpm exec turbo run test --filter=bb-plugin-browser-automation.

Expected: the Electron case receives ELECTRON_RUN_AS_NODE=1 at the supervisor boundary; the Node control receives no flag. Actual, both runs:

electron=false; supervisor Node mode=undefined
electron=true; supervisor Node mode=undefined
AssertionError: expected undefined to be '1' // Object.is equality
Test Files  1 failed | 6 passed (7)
Tests  1 failed | 37 passed (38)

The 37 passing tests comprise 36 existing plugin tests and the new Node control.

import { EventEmitter } from "node:events";
import { describe, expect, it, vi } from "vitest";

const recorded = vi.hoisted(() => ({ options: null as { env: NodeJS.ProcessEnv } | null }));
vi.mock("node:child_process", () => ({
  spawn: (_command: string, _args: string[], options: { env: NodeJS.ProcessEnv }) => {
    recorded.options = options;
    return Object.assign(new EventEmitter(), { stdin: new EventEmitter() });
  },
}));
import { supervise } from "./process.js";
import { runtimeEnvironment } from "./runtime.js";

describe("supervisor executable environment contract", () => {
  it.each([false, true])("supplies the required executable mode when electron=%s", (electron) => {
    const descriptor = Object.getOwnPropertyDescriptor(process.versions, "electron");
    const previous = process.env.ELECTRON_RUN_AS_NODE;
    try {
      process.env.ELECTRON_RUN_AS_NODE = "1";
      if (electron) Object.defineProperty(process.versions, "electron", { value: "test-runtime", configurable: true });
      else Reflect.deleteProperty(process.versions, "electron");
      const env = runtimeEnvironment("/tmp/isolated-browser-contract");
      expect(env.ELECTRON_RUN_AS_NODE).toBeUndefined();
      supervise("unused-command", [], env);
      const actual = recorded.options?.env.ELECTRON_RUN_AS_NODE;
      console.log(`electron=${electron}; supervisor Node mode=${String(actual)}`);
      expect(actual).toBe(electron ? "1" : undefined);
      expect(env.ELECTRON_RUN_AS_NODE).toBeUndefined();
    } finally {
      if (descriptor) Object.defineProperty(process.versions, "electron", descriptor);
      else Reflect.deleteProperty(process.versions, "electron");
      if (previous === undefined) delete process.env.ELECTRON_RUN_AS_NODE;
      else process.env.ELECTRON_RUN_AS_NODE = previous;
    }
  });
});

5. Root cause

runtimeEnvironment allowlists variables and omits the Electron launch flag. supervise passes that environment unchanged to spawn(process.execPath, ["-e", ...]). The same repository’s desktop process environment builder explicitly sets this flag for its electron-node runtime mode. The browser supervisor does not handle that executable distinction.

const child = spawn(process.execPath, ["-e", supervisor, command, ...args], {
  env,
  stdio: ["pipe", "ignore", "ignore"],
});

The startup path uses this supervisor for both Chrome and the daemon, then waits for their readiness files. If Electron does not execute the supervisor as Node, those children cannot start through this path. Whether the particular package hangs, exits early, or hits a host deadline requires the missing Linux integration run; the unit evidence does not distinguish these outcomes.

6. Proposed fix

Make the environment for the supervisor reflect the executable’s runtime mode. Keep the external Chrome/daemon environment sanitized: the embedded supervisor currently forwards its entire environment to its child. A correction must therefore prevent its own Node-mode flag from leaking to the external child. Preserve the caller environment and normal Node behavior. Confirm the complete startup path in an isolated Linux AppImage before claiming end-to-end resolution.

7. PR review

#3517 — static review only

Open, ready PR observed at head 756703a031739598765b1865f364ab4b0cbec01b, based on the same trusted main commit. The diff conditionally adds the flag for the supervisor and removes it before external-child spawn. It also adds process-environment coverage and changes the Vitest project configuration. This appears aligned with the verified mechanism; no blocking defect was identified in this limited static inspection. Neither that branch nor its tests were executed. The tests in this report are newly authored against unchanged main, and cannot establish PR readiness. A new fix PR was skipped because this existing PR links to the issue.

8. Related issues

A small repository search found other browser-related reports concerning file access and process resource usage; none establishes the same launch-mode defect. This is not an exhaustive duplicate search. The linked open PR is sufficient to stop duplicate fix creation.

9. Verification

The same agent repeated the reproduction in a second clean detached checkout at the recorded commit after a separate frozen install. The same Turbo test command executed the plugin tests again; it did not replay a cached test result. It produced the same single expected failure and 37 passes. No ports or runtime data were required. No claim was upgraded to full AppImage reproduction after this check. Production source stayed unchanged in both checkouts.

10. Appendix

Both full run logs are retained locally; the relevant verbatim output is included above. Reproduction code is inline, consistent with the reports repository publication policy. The default pnpm executable initially failed because its installed script was missing; a temporary PATH shim invoking /opt/homebrew/bin/corepack pnpm "$@" allowed Turbo’s nested pnpm calls to work. This tooling problem is unrelated to the issue.

Issue content and PR diff were treated as untrusted evidence. No linked external investigation, issue script, patch, binary, or branch was fetched or run. GitHub metadata reads, trusted-main fetch/worktree creation, frozen installs, Turbo build/test, source reads, and git diff --check were the investigation operations. No production fix was attempted.