@@ -290,14 +290,16 @@ export async function applyInitialEnvironmentSelection(
290290 `[interpreterSelection] Applying initial environment selection for ${ folders . length } workspace folder(s)` ,
291291 ) ;
292292
293- // Checkpoint 1: env selection starting — managers are registered
294293 sendTelemetryEvent ( EventNames . ENV_SELECTION_STARTED , activationToReadyDurationMs , {
295294 registeredManagerCount : envManagers . managers . length ,
296295 registeredManagerIds : envManagers . managers . map ( ( m ) => m . id ) . join ( ',' ) ,
297296 workspaceFolderCount : folders . length ,
298297 } ) ;
299298
300299 const allErrors : SettingResolutionError [ ] = [ ] ;
300+ let workspaceFolderResolved = false ;
301+ let resolvedFolderCount = 0 ;
302+ const selectionStopWatch = new StopWatch ( ) ;
301303
302304 for ( const folder of folders ) {
303305 try {
@@ -311,23 +313,24 @@ export async function applyInitialEnvironmentSelection(
311313 ) ;
312314 allErrors . push ( ...errors ) ;
313315
314- // Checkpoint 2: priority chain resolved — which path?
315- const isPathA = result . environment !== undefined ;
316-
317- // Get the specific environment if not already resolved
318316 const env = result . environment ?? ( await result . manager . get ( folder . uri ) ) ;
319317
320318 sendTelemetryEvent ( EventNames . ENV_SELECTION_RESULT , scopeStopWatch . elapsedTime , {
321319 scope : 'workspace' ,
322320 prioritySource : result . source ,
323321 managerId : result . manager . id ,
324- resolutionPath : isPathA ? 'envPreResolved' : 'managerDiscovery' ,
322+ resolutionPath : result . environment ? 'envPreResolved' : 'managerDiscovery' ,
325323 hasPersistedSelection : env !== undefined ,
326324 } ) ;
327325
328326 // Cache only — NO settings.json write (shouldPersistSettings = false)
329327 await envManagers . setEnvironment ( folder . uri , env , false ) ;
330328
329+ if ( env ) {
330+ workspaceFolderResolved = true ;
331+ resolvedFolderCount ++ ;
332+ }
333+
331334 traceInfo (
332335 `[interpreterSelection] ${ folder . name } : ${ env ?. displayName ?? 'none' } (source: ${ result . source } )` ,
333336 ) ;
@@ -336,49 +339,94 @@ export async function applyInitialEnvironmentSelection(
336339 }
337340 }
338341
339- // Also apply initial selection for global scope (no workspace folder)
340- // This ensures defaultInterpreterPath is respected even without a workspace
341- try {
342- const globalStopWatch = new StopWatch ( ) ;
343- const { result, errors } = await resolvePriorityChainCore ( undefined , envManagers , undefined , nativeFinder , api ) ;
344- allErrors . push ( ...errors ) ;
342+ // Resolve global scope (fallback for files outside workspace folders).
343+ // Deferred to background when a workspace folder already resolved.
344+ const resolveGlobalScope = async ( ) : Promise < SettingResolutionError [ ] > => {
345+ try {
346+ const globalStopWatch = new StopWatch ( ) ;
347+ const { result, errors : globalErrors } = await resolvePriorityChainCore (
348+ undefined ,
349+ envManagers ,
350+ undefined ,
351+ nativeFinder ,
352+ api ,
353+ ) ;
345354
346- const isPathA = result . environment !== undefined ;
355+ const env = result . environment ?? ( await result . manager . get ( undefined ) ) ;
347356
348- // Get the specific environment if not already resolved
349- const env = result . environment ?? ( await result . manager . get ( undefined ) ) ;
357+ sendTelemetryEvent ( EventNames . ENV_SELECTION_RESULT , globalStopWatch . elapsedTime , {
358+ scope : 'global' ,
359+ prioritySource : result . source ,
360+ managerId : result . manager . id ,
361+ resolutionPath : result . environment ? 'envPreResolved' : 'managerDiscovery' ,
362+ hasPersistedSelection : env !== undefined ,
363+ } ) ;
350364
351- sendTelemetryEvent ( EventNames . ENV_SELECTION_RESULT , globalStopWatch . elapsedTime , {
352- scope : 'global' ,
353- prioritySource : result . source ,
354- managerId : result . manager . id ,
355- resolutionPath : isPathA ? 'envPreResolved' : 'managerDiscovery' ,
356- hasPersistedSelection : env !== undefined ,
357- } ) ;
365+ // Cache only — NO settings.json write
366+ await envManagers . setEnvironments ( 'global' , env , false ) ;
358367
359- // Cache only — NO settings.json write (shouldPersistSettings = false)
360- await envManagers . setEnvironments ( 'global' , env , false ) ;
368+ traceInfo ( `[interpreterSelection] global: ${ env ?. displayName ?? 'none' } (source: ${ result . source } )` ) ;
361369
362- traceInfo ( `[interpreterSelection] global: ${ env ?. displayName ?? 'none' } (source: ${ result . source } )` ) ;
363- } catch ( err ) {
364- traceError ( `[interpreterSelection] Failed to set global environment: ${ err } ` ) ;
370+ return globalErrors ;
371+ } catch ( err ) {
372+ traceError ( `[interpreterSelection] Failed to set global environment: ${ err } ` ) ;
373+ return [ ] ;
374+ }
375+ } ;
376+
377+ if ( workspaceFolderResolved ) {
378+ // Defer global scope so it doesn't block post-selection startup.
379+ traceInfo ( '[interpreterSelection] Workspace env resolved, deferring global scope to background' ) ;
380+ resolveGlobalScope ( )
381+ . then ( async ( globalErrors ) => {
382+ if ( globalErrors . length > 0 ) {
383+ await notifyUserOfSettingErrors ( globalErrors ) ;
384+ }
385+ } )
386+ . catch ( ( err ) => traceError ( `[interpreterSelection] Background global scope resolution failed: ${ err } ` ) ) ;
387+ } else {
388+ // No workspace folder resolved — global scope is the primary fallback, must await.
389+ const globalErrors = await resolveGlobalScope ( ) ;
390+ allErrors . push ( ...globalErrors ) ;
365391 }
366392
367- // Notify user if any settings could not be applied
393+ // Notify user if any settings could not be applied (workspace + global when awaited)
368394 if ( allErrors . length > 0 ) {
369395 await notifyUserOfSettingErrors ( allErrors ) ;
370396 }
397+
398+ // Duration measures blocking time only (excludes deferred global scope).
399+ sendTelemetryEvent ( EventNames . ENV_SELECTION_COMPLETED , selectionStopWatch . elapsedTime , {
400+ globalScopeDeferred : workspaceFolderResolved ,
401+ workspaceFolderCount : folders . length ,
402+ resolvedFolderCount,
403+ settingErrorCount : allErrors . length ,
404+ } ) ;
371405}
372406
373407/**
374408 * Notify the user when their configured settings could not be applied.
375409 * Shows a warning message with an option to open settings.
410+ * Tracks already-warned settings to avoid duplicate dialogs (e.g., when
411+ * the same user-level misconfiguration is hit by both workspace and
412+ * deferred global scope resolution).
376413 */
414+ const warnedSettings = new Set < string > ( ) ;
415+
416+ export function resetSettingWarnings ( ) : void {
417+ warnedSettings . clear ( ) ;
418+ }
419+
377420async function notifyUserOfSettingErrors ( errors : SettingResolutionError [ ] ) : Promise < void > {
378421 // Group errors by setting type to avoid spamming the user
379422 const uniqueSettings = [ ...new Set ( errors . map ( ( e ) => e . setting ) ) ] ;
380423
381424 for ( const setting of uniqueSettings ) {
425+ if ( warnedSettings . has ( setting ) ) {
426+ continue ;
427+ }
428+ warnedSettings . add ( setting ) ;
429+
382430 const settingErrors = errors . filter ( ( e ) => e . setting === setting ) ;
383431 const firstError = settingErrors [ 0 ] ;
384432
0 commit comments