From eb2089ad2cc80621110b124f501e379efe55c25b Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 21 Jun 2026 01:09:14 +0000 Subject: [PATCH 1/2] feat(experiments): register PARALLEL_TOOL_EXECUTION flag (internal-only) --- packages/types/src/experiment.ts | 9 ++++- src/shared/__tests__/experiments.spec.ts | 21 ++++++++++++ src/shared/experiments.ts | 3 ++ .../settings/ExperimentalSettings.tsx | 1 + .../__tests__/ExperimentalSettings.spec.tsx | 33 +++++++++++++++++++ 5 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx diff --git a/packages/types/src/experiment.ts b/packages/types/src/experiment.ts index d7eb0b03d6..5d511859b1 100644 --- a/packages/types/src/experiment.ts +++ b/packages/types/src/experiment.ts @@ -6,7 +6,13 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js" * ExperimentId */ -export const experimentIds = ["preventFocusDisruption", "imageGeneration", "runSlashCommand", "customTools"] as const +export const experimentIds = [ + "preventFocusDisruption", + "imageGeneration", + "runSlashCommand", + "customTools", + "parallelToolExecution", +] as const export const experimentIdsSchema = z.enum(experimentIds) @@ -21,6 +27,7 @@ export const experimentsSchema = z.object({ imageGeneration: z.boolean().optional(), runSlashCommand: z.boolean().optional(), customTools: z.boolean().optional(), + parallelToolExecution: z.boolean().optional(), }) export type Experiments = z.infer diff --git a/src/shared/__tests__/experiments.spec.ts b/src/shared/__tests__/experiments.spec.ts index 92a7d7604f..f2261a5c09 100644 --- a/src/shared/__tests__/experiments.spec.ts +++ b/src/shared/__tests__/experiments.spec.ts @@ -21,6 +21,7 @@ describe("experiments", () => { imageGeneration: false, runSlashCommand: false, customTools: false, + parallelToolExecution: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(false) }) @@ -31,6 +32,7 @@ describe("experiments", () => { imageGeneration: false, runSlashCommand: false, customTools: false, + parallelToolExecution: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(true) }) @@ -41,8 +43,27 @@ describe("experiments", () => { imageGeneration: false, runSlashCommand: false, customTools: false, + parallelToolExecution: false, } expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION)).toBe(false) }) }) + + describe("PARALLEL_TOOL_EXECUTION", () => { + it("is configured correctly", () => { + expect(EXPERIMENT_IDS.PARALLEL_TOOL_EXECUTION).toBe("parallelToolExecution") + expect(experimentConfigsMap.PARALLEL_TOOL_EXECUTION).toMatchObject({ + enabled: false, + showInSettings: false, + }) + }) + + it("returns false by default", () => { + expect(Experiments.isEnabled({}, "parallelToolExecution")).toBe(false) + }) + + it("returns true when enabled", () => { + expect(Experiments.isEnabled({ parallelToolExecution: true }, "parallelToolExecution")).toBe(true) + }) + }) }) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index e189f99e23..5ab1ee70b4 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -5,6 +5,7 @@ export const EXPERIMENT_IDS = { IMAGE_GENERATION: "imageGeneration", RUN_SLASH_COMMAND: "runSlashCommand", CUSTOM_TOOLS: "customTools", + PARALLEL_TOOL_EXECUTION: "parallelToolExecution", } as const satisfies Record type _AssertExperimentIds = AssertEqual>> @@ -13,6 +14,7 @@ type ExperimentKey = Keys interface ExperimentConfig { enabled: boolean + showInSettings?: boolean } export const experimentConfigsMap: Record = { @@ -20,6 +22,7 @@ export const experimentConfigsMap: Record = { IMAGE_GENERATION: { enabled: false }, RUN_SLASH_COMMAND: { enabled: false }, CUSTOM_TOOLS: { enabled: false }, + PARALLEL_TOOL_EXECUTION: { enabled: false, showInSettings: false }, } export const experimentDefault = Object.fromEntries( diff --git a/webview-ui/src/components/settings/ExperimentalSettings.tsx b/webview-ui/src/components/settings/ExperimentalSettings.tsx index 23786ce0b9..d5c55297ea 100644 --- a/webview-ui/src/components/settings/ExperimentalSettings.tsx +++ b/webview-ui/src/components/settings/ExperimentalSettings.tsx @@ -51,6 +51,7 @@ export const ExperimentalSettings = ({
{Object.entries(experimentConfigsMap) .filter(([key]) => key in EXPERIMENT_IDS) + .filter(([, config]) => config.showInSettings !== false) .map((config) => { // Use the same translation key pattern as ExperimentalFeature const experimentKey = config[0] diff --git a/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx new file mode 100644 index 0000000000..0774bb42e5 --- /dev/null +++ b/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx @@ -0,0 +1,33 @@ +import { render, screen } from "@testing-library/react" + +import { experimentDefault } from "@roo/experiments" + +import { ExperimentalSettings } from "../ExperimentalSettings" + +vi.mock("@src/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => key, + }), +})) + +describe("ExperimentalSettings", () => { + const defaultProps = { + experiments: experimentDefault, + setExperimentEnabled: vi.fn(), + setImageGenerationProvider: vi.fn(), + setOpenRouterImageApiKey: vi.fn(), + setImageGenerationSelectedModel: vi.fn(), + } + + beforeEach(() => { + vi.clearAllMocks() + }) + + it("does not render internal-only experiment flags", () => { + render() + + expect(screen.getByText("settings:experimental.PREVENT_FOCUS_DISRUPTION.name")).toBeInTheDocument() + expect(screen.getByText("settings:experimental.RUN_SLASH_COMMAND.name")).toBeInTheDocument() + expect(screen.queryByText("settings:experimental.PARALLEL_TOOL_EXECUTION.name")).not.toBeInTheDocument() + }) +}) From 4367bf377a882e50ae84f063ae67affa1d8b4afa Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Mon, 22 Jun 2026 02:11:05 +0000 Subject: [PATCH 2/2] test(ExperimentalSettings): increasing coverage --- src/shared/experiments.ts | 2 ++ .../components/settings/__tests__/ExperimentalSettings.spec.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/shared/experiments.ts b/src/shared/experiments.ts index 5ab1ee70b4..ae538b9138 100644 --- a/src/shared/experiments.ts +++ b/src/shared/experiments.ts @@ -14,6 +14,7 @@ type ExperimentKey = Keys interface ExperimentConfig { enabled: boolean + /** Defaults to true; set to false to hide from the Settings panel. */ showInSettings?: boolean } @@ -22,6 +23,7 @@ export const experimentConfigsMap: Record = { IMAGE_GENERATION: { enabled: false }, RUN_SLASH_COMMAND: { enabled: false }, CUSTOM_TOOLS: { enabled: false }, + // TODO: add i18n keys (settings:experimental.PARALLEL_TOOL_EXECUTION.name/.description) in the same PR that sets showInSettings: true PARALLEL_TOOL_EXECUTION: { enabled: false, showInSettings: false }, } diff --git a/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx index 0774bb42e5..b31f87dc7e 100644 --- a/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/ExperimentalSettings.spec.tsx @@ -28,6 +28,8 @@ describe("ExperimentalSettings", () => { expect(screen.getByText("settings:experimental.PREVENT_FOCUS_DISRUPTION.name")).toBeInTheDocument() expect(screen.getByText("settings:experimental.RUN_SLASH_COMMAND.name")).toBeInTheDocument() + expect(screen.getByText("settings:experimental.IMAGE_GENERATION.name")).toBeInTheDocument() + expect(screen.getByText("settings:experimental.CUSTOM_TOOLS.name")).toBeInTheDocument() expect(screen.queryByText("settings:experimental.PARALLEL_TOOL_EXECUTION.name")).not.toBeInTheDocument() }) })