How Experiment Flags Control Feature Rollouts in the Roo Code Extension
Roo Code utilizes a centralized experiment flag system in src/shared/experiments.ts that gates features through a three-layer architecture: flag definitions, runtime checks via experiments.isEnabled(), and VS Code state persistence.
The Roo Code VS Code extension implements a lightweight yet robust mechanism for rolling out new capabilities gradually. By leveraging experiment flags defined in the shared configuration, developers can safely deploy optional functionality without risking stability for all users. This system allows specific tools and UI components to check whether a feature should be active based on merged user preferences and default settings.
The Three-Piece Architecture of Roo Code's Experiment System
The experiment flag implementation consists of three interconnected components that manage the complete lifecycle from definition to runtime evaluation.
First, EXPERIMENT_IDS and experimentConfigsMap in src/shared/experiments.ts enumerate available flags and establish default disabled states. Second, the experiments.isEnabled() helper function provides the runtime logic for checking flag status. Third, individual tools and components perform feature checks by calling this helper with the current state to determine execution paths.
Defining Experiment Flags in src/shared/experiments.ts
All experiment flag definitions reside in src/shared/experiments.ts, which exports both the identifier constants and their default configurations.
The EXPERIMENT_IDS Enum
The system uses a const assertion to define unique string identifiers for each experiment:
export const EXPERIMENT_IDS = {
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
IMAGE_GENERATION: "imageGeneration",
RUN_SLASH_COMMAND: "runSlashCommand",
CUSTOM_TOOLS: "customTools",
} as const satisfies Record<string, ExperimentId>;
This structure ensures type safety while providing human-readable keys for developers.
Default Configuration with experimentConfigsMap
Each flag maps to a configuration object specifying its default enabled state. As implemented in the Roo Code source, all flags default to false:
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
PREVENT_FOCUS_DISRUPTION: { enabled: false },
IMAGE_GENERATION: { enabled: false },
RUN_SLASH_COMMAND: { enabled: false },
CUSTOM_TOOLS: { enabled: false },
};
Generating Fallback Values
The system converts the configuration map into a flat record for runtime lookups using experimentDefault:
export const experimentDefault = Object.fromEntries(
Object.entries(experimentConfigsMap).map(([_, config]) => [
EXPERIMENT_IDS[_ as keyof typeof EXPERIMENT_IDS] as ExperimentId,
config.enabled,
]),
) as Record<ExperimentId, boolean>;
This creates a fallback object where missing user preferences default to the safe, disabled state.
Checking Feature Flags at Runtime
The experiments namespace exposes two utilities for interacting with flags. The isEnabled method handles the critical logic of merging user state with defaults:
export const experiments = {
get: (id: ExperimentKey) => experimentConfigsMap[id],
isEnabled: (experimentsConfig: Experiments, id: ExperimentId) =>
experimentsConfig[id] ?? experimentDefault[id],
} as const;
When experiments.isEnabled() is called, it returns the user's stored preference if available, or falls back to the default configuration. This boolean determines whether gated features execute or return early with error messages.
Real-World Implementation Examples
The Roo Code extension demonstrates consistent patterns for gating functionality across different tool types.
Gating UI Behavior: The Prevent Focus Disruption Flag
In src/core/tools/WriteToFileTool.ts (lines 1006-1010), the PREVENT_FOCUS_DISRUPTION flag controls how diff views are presented:
const isPreventFocusDisruptionEnabled = experiments.isEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.PREVENT_FOCUS_DISRUPTION,
);
When this flag returns true, the tool gathers a unified diff and waits for explicit user approval before writing files, preventing accidental focus switches. When false, it uses the original live-edit workflow that opens diff views immediately.
Blocking Entire Tools: Image Generation and Slash Commands
Some features require complete tool suppression when disabled. In src/core/tools/GenerateImageTool.ts (lines 15-30), the IMAGE_GENERATION flag blocks execution and surfaces an error message when disabled. Similarly, src/core/tools/RunSlashCommandTool.ts (lines 4-30) validates the RUN_SLASH_COMMAND flag before executing commands, returning errors when the feature is disabled.
This pattern follows the same check structure demonstrated in WriteToFileTool.ts, where experiments.isEnabled() determines whether the tool proceeds or returns early.
Validating Custom Tool Registration
The CUSTOM_TOOLS flag in src/core/tools/validateToolUse.ts (lines 6-20) prevents registration of user-defined tools unless explicitly enabled. Tools call experiments.isEnabled(state?.experiments ?? {}, EXPERIMENT_IDS.CUSTOM_TOOLS) and throw descriptive errors when the flag is false, ensuring experimental capabilities remain inaccessible until users opt in.
Persisting User Preferences in VS Code State
User overrides for experiment flags are stored in VS Code's global state under the key "experiments". During initialization in src/core/webview/webviewMessageHandler.ts (line 736), the extension merges persisted settings with defaults:
...(getGlobalState("experiments") ?? experimentDefault),
Users toggle flags through the Experimental Settings UI, which writes updates back to global state. Subsequent tool invocations read these values via the state?.experiments object passed to isEnabled() checks.
Summary
- Roo Code defines experiment flags centrally in
src/shared/experiments.tsusingEXPERIMENT_IDSandexperimentConfigsMap, with all features disabled by default for safety. - Runtime checks use
experiments.isEnabled(), which merges user state from VS Code's global storage with fallback defaults to return a boolean. - Tools gate behavior conditionally by checking flags at execution time, either modifying UI workflows in
WriteToFileTool.tsor blocking entire features inGenerateImageTool.tsandvalidateToolUse.ts. - User preferences persist across sessions via the global state management in
webviewMessageHandler.ts, allowing gradual feature rollouts without code changes.
Frequently Asked Questions
How do I enable an experiment flag in Roo Code?
Navigate to the Experimental Settings section in the Roo Code extension UI and toggle the desired flag. The extension writes this configuration to VS Code's global state under the "experiments" key, which is then merged with experimentDefault values on the next execution.
What happens if an experiment flag is not defined in user settings?
When a flag is missing from user state, experiments.isEnabled() automatically falls back to the value in experimentDefault (defined in src/shared/experiments.ts). Since Roo Code initializes all flags to false by default, undefined features remain safely disabled until explicitly activated.
Where are experiment flags defined in the codebase?
All flag definitions, default configurations, and the isEnabled helper function are located in src/shared/experiments.ts. Individual tool implementations (such as WriteToFileTool.ts, GenerateImageTool.ts, and validateToolUse.ts) import these definitions to perform runtime checks.
Can experiment flags be used for A/B testing in Roo Code?
Yes, the architecture supports A/B testing scenarios. Developers can modify the default values in experimentConfigsMap for different user segments, or implement custom logic in webviewMessageHandler.ts to assign flag values based on user IDs or random distribution, then analyze tool usage patterns through standard telemetry.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →