@@ -38,10 +38,10 @@ export const experimentIds = [
3838 " concurrentFileReads" ,
3939
4040 // Internal feature flags (prefix with underscore for clarity)
41+ " _nightlyTestBanner" ,
4142 " _improvedFileReader" ,
4243 " _asyncToolExecution" ,
4344 " _enhancedDiffStrategy" ,
44- " _experimentalCaching" ,
4545] as const
4646```
4747
@@ -58,10 +58,8 @@ export const EXPERIMENT_IDS = {
5858 CONCURRENT_FILE_READS: " concurrentFileReads" ,
5959
6060 // Internal flags (use underscore prefix)
61- _IMPROVED_FILE_READER: " _improvedFileReader" ,
62- _ASYNC_TOOL_EXECUTION: " _asyncToolExecution" ,
63- _ENHANCED_DIFF_STRATEGY: " _enhancedDiffStrategy" ,
64- _EXPERIMENTAL_CACHING: " _experimentalCaching" ,
61+ _NIGHTLY_TEST_BANNER: " _nightlyTestBanner" ,
62+ // Add more internal flags as needed
6563} as const satisfies Record <string , ExperimentId >
6664
6765interface ExperimentConfig {
@@ -77,30 +75,24 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
7775 CONCURRENT_FILE_READS: { enabled: false },
7876
7977 // Internal flags
80- _IMPROVED_FILE_READER: {
81- enabled: false ,
82- internal: true ,
83- nightlyDefault: true ,
84- description: " Internal: Optimized file reading with streaming and caching" ,
85- },
86- _ASYNC_TOOL_EXECUTION: {
78+ _NIGHTLY_TEST_BANNER: {
8779 enabled: false ,
88- internal: true ,
80+ internal: false , // Currently visible to users
8981 nightlyDefault: true ,
90- description: " Internal: Parallel tool execution for better performance " ,
82+ description: " Internal: Shows a test banner in nightly builds " ,
9183 },
92- // ... other internal flags
84+ // Add more internal flags as needed
9385}
9486```
9587
96- ### 3. Nightly Build Configuration
88+ ### 3. Nightly Build Detection
9789
98- Create a nightly defaults system :
90+ The nightly build detection is implemented as follows :
9991
10092``` typescript
10193// src/shared/experiments.ts
10294export function getExperimentDefaults(isNightly : boolean = false ): Record <ExperimentId , boolean > {
103- const defaults: Record <ExperimentId , boolean > = {}
95+ const defaults: Record <ExperimentId , boolean > = {} as Record < ExperimentId , boolean >
10496
10597 Object .entries (experimentConfigsMap ).forEach (([key , config ]) => {
10698 const experimentId = EXPERIMENT_IDS [key as keyof typeof EXPERIMENT_IDS ]
@@ -117,9 +109,9 @@ export function getExperimentDefaults(isNightly: boolean = false): Record<Experi
117109
118110// Check if running nightly build
119111export function isNightlyBuild(): boolean {
120- // Check package name from extension context
121- const extensionId = vscode . extensions . getExtension ( " RooVeterinaryInc.roo-cline " )?. id
122- return extensionId ?. includes ( " nightly" ) ?? false
112+ // The nightly build process defines PKG_NAME as "roo-code-nightly" at compile time
113+ // This is the most reliable single indicator for nightly builds
114+ return process . env . PKG_NAME === " roo-code- nightly"
123115}
124116
125117// Update experimentDefault to use nightly defaults when appropriate
@@ -128,27 +120,25 @@ export const experimentDefault = getExperimentDefaults(isNightlyBuild())
128120
129121### 4. Hide Internal Flags from UI
130122
131- Modify the settings UI to hide internal flags :
123+ To hide internal flags from the settings UI, filter experiments that start with underscore :
132124
133125``` typescript
134126// webview-ui/src/components/settings/ExperimentalSettings.tsx
135127<Section >
136128 {Object .entries (experimentConfigsMap )
137- .filter ((config ) => {
129+ .filter (([ key , config ] ) => {
138130 // Filter out internal experiments (those starting with underscore)
139- const experimentId = EXPERIMENT_IDS [config [ 0 ] as keyof typeof EXPERIMENT_IDS ]
131+ const experimentId = EXPERIMENT_IDS [key as keyof typeof EXPERIMENT_IDS ]
140132 return ! experimentId .startsWith (' _' )
141133 })
142- .map ((config ) => {
143- // Render only user-facing experiments
134+ .map (([ key , config ] ) => {
135+ const experimentId = EXPERIMENT_IDS [ key as keyof typeof EXPERIMENT_IDS ]
144136 return (
145137 < ExperimentalFeature
146- key = {config[0 ]}
147- experimentKey={config[0 ]}
148- enabled={experiments[EXPERIMENT_IDS [config [0 ] as keyof typeof EXPERIMENT_IDS ]] ?? false}
149- onChange={(enabled) =>
150- setExperimentEnabled(EXPERIMENT_IDS[config [0 ] as keyof typeof EXPERIMENT_IDS ], enabled)
151- }
138+ key = {key }
139+ experimentKey = {key }
140+ enabled = {experiments[experimentId ] ?? false}
141+ onChange={(enabled) => setExperimentEnabled(experimentId, enabled)}
152142 />
153143 )
154144 })}
@@ -159,59 +149,48 @@ Modify the settings UI to hide internal flags:
159149
160150### 1. Nightly Build Workflow
161151
162- Update `.github/workflows/nightly-publish.yml`:
152+ The nightly build workflow ( `.github/workflows/nightly-publish.yml`) includes :
163153
164154` ` ` yaml
165- - name: Enable Internal Feature Flags
155+ - name: Log Internal Experiments
166156 run: |
167- # Set environment variable for nightly build
168- echo "ROO_CODE_NIGHTLY=true" >> $GITHUB_ENV
169-
170157 # Log enabled experiments
171- node scripts/log-experiments.js --nightly
158+ node scripts/log-experiments.js
172159` ` `
173160
174- ### 2. Validation Script ( Optional )
161+ ### 2. Build Configuration
175162
176- Create ` scripts/validate-internal-flags.js ` :
163+ The nightly build process sets the PKG_NAME environment variable in ` apps/vscode-nightly/esbuild.mjs ` :
177164
178165` ` ` javascript
179- #!/usr/bin/env node
180-
181- const { EXPERIMENT_IDS, experimentConfigsMap } = require("../src/shared/experiments")
182-
183- // Validate internal flags
184- const internalFlags = Object.entries(experimentConfigsMap)
185- .filter(([_, config]) => config.internal)
186- .map(([key, config]) => ({
187- key,
188- id: EXPERIMENT_IDS[key],
189- ...config,
190- }))
191-
192- console.log( ` Found $ {internalFlags.length } internal feature flags :` )
193- internalFlags.forEach(({ id, nightlyDefault }) => {
194- console.log( ` - $ {id }: nightlyDefault = $ {nightlyDefault }` )
195- })
196-
197- // Ensure internal flags are prefixed
198- const invalidFlags = internalFlags.filter(({ id }) => !id.startsWith("_"))
199- if (invalidFlags.length > 0) {
200- console.error("❌ Internal flags must start with underscore:")
201- invalidFlags.forEach(({ id }) => console.error( ` - $ {id }` ))
202- process.exit(1)
166+ define: {
167+ "process.env.PKG_NAME": '"roo-code-nightly"',
168+ "process.env.PKG_VERSION": ` " ${overrideJson.version}" ` ,
169+ "process.env.PKG_OUTPUT_CHANNEL": '"Roo-Code-Nightly"',
170+ // ... other defines
203171}
204-
205- console.log("✅ All internal flags are properly configured")
206172` ` `
207173
208- ### 3. Gradual Rollout Process
174+ ### 3. Experiment Logging Script
175+
176+ The ` scripts/log-experiments.js ` script :
177+
178+ - Checks if running in nightly mode via ` process.env.PKG_NAME === "roo-code-nightly" `
179+ - Parses the experiments configuration from the TypeScript source
180+ - Logs which experiments are enabled based on build type
181+ - Separates user - facing and internal experiments in the output
182+
183+ ### 4. Gradual Rollout Process
184+
185+ For managing the lifecycle of internal flags , you can extend the configuration:
209186
210187` ` ` typescript
211188// src/shared/experiments.ts
212- export interface InternalExperimentConfig extends ExperimentConfig {
213- internal: boolean
214- nightlyDefault: boolean
189+ interface ExperimentConfig {
190+ enabled: boolean
191+ internal?: boolean
192+ nightlyDefault?: boolean
193+ description?: string
215194 stableRolloutDate?: string // When to enable in stable
216195 removalDate?: string // When to remove the flag
217196}
@@ -496,21 +475,14 @@ export class MultiSearchReplaceDiffStrategy {
496475
497476` ` ` typescript
498477// packages/types/src/experiment.ts
499- import { z } from "zod"
500-
501478export const experimentIds = [
502479 "powerSteering",
503480 "concurrentFileReads",
504481 // Add new internal flag
505482 "_enhancedDiffStrategy",
506483] as const
507484
508- export const experimentsSchema = z.object({
509- powerSteering: z.boolean(),
510- concurrentFileReads: z.boolean(),
511- // Add schema for new flag
512- _enhancedDiffStrategy: z.boolean(),
513- })
485+ export type ExperimentId = (typeof experimentIds)[number]
514486` ` `
515487
516488### Step 2 : Update Experiments Configuration
@@ -545,25 +517,29 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
545517// webview-ui/src/components/settings/ExperimentalSettings.tsx
546518import { experimentConfigsMap, EXPERIMENT_IDS } from "../../../src/shared/experiments"
547519
548- <Section>
549- {Object.entries(experimentConfigsMap)
550- .filter(([key, config]) => {
551- // Filter out internal experiments
552- const experimentId = EXPERIMENT_IDS[key as keyof typeof EXPERIMENT_IDS]
553- return !experimentId.startsWith('_')
554- })
555- .map(([key, config]) => {
556- const experimentId = EXPERIMENT_IDS[key as keyof typeof EXPERIMENT_IDS]
557- return (
558- <ExperimentalFeature
559- key={key}
560- experimentKey={key}
561- enabled={experiments[experimentId] ?? false}
562- onChange={(enabled) => setExperimentEnabled(experimentId, enabled)}
563- />
564- )
565- })}
566- </Section>
520+ export function ExperimentalSettings({ experiments, setExperimentEnabled }) {
521+ return (
522+ <Section>
523+ {Object.entries(experimentConfigsMap)
524+ .filter(([key, config]) => {
525+ // Filter out internal experiments
526+ const experimentId = EXPERIMENT_IDS[key as keyof typeof EXPERIMENT_IDS]
527+ return !experimentId.startsWith('_')
528+ })
529+ .map(([key, config]) => {
530+ const experimentId = EXPERIMENT_IDS[key as keyof typeof EXPERIMENT_IDS]
531+ return (
532+ <ExperimentalFeature
533+ key={key}
534+ experimentKey={key}
535+ enabled={experiments[experimentId] ?? false}
536+ onChange={(enabled) => setExperimentEnabled(experimentId, enabled)}
537+ />
538+ )
539+ })}
540+ </Section>
541+ )
542+ }
567543` ` `
568544
569545### Step 4 : Use the Internal Flag in Code
@@ -735,12 +711,28 @@ describe("Apply Diff with internal flags", () => {
735711
736712### Manual Testing in Development
737713
714+ To test internal flags during development :
715+
716+ 1. ** Temporarily enable in code ** :
717+
718+ ` ` ` typescript
719+ // For testing, temporarily modify the config
720+ experimentConfigsMap._ENHANCED_DIFF_STRATEGY.enabled = true
721+ ` ` `
722+
723+ 2. ** Use environment variable ** :
724+
725+ ` ` ` bash
726+ # Set PKG_NAME to simulate nightly build
727+ PKG_NAME=roo-code-nightly npm run dev
728+ ` ` `
729+
730+ 3. ** Modify the isNightlyBuild function temporarily** :
731+
738732```typescript
739- // For testing, temporarily enable the flag in development
740- // src/extension.ts
741- if (process.env.NODE_ENV === "development") {
742- // Override specific internal flags for testing
743- experimentDefault._enhancedDiffStrategy = true
733+ export function isNightlyBuild(): boolean {
734+ // For testing only - remove before committing
735+ return true
744736}
745737` ` `
746738
0 commit comments