@@ -72,6 +72,7 @@ const CONTAINER_CANDIDATES = [process.env.HELM_CONTAINER_CLI, "/usr/local/bin/co
7272const COMMAND_TIMEOUT_MS = Math . max ( 5_000 , Number ( process . env . HELM_MACHINE_COMMAND_TIMEOUT_MS || 120_000 ) ) ;
7373const IDLE_AFTER_MS = Math . max ( 60_000 , Number ( process . env . HELM_MACHINE_IDLE_MS || 15 * 60_000 ) ) ;
7474const RECONCILE_EVERY_MS = Math . max ( 15_000 , Number ( process . env . HELM_FLEET_INTERVAL_MS || 60_000 ) ) ;
75+ const INITIAL_RECONCILE_MS = Math . max ( 25 , Number ( process . env . HELM_FLEET_INITIAL_MS || 2_000 ) ) ;
7576const UPDATE_EVERY_MS = Math . max ( 24 * 60 * 60_000 , Number ( process . env . HELM_MACHINE_UPDATE_MS || 7 * 24 * 60 * 60_000 ) ) ;
7677const UPDATE_RETRY_MS = Math . max ( 60 * 60_000 , Number ( process . env . HELM_MACHINE_UPDATE_RETRY_MS || 6 * 60 * 60_000 ) ) ;
7778const MAX_WORKSPACE_SYNC_BYTES = Math . max ( 64 * 1024 ** 2 , Number ( process . env . HELM_WORKSPACE_SYNC_MAX_BYTES || 2 * 1024 ** 3 ) ) ;
@@ -85,7 +86,10 @@ const terminalSessions = new Map<string, MachineTerminal>();
8586const channelLocks = new Map < number , Promise < unknown > > ( ) ;
8687const syncTimers = new Map < number , NodeJS . Timeout > ( ) ;
8788let reconcileTimer : NodeJS . Timeout | null = null ;
89+ let reconcileStartupTimer : NodeJS . Timeout | null = null ;
8890let reconcileRunning = false ;
91+ let reconcileEnabled = false ;
92+ let reconcilePass : Promise < void > | null = null ;
8993
9094const installationId = ( ) : string => {
9195 let id = String ( q1 ( "SELECT installation_id FROM workspace WHERE id=1" ) ?. installation_id || "" ) ;
@@ -757,6 +761,10 @@ export async function appRemovalStatus(): Promise<{ backend: ChannelComputerBack
757761export async function prepareAppRemoval ( ) : Promise < { backend : ChannelComputerBackend ; deleted : number ; remaining : number } > {
758762 const backend = configuredChannelBackend ( ) ;
759763 if ( backend !== "apple" ) return { backend, deleted : 0 , remaining : 0 } ;
764+ // Uninstall is a fleet-wide terminal state. Quiesce and fence the reconciler
765+ // before enumerating machines so an already-running pass cannot recreate a
766+ // machine from its stale pre-removal snapshot after deletion completes.
767+ await shutdownChannelComputers ( ) ;
760768 const install = installationId ( ) ;
761769 const prefix = `1helm-${ install } -channel-` ;
762770 const machineIds = await ownedInstallationMachineIds ( ) ;
@@ -1028,19 +1036,32 @@ export async function reconcileChannelComputers(channelIds?: Iterable<number>):
10281036}
10291037
10301038export function startChannelComputerReconciler ( ) : void {
1031- if ( reconcileTimer ) return ;
1039+ if ( reconcileEnabled ) return ;
1040+ reconcileEnabled = true ;
10321041 const tick = ( ) : void => {
1033- if ( reconcileRunning ) return ;
1042+ if ( ! reconcileEnabled || reconcileRunning ) return ;
10341043 reconcileRunning = true ;
1035- void reconcileChannelComputers ( ) . catch ( ( error ) => console . error ( "channel computer reconcile failed:" , ( error as Error ) . message ) ) . finally ( ( ) => { reconcileRunning = false ; } ) ;
1044+ const pass : Promise < void > = reconcileChannelComputers ( )
1045+ . then ( ( ) => undefined )
1046+ . catch ( ( error ) => console . error ( "channel computer reconcile failed:" , ( error as Error ) . message ) )
1047+ . finally ( ( ) => {
1048+ reconcileRunning = false ;
1049+ if ( reconcilePass === pass ) reconcilePass = null ;
1050+ } ) ;
1051+ reconcilePass = pass ;
1052+ void pass ;
10361053 } ;
1037- setTimeout ( tick , 2_000 ) . unref ( ) ;
1054+ reconcileStartupTimer = setTimeout ( ( ) => { reconcileStartupTimer = null ; tick ( ) ; } , INITIAL_RECONCILE_MS ) ;
1055+ reconcileStartupTimer . unref ( ) ;
10381056 reconcileTimer = setInterval ( tick , RECONCILE_EVERY_MS ) ;
10391057 reconcileTimer . unref ( ) ;
10401058}
10411059
10421060export async function shutdownChannelComputers ( ) : Promise < void > {
1061+ reconcileEnabled = false ;
1062+ if ( reconcileStartupTimer ) { clearTimeout ( reconcileStartupTimer ) ; reconcileStartupTimer = null ; }
10431063 if ( reconcileTimer ) { clearInterval ( reconcileTimer ) ; reconcileTimer = null ; }
1064+ await reconcilePass ?. catch ( ( ) => undefined ) ;
10441065 for ( const session of [ ...terminalSessions . values ( ) ] ) closeChannelTerminal ( session . id ) ;
10451066 for ( const timer of syncTimers . values ( ) ) clearTimeout ( timer ) ;
10461067 syncTimers . clear ( ) ;
0 commit comments