Change:
I don't think the state of walkthrough progress is synced between machines, so I see this on each new machine I setup: !
Again, we do try to sync the state but there may be a race where it's not available in time. I'll investigate more..
Fixed it by adding the context to the installation event when extensions are synced by settings sync. Walk through component shall simply ignore walk through for those extensions which are installed by settings sync (irrespective of the walk through state is synced or not).

Discussion:
import { AbstractInitializer, AbstractSynchroniser, getSyncResourceLogLabel, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { IMergeResult as IExtensionMergeResult, merge } from 'vs/platform/userDataSync/common/extensionsMerge'; import { IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; <del> import { Change, IRemoteUserData, ISyncData, ISyncExtension, ISyncExtensionWithVersion, IUserDataSyncBackupStoreService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync'; </del> <ins> import { Change, IRemoteUserData, ISyncData, ISyncExtension, ISyncExtensionWithVersion, IUserDataSyncBackupStoreService, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, SyncResource, USER_DATA_SYNC_SCHEME, EXTENSIONS_SYNC_CONTEXT_KEY } from 'vs/platform/userDataSync/common/userDataSync'; </ins> import { IUserDataProfileStorageService } from 'vs/platform/userDataProfile/common/userDataProfileStorageService'; type IExtensionResourceMergeResult = IAcceptResult & IExtensionMergeResult;

async updateLocalExtensions(added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], skippedExtensions: ISyncExtension[], profile: IUserDataProfile): Promise<ISyncExtension[]> { const syncResourceLogLabel = getSyncResourceLogLabel(SyncResource.Extensions, profile); <del> return this.withProfileScopedServices(profile, async (extensionEnablementService, extensionStorageService) => { const removeFromSkipped: IExtensionIdentifier[] = []; const addToSkipped: ISyncExtension[] = []; const installedExtensions = await this.extensionManagementService.getInstalled(undefined, profile.extensionsResource); if (removed.length) { const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); await Promises.settled(extensionsToRemove.map(async extensionToRemove => { this.logService.trace(`${syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true, profileLocation: profile.extensionsResource }); this.logService.info(`${syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); removeFromSkipped.push(extensionToRemove.identifier); })); } </del> <ins> const extensionsToInstall: [ISyncExtension, IGalleryExtension][] = []; const removeFromSkipped: IExtensionIdentifier[] = []; const addToSkipped: ISyncExtension[] = []; const installedExtensions = await this.extensionManagementService.getInstalled(undefined, profile.extensionsResource); </ins> <del> if (added.length || updated.length) { </del> <ins> // 1. Sync extensions state first so that the storage is flushed and updated in all opened windows if (added.length || updated.length) { await this.withProfileScopedServices(profile, async (extensionEnablementService, extensionStorageService) => { </ins> await Promises.settled([...added, ...updated].map(async e => { const installedExtension = installedExtensions.find(installed => areSameExtensions(installed.identifier, e.identifier));

|| installedExtension.preRelease !== e.preRelease // Install if the extension pre-release preference has changed ) { if (await this.extensionManagementService.canInstall(extension)) { <del> this.logService.trace(`${syncResourceLogLabel}: Installing extension...`, e.identifier.id, extension.version); await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false, donotIncludePackAndDependencies: true, installPreReleaseVersion: e.preRelease, profileLocation: profile.extensionsResource } /* set isMachineScoped value to prevent install and sync dialog in web */); this.logService.info(`${syncResourceLogLabel}: Installed extension.`, e.identifier.id, extension.version); removeFromSkipped.push(extension.identifier); </del> <ins> extensionsToInstall.push([e, extension]); </ins> } else { this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because it cannot be installed.`, extension.displayName || extension.identifier.id); addToSkipped.push(e);

} } catch (error) { addToSkipped.push(e); <del> if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) { this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id); } else { this.logService.error(error); this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id); } </del> <ins> this.logService.error(error); this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id); </ins> } } else { addToSkipped.push(e); this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the extension is not found.`, e.identifier.id); } })); <del> } </del> <ins> }); } </ins> <del> const newSkippedExtensions: ISyncExtension[] = []; for (const skippedExtension of skippedExtensions) { if (!removeFromSkipped.some(e => areSameExtensions(e, skippedExtension.identifier))) { newSkippedExtensions.push(skippedExtension); </del> <ins> // 2. Next uninstall the removed extensions if (removed.length) { const extensionsToRemove = installedExtensions.filter(({ identifier, isBuiltin }) => !isBuiltin && removed.some(r => areSameExtensions(identifier, r))); await Promises.settled(extensionsToRemove.map(async extensionToRemove => { this.logService.trace(`${syncResourceLogLabel}: Uninstalling local extension...`, extensionToRemove.identifier.id); await this.extensionManagementService.uninstall(extensionToRemove, { donotIncludePack: true, donotCheckDependents: true, profileLocation: profile.extensionsResource }); this.logService.info(`${syncResourceLogLabel}: Uninstalled local extension.`, extensionToRemove.identifier.id); removeFromSkipped.push(extensionToRemove.identifier); })); } // 3. Install extensions at the end for (const [e, extension] of extensionsToInstall) { try { this.logService.trace(`${syncResourceLogLabel}: Installing extension...`, extension.identifier.id, extension.version); await this.extensionManagementService.installFromGallery(extension, { isMachineScoped: false/* set isMachineScoped value to prevent install and sync dialog in web */, donotIncludePackAndDependencies: true, installPreReleaseVersion: e.preRelease, profileLocation: profile.extensionsResource, context: { [EXTENSIONS_SYNC_CONTEXT_KEY]: true } }); this.logService.info(`${syncResourceLogLabel}: Installed extension.`, extension.identifier.id, extension.version); removeFromSkipped.push(extension.identifier); } catch (error) { addToSkipped.push(e); if (error instanceof ExtensionManagementError && [ExtensionManagementErrorCode.Incompatible, ExtensionManagementErrorCode.IncompatiblePreRelease, ExtensionManagementErrorCode.IncompatibleTargetPlatform].includes(error.code)) { this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension because the compatible extension is not found.`, extension.displayName || extension.identifier.id); } else { this.logService.error(error); this.logService.info(`${syncResourceLogLabel}: Skipped synchronizing extension`, extension.displayName || extension.identifier.id); </ins> } } <del> for (const skippedExtension of addToSkipped) { if (!newSkippedExtensions.some(e => areSameExtensions(e.identifier, skippedExtension.identifier))) { newSkippedExtensions.push(skippedExtension); } </del> <ins> } const newSkippedExtensions: ISyncExtension[] = []; for (const skippedExtension of skippedExtensions) { if (!removeFromSkipped.some(e => areSameExtensions(e, skippedExtension.identifier))) { newSkippedExtensions.push(skippedExtension); </ins> } <del> return newSkippedExtensions; }); </del> <ins> } for (const skippedExtension of addToSkipped) { if (!newSkippedExtensions.some(e => areSameExtensions(e.identifier, skippedExtension.identifier))) { newSkippedExtensions.push(skippedExtension); } } return newSkippedExtensions; </ins> } private updateExtensionState(state: IStringDictionary<any>, extension: ILocalExtension | IGalleryExtension, version: string | undefined, extensionStorageService: IExtensionStorageService): void {

export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync'; export const PREVIEW_DIR_NAME = 'preview'; <ins> export const EXTENSIONS_SYNC_CONTEXT_KEY = 'extensionsSync'; </ins>

import { ICommandService } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, ContextKeyExpression, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { Disposable } from 'vs/base/common/lifecycle'; <del> import { IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; </del> <ins> import { EXTENSIONS_SYNC_CONTEXT_KEY, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; </ins> import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources';

this._register(this.extensionManagementService.onDidInstallExtensions(async (result) => { const hadLastFoucs = await this.hostService.hadLastFocus(); for (const e of result) { <ins> if (e.context?.[EXTENSIONS_SYNC_CONTEXT_KEY]) { continue; } </ins> // If the window had last focus and the install didn't specify to skip the walkthrough // Then add it to the sessionInstallExtensions to be opened if (hadLastFoucs && !e?.context?.skipWalkthrough) {