From cdc51d4d303c157f615b06d931e69523b5626a79 Mon Sep 17 00:00:00 2001 From: Molecule AI Frontend Engineer Date: Fri, 17 Apr 2026 18:51:22 +0000 Subject: [PATCH 1/3] fix(canvas): color-code similarity badge by score tier (issue #783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Badge was always text-zinc-500; apply blue-500 (>=0.8), zinc-400 (0.5–0.8), zinc-600 (<0.5) per spec. Add 3 vitest tests for each color tier (725 total). Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/MemoryInspectorPanel.tsx | 9 ++++- .../__tests__/MemoryInspectorPanel.test.tsx | 39 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/canvas/src/components/MemoryInspectorPanel.tsx b/canvas/src/components/MemoryInspectorPanel.tsx index ed54d8b5..83be9df4 100644 --- a/canvas/src/components/MemoryInspectorPanel.tsx +++ b/canvas/src/components/MemoryInspectorPanel.tsx @@ -427,7 +427,14 @@ function MemoryEntryRow({ {/* Similarity score badge — only rendered when backend provides a score */} {entry.similarity_score != null && ( = 0.8 + ? "text-blue-500" + : entry.similarity_score >= 0.5 + ? "text-zinc-400" + : "text-zinc-600", + ].join(" ")} title={`Similarity: ${(entry.similarity_score * 100).toFixed(1)}%`} data-testid="similarity-badge" > diff --git a/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx b/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx index 1cb709ac..e6a17fca 100644 --- a/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx +++ b/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx @@ -475,6 +475,45 @@ describe("MemoryInspectorPanel — semantic search", () => { ).toBeNull(); }); + it("colors similarity-badge blue-500 when score >= 0.8", async () => { + mockGet.mockResolvedValue([ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + { ...ENTRY_A, similarity_score: 0.92 }, + ] as any); + render(); + await waitFor(() => screen.getByText("task-queue")); + const badge = document.querySelector('[data-testid="similarity-badge"]'); + expect(badge?.className).toContain("text-blue-500"); + expect(badge?.className).not.toContain("text-zinc-400"); + expect(badge?.className).not.toContain("text-zinc-600"); + }); + + it("colors similarity-badge zinc-400 when score is between 0.5 and 0.8", async () => { + mockGet.mockResolvedValue([ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + { ...ENTRY_A, similarity_score: 0.65 }, + ] as any); + render(); + await waitFor(() => screen.getByText("task-queue")); + const badge = document.querySelector('[data-testid="similarity-badge"]'); + expect(badge?.className).toContain("text-zinc-400"); + expect(badge?.className).not.toContain("text-blue-500"); + expect(badge?.className).not.toContain("text-zinc-600"); + }); + + it("colors similarity-badge zinc-600 when score is below 0.5", async () => { + mockGet.mockResolvedValue([ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + { ...ENTRY_A, similarity_score: 0.31 }, + ] as any); + render(); + await waitFor(() => screen.getByText("task-queue")); + const badge = document.querySelector('[data-testid="similarity-badge"]'); + expect(badge?.className).toContain("text-zinc-600"); + expect(badge?.className).not.toContain("text-blue-500"); + expect(badge?.className).not.toContain("text-zinc-400"); + }); + it("clear button resets debouncedQuery immediately and re-fetches without ?q=", async () => { vi.useFakeTimers(); // eslint-disable-next-line @typescript-eslint/no-explicit-any From 1e9fd374608b11e543f26294eb124a4ad949e0eb Mon Sep 17 00:00:00 2001 From: "molecule-ai[bot]" <276602405+molecule-ai[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:23:51 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(gate-5):=20WCAG=20AA=20=E2=80=94=20zinc?= =?UTF-8?q?-400=20italic=20for=20low-score=20badge=20per=20[uiux-agent]=20?= =?UTF-8?q?review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- canvas/src/components/MemoryInspectorPanel.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/canvas/src/components/MemoryInspectorPanel.tsx b/canvas/src/components/MemoryInspectorPanel.tsx index 83be9df4..4f49242b 100644 --- a/canvas/src/components/MemoryInspectorPanel.tsx +++ b/canvas/src/components/MemoryInspectorPanel.tsx @@ -433,12 +433,12 @@ function MemoryEntryRow({ ? "text-blue-500" : entry.similarity_score >= 0.5 ? "text-zinc-400" - : "text-zinc-600", + : "text-zinc-400 italic", ].join(" ")} title={`Similarity: ${(entry.similarity_score * 100).toFixed(1)}%`} data-testid="similarity-badge" > - {Math.round(entry.similarity_score * 100)}% + {entry.similarity_score < 0.5 ? "~" : ""}{Math.round(entry.similarity_score * 100)}% )} From 55200e95d8a86186ed987e076813b87e4629d57d Mon Sep 17 00:00:00 2001 From: "molecule-ai[bot]" <276602405+molecule-ai[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:24:02 +0000 Subject: [PATCH 3/3] =?UTF-8?q?fix(gate-5):=20update=20test=20=E2=80=94=20?= =?UTF-8?q?zinc-400=20italic=20+=20tilde=20assertion=20for=20low-score=20b?= =?UTF-8?q?adge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/__tests__/MemoryInspectorPanel.test.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx b/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx index e6a17fca..31ef0925 100644 --- a/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx +++ b/canvas/src/components/__tests__/MemoryInspectorPanel.test.tsx @@ -501,7 +501,7 @@ describe("MemoryInspectorPanel — semantic search", () => { expect(badge?.className).not.toContain("text-zinc-600"); }); - it("colors similarity-badge zinc-600 when score is below 0.5", async () => { + it("colors similarity-badge zinc-400 italic with tilde prefix when score is below 0.5", async () => { mockGet.mockResolvedValue([ // eslint-disable-next-line @typescript-eslint/no-explicit-any { ...ENTRY_A, similarity_score: 0.31 }, @@ -509,9 +509,11 @@ describe("MemoryInspectorPanel — semantic search", () => { render(); await waitFor(() => screen.getByText("task-queue")); const badge = document.querySelector('[data-testid="similarity-badge"]'); - expect(badge?.className).toContain("text-zinc-600"); + expect(badge?.className).toContain("text-zinc-400"); + expect(badge?.className).toContain("italic"); expect(badge?.className).not.toContain("text-blue-500"); - expect(badge?.className).not.toContain("text-zinc-400"); + expect(badge?.className).not.toContain("text-zinc-600"); + expect(badge?.textContent).toBe("~31%"); }); it("clear button resets debouncedQuery immediately and re-fetches without ?q=", async () => {