diff --git a/apps/mcp-server/src/tui/dashboard-app.spec.tsx b/apps/mcp-server/src/tui/dashboard-app.spec.tsx index 2b4070da..9940ddf0 100644 --- a/apps/mcp-server/src/tui/dashboard-app.spec.tsx +++ b/apps/mcp-server/src/tui/dashboard-app.spec.tsx @@ -159,6 +159,25 @@ describe('DashboardApp', () => { expect(frame).not.toContain('/from-state'); }); + it('memoizes now based on tick to avoid non-deterministic re-renders', () => { + const dateNowSpy = vi.spyOn(Date, 'now').mockReturnValue(1700000000000); + + const state = createInitialDashboardState(); + const { lastFrame, rerender } = render(); + const frame1 = lastFrame() ?? ''; + + // Advance Date.now() by 1 minute — but tick has NOT changed (still 0 from mock) + dateNowSpy.mockReturnValue(1700000060000); + rerender(); + const frame2 = lastFrame() ?? ''; + + // With useMemo([tick]): now stays cached → frames identical + // Without useMemo: now = Date.now() changes → time display differs + expect(frame1).toBe(frame2); + + dateNowSpy.mockRestore(); + }); + it('should use externalState when provided (multi-session mode)', () => { const mockState = createInitialDashboardState(); // Modify some fields to verify they propagate diff --git a/apps/mcp-server/src/tui/dashboard-app.tsx b/apps/mcp-server/src/tui/dashboard-app.tsx index 1e849ae8..352726ff 100644 --- a/apps/mcp-server/src/tui/dashboard-app.tsx +++ b/apps/mcp-server/src/tui/dashboard-app.tsx @@ -27,7 +27,7 @@ export function DashboardApp({ }: DashboardAppProps): React.ReactElement { const { columns, rows, layoutMode } = useTerminalSize(); const tick = useTick(1000); - const now = Date.now(); + const now = useMemo(() => Date.now(), [tick]); const internalState = useDashboardState(externalState ? undefined : eventBus); const state = externalState ?? internalState; const focusedAgent = state.focusedAgentId