@@ -63,11 +63,12 @@ export class CollaborationState {
6363 return [ ] ;
6464 }
6565
66- const activeCollaborators = this . cacheHashToCollaborators ( cacheValue ) ;
67- const [ expired , stillActive ] = this . splitToExpiredAndStillActive ( activeCollaborators ) ;
66+ const { valid , invalid } = this . parseCacheHashToCollaborators ( cacheValue ) ;
67+ const [ expired , stillActive ] = this . splitToExpiredAndStillActive ( valid ) ;
6868
69- if ( expired . length > 0 ) {
70- void this . removeExpiredCollaborators ( workflowId , expired ) ;
69+ const toRemove = [ ...expired , ...invalid ] ;
70+ if ( toRemove . length > 0 ) {
71+ void this . removeExpiredCollaborators ( workflowId , toRemove ) ;
7172 }
7273
7374 // Deduplicate by userId - keep the most recent entry for each user
@@ -113,15 +114,36 @@ export class CollaborationState {
113114 ) ;
114115 }
115116
116- private cacheHashToCollaborators ( workflowCacheEntry : WorkflowCacheHash ) : CacheEntry [ ] {
117- return Object . entries ( workflowCacheEntry ) . map ( ( [ clientId , value ] ) => {
118- const [ userId , lastSeen ] = value . split ( '|' ) ;
119- return {
120- userId,
121- lastSeen,
122- clientId,
123- } ;
124- } ) ;
117+ private parseCacheHashToCollaborators ( workflowCacheEntry : WorkflowCacheHash ) : {
118+ valid : CacheEntry [ ] ;
119+ invalid : CacheEntry [ ] ;
120+ } {
121+ const valid : CacheEntry [ ] = [ ] ;
122+ const invalid : CacheEntry [ ] = [ ] ;
123+
124+ for ( const [ clientId , value ] of Object . entries ( workflowCacheEntry ) ) {
125+ const parts = value . split ( '|' ) ;
126+
127+ // Handle old format (pre-tab-scoped collaboration) where value was just a timestamp
128+ // Old: { "userId": "2026-02-26T21:23:36.318Z" }
129+ // New: { "clientId": "userId|2026-02-26T21:23:36.318Z" }
130+ if ( parts . length === 1 ) {
131+ invalid . push ( {
132+ clientId,
133+ userId : '' , // Not needed for deletion
134+ lastSeen : value ,
135+ } ) ;
136+ } else {
137+ const [ userId , lastSeen ] = parts ;
138+ valid . push ( {
139+ userId,
140+ lastSeen,
141+ clientId,
142+ } ) ;
143+ }
144+ }
145+
146+ return { valid, invalid } ;
125147 }
126148
127149 private hasSessionExpired ( lastSeenString : Iso8601DateTimeString ) {
0 commit comments