From 47fc6a4606f86fa5cd0ef11672dbc593e1eda865 Mon Sep 17 00:00:00 2001 From: Isaac Sanchez-Hawkins <266845420+isanchez404@users.noreply.github.com> Date: Fri, 8 May 2026 08:12:08 -0400 Subject: [PATCH] fix(sync): compare retry status metadata (#1141) * fix(sync): compare retry status metadata * fix(sync): compare status fields directly --------- Co-authored-by: Isaac Sanchez --- .../src/sync/__tests__/live-aggregate.test.js | 15 +++++++++++++++ packages/ui/src/sync/live-aggregate.ts | 17 +++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/sync/__tests__/live-aggregate.test.js b/packages/ui/src/sync/__tests__/live-aggregate.test.js index 2ba1a1d1..88f80c73 100644 --- a/packages/ui/src/sync/__tests__/live-aggregate.test.js +++ b/packages/ui/src/sync/__tests__/live-aggregate.test.js @@ -3,6 +3,7 @@ import { describe, expect, it } from 'bun:test' import { aggregateLiveSessions, aggregateLiveSessionStatuses, + areStatusMapsEquivalent, findLiveSession, findLiveSessionStatus, } from '../live-aggregate.ts' @@ -79,6 +80,20 @@ describe('live aggregate', () => { expect(findLiveSessionStatus(states, 'ses-1')?.type).toBe('idle') }) + it('detects retry metadata changes in status maps', () => { + const retryStatus = { type: 'retry', message: 'retrying|server|message', attempt: 1, next: 100 } + + expect(areStatusMapsEquivalent( + { 'ses-1': retryStatus }, + { 'ses-1': { ...retryStatus } }, + )).toBe(true) + + expect(areStatusMapsEquivalent( + { 'ses-1': retryStatus }, + { 'ses-1': { ...retryStatus, attempt: 2, next: 200 } }, + )).toBe(false) + }) + it('derives active-now sessions from live statuses instead of persisted history', () => { const sessions = [ session('ses-1', '/a', 20), diff --git a/packages/ui/src/sync/live-aggregate.ts b/packages/ui/src/sync/live-aggregate.ts index 50358286..77535e4a 100644 --- a/packages/ui/src/sync/live-aggregate.ts +++ b/packages/ui/src/sync/live-aggregate.ts @@ -47,6 +47,18 @@ const getStatusMessage = (status: SessionStatus | undefined): string | null => { return typeof message === 'string' ? message : null } +const getStatusNumberField = (status: SessionStatus | undefined, field: 'attempt' | 'next'): number | null => { + const value = (status as Record | undefined)?.[field] + return typeof value === 'number' ? value : null +} + +const areStatusesEquivalent = (left: SessionStatus | undefined, right: SessionStatus | undefined): boolean => { + return left?.type === right?.type + && getStatusMessage(left) === getStatusMessage(right) + && getStatusNumberField(left, 'attempt') === getStatusNumberField(right, 'attempt') + && getStatusNumberField(left, 'next') === getStatusNumberField(right, 'next') +} + type StatusCandidate = { status: SessionStatus sessionUpdatedAt: number @@ -114,10 +126,7 @@ export const areStatusMapsEquivalent = ( } const leftStatus = left[key] const rightStatus = right[key] - if (leftStatus?.type !== rightStatus?.type) { - return false - } - if (getStatusMessage(leftStatus) !== getStatusMessage(rightStatus)) { + if (!areStatusesEquivalent(leftStatus, rightStatus)) { return false } }