From 1498335e398f2cd23b8fd1133ca3ba020d9b8905 Mon Sep 17 00:00:00 2001 From: "cursor[bot]" <206951365+cursor[bot]@users.noreply.github.com> Date: Tue, 5 May 2026 20:57:33 -0700 Subject: [PATCH] Optimize MessagesTimeline work row stability (#2527) Co-authored-by: Cursor Agent Co-authored-by: Julius Marminge --- .../chat/MessagesTimeline.logic.test.ts | 54 +++++++++++++++++++ .../components/chat/MessagesTimeline.logic.ts | 3 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/chat/MessagesTimeline.logic.test.ts b/apps/web/src/components/chat/MessagesTimeline.logic.test.ts index 7dc6583b133b1aeaa67cc27c3aed41947156e168..633fb5d6bef0b73efd0735c4a0899084deb144cb 100644 --- a/apps/web/src/components/chat/MessagesTimeline.logic.test.ts +++ b/apps/web/src/components/chat/MessagesTimeline.logic.test.ts @@ -382,6 +382,60 @@ describe("computeStableMessagesTimelineRows", () => { expect(repeated.result).toBe(initial.result); }); + it("reuses work rows when equivalent timeline derivations create new grouped arrays", () => { + const firstWorkEntry = { + id: "work-1", + createdAt: "2026-01-01T00:00:00Z", + label: "thinking", + detail: "Inspecting repository state", + tone: "thinking" as const, + }; + const secondWorkEntry = { + id: "work-2", + createdAt: "2026-01-01T00:00:01Z", + label: "read", + detail: "Reading package.json", + tone: "tool" as const, + }; + + const createRows = () => + deriveMessagesTimelineRows({ + timelineEntries: [ + { + id: "entry-work-1", + kind: "work", + createdAt: firstWorkEntry.createdAt, + entry: firstWorkEntry, + }, + { + id: "entry-work-2", + kind: "work", + createdAt: secondWorkEntry.createdAt, + entry: secondWorkEntry, + }, + ], + completionDividerBeforeEntryId: null, + isWorking: false, + activeTurnStartedAt: null, + turnDiffSummaryByAssistantMessageId: new Map(), + revertTurnCountByUserMessageId: new Map(), + }); + + const firstRows = createRows(); + const initial = computeStableMessagesTimelineRows(firstRows, { + byId: new Map(), + result: [], + }); + const secondRows = createRows(); + + expect(secondRows[0]).not.toBe(firstRows[0]); + + const repeated = computeStableMessagesTimelineRows(secondRows, initial); + + expect(repeated).toBe(initial); + expect(repeated.result[0]).toBe(initial.result[0]); + }); + it("returns a new result when row order changes without content changes", () => { const firstUserMessage = { id: "user-1" as never, diff --git a/apps/web/src/components/chat/MessagesTimeline.logic.ts b/apps/web/src/components/chat/MessagesTimeline.logic.ts index a56643d84b0d3c074e286350eb4868046ef86061..c99f0b98a66e827469e43917a002dd1031223901 100644 --- a/apps/web/src/components/chat/MessagesTimeline.logic.ts +++ b/apps/web/src/components/chat/MessagesTimeline.logic.ts @@ -1,3 +1,4 @@ +import * as Equal from "effect/Equal"; import { type TimelineEntry, type WorkLogEntry } from "../../session-logic"; import { type ChatMessage, type ProposedPlan, type TurnDiffSummary } from "../../types"; import { type MessageId } from "@t3tools/contracts"; @@ -223,7 +224,7 @@ function isRowUnchanged(a: MessagesTimelineRow, b: MessagesTimelineRow): boolean return a.proposedPlan === (b as typeof a).proposedPlan; case "work": - return a.groupedEntries === (b as typeof a).groupedEntries; + return Equal.equals(a.groupedEntries, (b as typeof a).groupedEntries); case "message": { const bm = b as typeof a; -- 2.54.0