Skip to content

Commit fba4235

Browse files
dante01yoonampagent
andcommitted
fix: close workflow transition review gaps
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb985-aceb-70a8-96f3-776224f1bf38
1 parent 5da82e0 commit fba4235

4 files changed

Lines changed: 69 additions & 14 deletions

File tree

src/platform/workflow/persistence/composables/useWorkflowPersistenceV2.test.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ import { WORKSPACE_STORAGE_KEYS } from '@/platform/workspace/workspaceConstants'
88
import { useTeamWorkspaceStore } from '@/platform/workspace/stores/teamWorkspaceStore'
99
import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore'
1010
import { StorageKeys } from '../base/storageKeys'
11-
import {
12-
prepareWorkflowWorkspaceTransition,
13-
writePayload
14-
} from '../base/storageIO'
11+
import * as storageIO from '../base/storageIO'
1512
import { useWorkflowDraftStoreV2 } from '../stores/workflowDraftStoreV2'
1613
import { useWorkflowPersistenceV2 } from './useWorkflowPersistenceV2'
1714

@@ -680,7 +677,7 @@ describe('useWorkflowPersistenceV2', () => {
680677
)
681678
expect(localStorage.getItem(sourcePayloadKey)).toBeNull()
682679

683-
const cancelTransition = prepareWorkflowWorkspaceTransition()
680+
const cancelTransition = storageIO.prepareWorkflowWorkspaceTransition()
684681
sessionStorage.setItem(
685682
WORKSPACE_STORAGE_KEYS.CURRENT_WORKSPACE,
686683
JSON.stringify({ id: destinationWorkspaceId, type: 'team' })
@@ -720,16 +717,46 @@ describe('useWorkflowPersistenceV2', () => {
720717
expect(localStorage).toHaveLength(0)
721718
expect(sessionStorage).toHaveLength(0)
722719
expect(
723-
writePayload('workspace-a', 'blocked', { data: '{}', updatedAt: 1 })
720+
storageIO.writePayload('workspace-a', 'blocked', {
721+
data: '{}',
722+
updatedAt: 1
723+
})
724724
).toBe(false)
725725

726726
onUserResolved({ id: 'user-a' })
727727

728728
expect(
729-
writePayload('workspace-a', 'resumed', { data: '{}', updatedAt: 2 })
729+
storageIO.writePayload('workspace-a', 'resumed', {
730+
data: '{}',
731+
updatedAt: 2
732+
})
730733
).toBe(true)
731734
})
732735

736+
it('cancels stale workspace-readiness watchers across authentication episodes', async () => {
737+
distributionMocks.isCloud = true
738+
featureFlagMocks.teamWorkspacesEnabled = true
739+
const completeTransitionSpy = vi.spyOn(
740+
storageIO,
741+
'completeWorkflowLogoutTransition'
742+
)
743+
mountWorkflowPersistence()
744+
745+
const onLogout = currentUserMocks.onUserLogout.mock.calls[0][0]
746+
const onUserResolved = currentUserMocks.onUserResolved.mock.calls[0][0]
747+
onLogout()
748+
onUserResolved({ id: 'user-b' })
749+
onLogout()
750+
onUserResolved({ id: 'user-c' })
751+
752+
const teamWorkspaceStore = useTeamWorkspaceStore()
753+
teamWorkspaceStore.activeWorkspaceId = 'workspace-c'
754+
teamWorkspaceStore.initState = 'ready'
755+
await nextTick()
756+
757+
expect(completeTransitionSpy).toHaveBeenCalledOnce()
758+
})
759+
733760
it('waits for workspace readiness and drops pending pre-logout edits', async () => {
734761
distributionMocks.isCloud = true
735762
featureFlagMocks.teamWorkspacesEnabled = true

src/platform/workflow/persistence/composables/useWorkflowPersistenceV2.ts

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ export function useWorkflowPersistenceV2() {
6262
const { onUserLogout, onUserResolved } = useCurrentUser()
6363
const { flags } = useFeatureFlags()
6464
const teamWorkspaceStore = useTeamWorkspaceStore()
65+
let stopWorkspaceReadinessWatcher: (() => void) | undefined
66+
67+
function stopPendingWorkspaceReadinessWatcher(): void {
68+
stopWorkspaceReadinessWatcher?.()
69+
stopWorkspaceReadinessWatcher = undefined
70+
}
6571

6672
// Run migration on module load, passing clientId for tab state migration
6773
migrateV1toV2(undefined, api.clientId ?? api.initialClientId ?? undefined)
@@ -139,23 +145,34 @@ export function useWorkflowPersistenceV2() {
139145

140146
onUserLogout(() => {
141147
if (!isCloud) return
148+
stopPendingWorkspaceReadinessWatcher()
142149
debouncedPersist.cancel()
143150
prepareWorkflowLogoutTransition()
144151
clearAllWorkflowStorage()
145152
})
146153
onUserResolved(() => {
147154
if (!isCloud) return
155+
stopPendingWorkspaceReadinessWatcher()
148156
if (!flags.teamWorkspacesEnabled) {
149157
completeWorkflowLogoutTransition()
150158
return
151159
}
152160

153-
whenever(
154-
() =>
155-
teamWorkspaceStore.initState === 'ready' &&
156-
teamWorkspaceStore.activeWorkspaceId !== null,
157-
completeWorkflowLogoutTransition,
158-
{ immediate: true, once: true }
161+
const isWorkspaceReady = () =>
162+
teamWorkspaceStore.initState === 'ready' &&
163+
teamWorkspaceStore.activeWorkspaceId !== null
164+
if (isWorkspaceReady()) {
165+
completeWorkflowLogoutTransition()
166+
return
167+
}
168+
169+
stopWorkspaceReadinessWatcher = whenever(
170+
isWorkspaceReady,
171+
() => {
172+
stopWorkspaceReadinessWatcher = undefined
173+
completeWorkflowLogoutTransition()
174+
},
175+
{ once: true }
159176
)
160177
})
161178

@@ -282,6 +299,7 @@ export function useWorkflowPersistenceV2() {
282299
api.removeEventListener('graphChanged', debouncedPersist)
283300
unregisterPersistenceFlush()
284301
debouncedPersist.cancel()
302+
stopPendingWorkspaceReadinessWatcher()
285303
})
286304

287305
// Restore workflow tabs states

src/platform/workspace/stores/useWorkspaceAuth.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,6 +1200,10 @@ describe('useWorkspaceAuthStore', () => {
12001200
const store = useWorkspaceAuthStore()
12011201
const { currentWorkspace, workspaceToken } = storeToRefs(store)
12021202
let workspaceWhenRevocationHandled: string | null = null
1203+
const cancelWorkflowTransition = vi.fn()
1204+
mockPrepareWorkflowWorkspaceTransition.mockReturnValue(
1205+
cancelWorkflowTransition
1206+
)
12031207
mockForgetRevokedActiveWorkspace.mockImplementation(() => {
12041208
workspaceWhenRevocationHandled = sessionStorage.getItem(
12051209
WORKSPACE_STORAGE_KEYS.CURRENT_WORKSPACE
@@ -1222,6 +1226,8 @@ describe('useWorkspaceAuthStore', () => {
12221226
expect(workspaceWhenRevocationHandled).toBe(
12231227
JSON.stringify(mockWorkspaceWithRole)
12241228
)
1229+
expect(cancelWorkflowTransition).toHaveBeenCalledOnce()
1230+
expect(mockReload).not.toHaveBeenCalled()
12251231
})
12261232
})
12271233

src/platform/workspace/stores/workspaceAuthStore.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,9 @@ export const useWorkspaceAuthStore = defineStore('workspaceAuth', () => {
981981

982982
function endWorkspaceSession(revokedWorkspaceId?: string): void {
983983
const hadContext = currentWorkspace.value !== null
984-
if (hadContext) prepareWorkflowWorkspaceTransition()
984+
const cancelWorkflowTransition = hadContext
985+
? prepareWorkflowWorkspaceTransition()
986+
: undefined
985987
const revokedWorkspaceHandled = revokedWorkspaceId
986988
? useTeamWorkspaceStore().forgetRevokedActiveWorkspace(revokedWorkspaceId)
987989
: false
@@ -991,7 +993,9 @@ export const useWorkspaceAuthStore = defineStore('workspaceAuth', () => {
991993
: hadContext
992994
if (shouldReload) {
993995
window.location.reload()
996+
return
994997
}
998+
cancelWorkflowTransition?.()
995999
}
9961000

9971001
return {

0 commit comments

Comments
 (0)