From d80367fa30db0fa9bde217757be7c43865dbf395 Mon Sep 17 00:00:00 2001 From: Fusion Agent Date: Tue, 18 Aug 2026 22:38:54 +0000 Subject: [PATCH] FN-018: fix KnowledgeGraphPanel hop typing Normalize absent path hop counts for the typed graph path interpolation while preserving zero-hop results. - Use undefined for non-found path hop counts to satisfy the typed interpolation contract. - Add regression coverage that renders a found zero-hop path. Files changed: packages/dashboard/app/components/KnowledgeGraphPanel.tsx | 3 ++- .../dashboard/app/components/__tests__/KnowledgeGraphPanel.test.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-018 Fusion-Task-Lineage: 28254bbd-967b-463e-87de-8dfa847ca42d Co-authored-by: Fusion --- packages/dashboard/app/components/KnowledgeGraphPanel.tsx | 3 ++- .../app/components/__tests__/KnowledgeGraphPanel.test.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/dashboard/app/components/KnowledgeGraphPanel.tsx b/packages/dashboard/app/components/KnowledgeGraphPanel.tsx index 956467c832..51447be341 100644 --- a/packages/dashboard/app/components/KnowledgeGraphPanel.tsx +++ b/packages/dashboard/app/components/KnowledgeGraphPanel.tsx @@ -42,7 +42,8 @@ export function KnowledgeGraphPanel({ projectId, addToast }: { projectId?: strin const selected = graph.selectedId; const limits = graph.status.pathLimits; const foundPath = graph.pathResult?.outcome === "found" ? graph.pathResult.path : null; - const foundHops = graph.pathResult?.outcome === "found" ? graph.pathResult.hops : null; + /* FNXC:KnowledgeGraphDashboard 2026-08-18-22:31: The typed i18n interpolation treats absent hop counts as optional; keep successful zero-hop paths numeric while normalizing non-found results to undefined. */ + const foundHops = graph.pathResult?.outcome === "found" ? graph.pathResult.hops : undefined; return
{graph.status.nodeCount} {t("memory.graphNodes", "nodes")} · {graph.status.edgeCount} {t("memory.graphEdges", "edges")}{graph.status.graphDir}
{t("knowledgeGraph.nodeKinds", "Node kinds")}
{Object.entries(graph.status.nodeKindCounts ?? {}).map(([kind, count]) => `${kind}: ${count}`).join(" · ") || "—"}
{t("knowledgeGraph.edgeKinds", "Edge kinds")}
{Object.entries(graph.status.edgeKindCounts ?? {}).map(([kind, count]) => `${kind}: ${count}`).join(" · ") || "—"}
{t("knowledgeGraph.provenance", "Provenance")}
{Object.entries(graph.status.provenanceCounts ?? {}).map(([kind, count]) => `${kind}: ${count}`).join(" · ") || "—"}
diff --git a/packages/dashboard/app/components/__tests__/KnowledgeGraphPanel.test.tsx b/packages/dashboard/app/components/__tests__/KnowledgeGraphPanel.test.tsx index da45d2b434..b176e23e1b 100644 --- a/packages/dashboard/app/components/__tests__/KnowledgeGraphPanel.test.tsx +++ b/packages/dashboard/app/components/__tests__/KnowledgeGraphPanel.test.tsx @@ -42,7 +42,10 @@ describe("KnowledgeGraphPanel", () => { rerender(); expect(screen.getByTestId("knowledge-graph-no-results")).toBeInTheDocument(); for (const [result, id] of [[{ outcome: "found", path: { nodes: [node], edges: [] }, hops: 0, maxHops: 2, expansions: 1, truncated: false }, "knowledge-graph-path-found"], [{ outcome: "not-found", path: null, maxHops: 2, expansions: 2, truncated: false }, "knowledge-graph-path-not-found"], [{ outcome: "limit-reached", path: null, maxHops: 2, expansions: 2, truncated: true, limit: "max-hops" }, "knowledge-graph-path-limit-reached"]] as const) { - useKnowledgeGraph.mockReturnValue(graph({ pathResult: result })); rerender(); expect(screen.getByTestId(id)).toBeInTheDocument(); + useKnowledgeGraph.mockReturnValue(graph({ pathResult: result })); + rerender(); + expect(screen.getByTestId(id)).toBeInTheDocument(); + if (id === "knowledge-graph-path-found") expect(screen.getByTestId(id)).toHaveTextContent("0 hops"); } useKnowledgeGraph.mockReturnValue(graph({ error: "Unknown graph node" })); rerender(); expect(screen.getByTestId("knowledge-graph-path-error")).toHaveTextContent("Unknown graph node"); });