From c365fee83da0427ede52cdd9de1bc9a9294bcad2 Mon Sep 17 00:00:00 2001 From: Renfei Zhou Date: Sun, 15 Nov 2026 12:55:10 +0000 Subject: [PATCH 1/2] test(fork): an edited agent message shows its edit time The browser case now checks the time under a sent message after a rewrite: the row and its tooltip must show when the rewrite completed. It fails on main, where the row keeps the first send time. Co-Authored-By: Claude Opus 5.5 --- .../displayModeTimeline.browser.test.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/web/src/fork/displayMode/displayModeTimeline.browser.test.tsx b/apps/web/src/fork/displayMode/displayModeTimeline.browser.test.tsx index 72ca35f69067b..dcd7b1ef87270 100644 --- a/apps/web/src/fork/displayMode/displayModeTimeline.browser.test.tsx +++ b/apps/web/src/fork/displayMode/displayModeTimeline.browser.test.tsx @@ -13,6 +13,7 @@ import { MESSAGES_ONLY_DISPLAY_MODE, NORMAL_DISPLAY_MODE } from "@t3tools/shared import * as DateTime from "effect/DateTime"; import { createRef, type ComponentProps } from "react"; import { describe, expect, it, vi } from "vite-plus/test"; +import { page, userEvent } from "vite-plus/test/browser"; import "~/index.css"; @@ -21,6 +22,7 @@ import { MessagesTimeline } from "../../components/chat/MessagesTimeline"; import { deriveMessagesTimelineRows } from "../../components/chat/MessagesTimeline.logic"; import { __setClientSettingsForTests } from "../../hooks/useSettings"; import { deriveTimelineEntriesFromVisibleTurnItems } from "../../session-logic"; +import { formatChatTimestampTooltip, formatDayAwareTimestamp } from "../../timestampFormat"; import { applyDisplayMode } from "./displayModeTimeline"; const threadId = ThreadId.make("display-mode-thread"); @@ -290,8 +292,10 @@ describe("Messages only timeline", () => { input: { text: "The build is red." }, output: { ok: true, message: "Shown to the user." }, }; + const editedAt = new Date(Date.UTC(2026, 10, 3, 0, 47, 0)).toISOString(); const replace = (id: string, status: "completed" | "running"): OrchestrationV2TurnItem => ({ ...base(id, 3), + completedAt: DateTime.makeUnsafe(editedAt), status, type: "dynamic_tool", toolName: "mcp__t3-code__replace_send_to_user", @@ -304,11 +308,19 @@ describe("Messages only timeline", () => { }); const sent = entriesOf([userMessage("rewrite-ask", 0, "Is the build green?"), send]); - it("redraws the send's row with the new text when the rewrite arrives", async () => { + it("redraws the send's row with the new text and time when the rewrite arrives", async () => { await using screen = await renderTimeline([ ...applyDisplayMode(MESSAGES_ONLY_DISPLAY_MODE, sent), ]); + const shownTime = (at: string) => formatDayAwareTimestamp(at, "locale"); + const rowOf = (text: string) => + page.elementLocator( + screen.getByText(text).element().closest('[data-timeline-row-kind="message"]')!, + ); await expect.element(screen.getByText("The build is red.")).toBeVisible(); + await expect + .element(rowOf("The build is red.").getByText(shownTime(time(1)), { exact: true })) + .toBeVisible(); const rewritten = [...sent, ...entriesOf([replace("rewrite-call", "completed")])]; await screen.rerender( { ); await expect.element(screen.getByText("The build is green again.")).toBeVisible(); expect(screen.container.textContent ?? "").not.toContain("The build is red."); + const reply = rowOf("The build is green again."); + const shown = reply.getByText(shownTime(editedAt), { exact: true }); + await expect.element(shown).toBeVisible(); + expect(reply.element().textContent).not.toContain(shownTime(time(1))); + await userEvent.hover(shown); + await expect + .element(page.getByText(formatChatTimestampTooltip(editedAt, "locale"), { exact: true })) + .toBeVisible(); }); it("draws no row for a replace call in any mode, running or done", () => { From 4b068530b9705e8a0fedd3c65b60abdcd5a57ac0 Mon Sep 17 00:00:00 2001 From: Renfei Zhou Date: Sun, 15 Nov 2026 12:55:49 +0000 Subject: [PATCH 2/2] fix(fork): an edited agent message shows its edit time In a display mode, an agent message that a rewrite changed kept the time of its first send, while its "Edited" mark already knew the rewrite time. The message's time and tooltip now read that edit time when there is one. The row keeps its place, and its stored times stay at the first send, so turn order and "Worked for" do not change. Co-Authored-By: Claude Opus 5.5 --- apps/web/src/components/chat/TimelineAssistantRows.tsx | 4 ++-- apps/web/src/fork/displayMode/displayModeTimeline.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/chat/TimelineAssistantRows.tsx b/apps/web/src/components/chat/TimelineAssistantRows.tsx index 49e4e0c19adb2..8a011a7a579ae 100644 --- a/apps/web/src/components/chat/TimelineAssistantRows.tsx +++ b/apps/web/src/components/chat/TimelineAssistantRows.tsx @@ -213,10 +213,10 @@ function AssistantMessageMeta({ {!message.streaming && ( }> - {formatDayAwareTimestamp(message.updatedAt, ctx.timestampFormat)} + {formatDayAwareTimestamp(message.editedAt ?? message.updatedAt, ctx.timestampFormat)} - {formatChatTimestampTooltip(message.updatedAt, ctx.timestampFormat)} + {formatChatTimestampTooltip(message.editedAt ?? message.updatedAt, ctx.timestampFormat)} )} diff --git a/apps/web/src/fork/displayMode/displayModeTimeline.ts b/apps/web/src/fork/displayMode/displayModeTimeline.ts index 1b11e91a84992..781bd74e5af65 100644 --- a/apps/web/src/fork/displayMode/displayModeTimeline.ts +++ b/apps/web/src/fork/displayMode/displayModeTimeline.ts @@ -62,6 +62,7 @@ function replyEntry( streaming: false, createdBy: "agent", createdAt: entry.createdAt, + // The turn fold's "Worked for" ends at updatedAt; the meta line shows editedAt instead. updatedAt: entry.createdAt, ...(editedAt === undefined ? {} : { editedAt }), },