diff --git a/apps/web/src/components/Sidebar.logic.test.ts b/apps/web/src/components/Sidebar.logic.test.ts index b57e2f00c89..94a2ed09256 100644 --- a/apps/web/src/components/Sidebar.logic.test.ts +++ b/apps/web/src/components/Sidebar.logic.test.ts @@ -29,6 +29,7 @@ import { resolveSidebarStageBadgeLabel, resolveThreadRowClassName, resolveSidebarV2Status, + resolveSidebarV2TopStatus, resolveThreadStatusPill, resolveWorkingStartedAt, searchSidebarThreadsByTitle, @@ -2147,3 +2148,22 @@ describe("sortLogicalProjectsForSidebar", () => { ).toEqual(["logical-newer", "logical-older"]); }); }); + +describe("resolveSidebarV2TopStatus", () => { + it("shows monitoring as its own calm status, not as Working", () => { + // Board cards read this. Folding monitoring into Working gave a watch loop + // the active-progress shimmer, which is what the v1 pill (pulse: false) and + // the v2 sidebar row both deliberately avoid. + const monitoring = resolveSidebarV2TopStatus({ status: "monitoring", isUnread: false }); + expect(monitoring?.label).toBe("Monitoring"); + expect(monitoring?.icon).toBeNull(); + expect(monitoring?.className).not.toContain("animate-sidebar-working-text"); + }); + + it("keeps working animated so the two remain distinguishable", () => { + const working = resolveSidebarV2TopStatus({ status: "working", isUnread: false }); + expect(working?.label).toBe("Working"); + expect(working?.icon).toBe("working"); + expect(working?.className).toContain("animate-sidebar-working-text"); + }); +}); diff --git a/apps/web/src/components/Sidebar.logic.ts b/apps/web/src/components/Sidebar.logic.ts index 14a36269e52..2e8c05478ee 100644 --- a/apps/web/src/components/Sidebar.logic.ts +++ b/apps/web/src/components/Sidebar.logic.ts @@ -883,7 +883,7 @@ export function resolveSidebarV2Status(thread: SidebarV2StatusInput): SidebarV2S } export interface SidebarV2TopStatus { - label: "Working" | "Approval" | "Input" | "Failed" | "Done"; + label: "Working" | "Monitoring" | "Approval" | "Input" | "Failed" | "Done"; icon: "working" | "done" | null; className: string; } @@ -894,15 +894,21 @@ export function resolveSidebarV2TopStatus(input: { }): SidebarV2TopStatus | null { switch (input.status) { case "working": - // Monitoring is live background work too. The v2 top-status label union has - // no Monitoring member, so it reads as Working rather than inventing one. - case "monitoring": return { label: "Working", icon: "working", className: "animate-sidebar-working-text text-sky-600 motion-reduce:animate-none dark:text-sky-400", }; + // Steady label, no icon, no duty-cycled shimmer: monitoring is calm + // background presence, not active progress (monitoring-pill D6). Matches the + // v2 sidebar row and the v1 pill, which uses pulse: false here. + case "monitoring": + return { + label: "Monitoring", + icon: null, + className: "text-sky-600 dark:text-sky-400", + }; case "approval": return { label: "Approval",