@@ -145,11 +145,8 @@ export class GitStatusStore {
145145 return ;
146146 }
147147
148- // Group workspaces by project for fetch management
149- const projectGroups = this . groupWorkspacesByProject ( this . workspaceMetadata ) ;
150-
151- // Try to fetch one project per cycle (background, non-blocking)
152- this . tryFetchNextProject ( projectGroups ) ;
148+ // Try to fetch workspaces that need it (background, non-blocking)
149+ this . tryFetchWorkspaces ( this . workspaceMetadata ) ;
153150
154151 // Query git status for each workspace
155152 // Rate limit: Process in batches to prevent bash process explosion
@@ -256,59 +253,52 @@ export class GitStatusStore {
256253 }
257254
258255 /**
259- * Group workspaces by project name.
256+ * Get a unique fetch key for a workspace.
257+ * For local workspaces: project name (shared git repo)
258+ * For SSH workspaces: workspace ID (each has its own git repo)
260259 */
261- private groupWorkspacesByProject (
262- metadata : Map < string , FrontendWorkspaceMetadata >
263- ) : Map < string , FrontendWorkspaceMetadata [ ] > {
264- const groups = new Map < string , FrontendWorkspaceMetadata [ ] > ( ) ;
265-
266- for ( const m of metadata . values ( ) ) {
267- const projectName = m . projectName ;
268-
269- if ( ! groups . has ( projectName ) ) {
270- groups . set ( projectName , [ ] ) ;
271- }
272- groups . get ( projectName ) ! . push ( m ) ;
273- }
274-
275- return groups ;
260+ private getFetchKey ( metadata : FrontendWorkspaceMetadata ) : string {
261+ const isSSH = metadata . runtimeConfig ?. type === "ssh" ;
262+ return isSSH ? metadata . id : metadata . projectName ;
276263 }
277264
278265 /**
279- * Try to fetch the project that needs it most urgently.
266+ * Try to fetch workspaces that need it most urgently.
267+ * For SSH workspaces: each workspace has its own repo, so fetch each one.
268+ * For local workspaces: workspaces share a repo, so fetch once per project.
280269 */
281- private tryFetchNextProject ( projectGroups : Map < string , FrontendWorkspaceMetadata [ ] > ) : void {
282- let targetProject : string | null = null ;
270+ private tryFetchWorkspaces ( workspaces : Map < string , FrontendWorkspaceMetadata > ) : void {
271+ // Find the workspace that needs fetching most urgently
272+ let targetFetchKey : string | null = null ;
283273 let targetWorkspaceId : string | null = null ;
284274 let oldestTime = Date . now ( ) ;
285275
286- for ( const [ projectName , workspaces ] of projectGroups ) {
287- if ( workspaces . length === 0 ) continue ;
276+ for ( const metadata of workspaces . values ( ) ) {
277+ const fetchKey = this . getFetchKey ( metadata ) ;
288278
289- if ( this . shouldFetch ( projectName ) ) {
290- const cache = this . fetchCache . get ( projectName ) ;
279+ if ( this . shouldFetch ( fetchKey ) ) {
280+ const cache = this . fetchCache . get ( fetchKey ) ;
291281 const lastFetch = cache ?. lastFetch ?? 0 ;
292282
293283 if ( lastFetch < oldestTime ) {
294284 oldestTime = lastFetch ;
295- targetProject = projectName ;
296- targetWorkspaceId = workspaces [ 0 ] . id ;
285+ targetFetchKey = fetchKey ;
286+ targetWorkspaceId = metadata . id ;
297287 }
298288 }
299289 }
300290
301- if ( targetProject && targetWorkspaceId ) {
291+ if ( targetFetchKey && targetWorkspaceId ) {
302292 // Fetch in background (don't await - don't block status checks)
303- void this . fetchProject ( targetProject , targetWorkspaceId ) ;
293+ void this . fetchWorkspace ( targetFetchKey , targetWorkspaceId ) ;
304294 }
305295 }
306296
307297 /**
308- * Check if project should be fetched.
298+ * Check if a workspace/ project should be fetched.
309299 */
310- private shouldFetch ( projectName : string ) : boolean {
311- const cached = this . fetchCache . get ( projectName ) ;
300+ private shouldFetch ( fetchKey : string ) : boolean {
301+ const cached = this . fetchCache . get ( fetchKey ) ;
312302 if ( ! cached ) return true ;
313303 if ( cached . inProgress ) return false ;
314304
@@ -321,15 +311,17 @@ export class GitStatusStore {
321311 }
322312
323313 /**
324- * Fetch updates for a project (one workspace is sufficient).
314+ * Fetch updates for a workspace.
315+ * For local workspaces: fetches the shared project repo.
316+ * For SSH workspaces: fetches the workspace's individual repo.
325317 */
326- private async fetchProject ( projectName : string , workspaceId : string ) : Promise < void > {
318+ private async fetchWorkspace ( fetchKey : string , workspaceId : string ) : Promise < void > {
327319 // Defensive: Return early if window.api is unavailable (e.g., test environment)
328320 if ( typeof window === "undefined" || ! window . api ) {
329321 return ;
330322 }
331323
332- const cache = this . fetchCache . get ( projectName ) ?? {
324+ const cache = this . fetchCache . get ( fetchKey ) ?? {
333325 lastFetch : 0 ,
334326 inProgress : false ,
335327 consecutiveFailures : 0 ,
@@ -338,7 +330,7 @@ export class GitStatusStore {
338330 if ( cache . inProgress ) return ;
339331
340332 // Mark as in progress
341- this . fetchCache . set ( projectName , { ...cache , inProgress : true } ) ;
333+ this . fetchCache . set ( fetchKey , { ...cache , inProgress : true } ) ;
342334
343335 try {
344336 const result = await window . api . workspace . executeBash ( workspaceId , GIT_FETCH_SCRIPT , {
@@ -355,15 +347,15 @@ export class GitStatusStore {
355347 }
356348
357349 // Success - reset failure counter
358- console . debug ( `[fetch] Success for ${ projectName } ` ) ;
359- this . fetchCache . set ( projectName , {
350+ console . debug ( `[fetch] Success for ${ fetchKey } ` ) ;
351+ this . fetchCache . set ( fetchKey , {
360352 lastFetch : Date . now ( ) ,
361353 inProgress : false ,
362354 consecutiveFailures : 0 ,
363355 } ) ;
364356 } catch ( error ) {
365357 // All errors logged to console, never shown to user
366- console . debug ( `[fetch] Failed for ${ projectName } :` , error ) ;
358+ console . debug ( `[fetch] Failed for ${ fetchKey } :` , error ) ;
367359
368360 const newFailures = cache . consecutiveFailures + 1 ;
369361 const nextDelay = Math . min (
@@ -372,11 +364,11 @@ export class GitStatusStore {
372364 ) ;
373365
374366 console . debug (
375- `[fetch] Will retry ${ projectName } after ${ Math . round ( nextDelay / 1000 ) } s ` +
367+ `[fetch] Will retry ${ fetchKey } after ${ Math . round ( nextDelay / 1000 ) } s ` +
376368 `(failure #${ newFailures } )`
377369 ) ;
378370
379- this . fetchCache . set ( projectName , {
371+ this . fetchCache . set ( fetchKey , {
380372 lastFetch : Date . now ( ) ,
381373 inProgress : false ,
382374 consecutiveFailures : newFailures ,
0 commit comments