fix(notifications): admit reconnect repair replay

This commit is contained in:
Jinwoo-H
2026-08-23 20:07:02 -04:00
parent 0b3fbeb8aa
commit 4971a425a0
2 changed files with 49 additions and 0 deletions
@@ -371,6 +371,48 @@ describe('paired session-tab notification receipt ordering', () => {
}
)
it('repairs an exact reconnect replay without dispatching it again', async () => {
const hook = renderHook(() => useWebSessionTabsSync())
await act(settle)
const global = findSubscription('session.tabs.subscribeAll')
await publish(global, { type: 'updated', ...snapshot(1, 'working', NOW) })
await publish(global, { type: 'updated', ...snapshot(2, 'waiting', NOW + 1_000) })
vi.advanceTimersByTime(1_500)
notificationDispatch.mockClear()
useAppStore.setState({
runtimeStatusByEnvironmentId: new Map([
[ENVIRONMENT_ID, { status: null, connectionGeneration: 1 }]
]) as AppState['runtimeStatusByEnvironmentId'],
agentStatusByPaneKey: {}
})
mocks.runtimeSessionMirrorEnvironmentKey.mockReturnValue('')
hook.rerender()
await act(settle)
runtimeCall.mockResolvedValueOnce({
id: 'same-version-list-all-replay',
ok: true,
result: { snapshots: [snapshot(2, 'waiting', NOW + 1_000)] },
_meta: { runtimeId: RUNTIME_ID }
})
useAppStore.setState({
runtimeStatusByEnvironmentId: new Map([
[ENVIRONMENT_ID, { status: { runtimeId: RUNTIME_ID }, connectionGeneration: 2 }]
]) as AppState['runtimeStatusByEnvironmentId']
})
mocks.runtimeSessionMirrorEnvironmentKey.mockReturnValue(RECONNECTED_MIRROR_KEY)
hook.rerender()
await act(settle)
vi.advanceTimersByTime(1_500)
expect(useAppStore.getState().agentStatusByPaneKey[PANE_KEY]).toMatchObject({
state: 'waiting'
})
expect(notificationDispatch).not.toHaveBeenCalled()
hook.unmount()
})
it('keeps newer inventory eligibility when older recovery finishes last', async () => {
const hook = renderHook(() => useWebSessionTabsSync())
await act(settle)
@@ -595,6 +595,12 @@ export function acceptReplayedWebSessionTabsSnapshot(
}
}
function acceptReplayedWebSessionTabsEnvironment(environmentId: string): void {
for (const { worktree } of getTrackedWebSessionTabsWorktrees(environmentId)) {
acceptReplayedWebSessionTabsSnapshot(environmentId, worktree)
}
}
/**
* A frame's fate, paired with whether that fate is host evidence for the
* worktree — the mirror latch reads the pair, never a bare boolean.
@@ -4401,6 +4407,7 @@ export function useWebSessionTabsSync(): void {
if (currentOwnerRevisions.get(environmentId) !== previousRevision) {
clearWebSessionTabsTrackingForEnvironment(environmentId)
} else if (!mirroredEnvironmentOwnerRevisions.has(environmentId)) {
acceptReplayedWebSessionTabsEnvironment(environmentId)
mirroredEnvironmentOwnerRevisions.set(environmentId, previousRevision)
}
}