+ {intro}
+
+ {/* Summary tiles */}
+
+ {[
+ { label: t('knowledgeGaps.metricEntities'), value: String(report.entityCount) },
+ { label: t('knowledgeGaps.metricOrphans'), value: String(report.orphanCount) },
+ { label: t('knowledgeGaps.metricLeaves'), value: String(report.leafCount) },
+ ].map(tile => (
+
+
+ {tile.label}
+
+
+ {tile.value}
+
+
+ ))}
+
+
+ {t('knowledgeGaps.ratioCaption').replace('{ratio}', String(pct(report.gapRatio)))}
+
+
+ {report.gaps.length === 0 ? (
+
+ {t('knowledgeGaps.allConnected')}
+
+ ) : (
+
+
+ {t('knowledgeGaps.heading')}
+
+
+ {rows.map(gap => (
+ -
+
+ {gap.id}
+ {gap.objectOnly && (
+
+ {t('knowledgeGaps.objectOnly')}
+
+ )}
+
+
+ {gap.kind === 'orphan'
+ ? t('knowledgeGaps.kindOrphan')
+ : t('knowledgeGaps.kindLeaf')}
+
+
+ ))}
+
+ {truncated && (
+
+ {t('knowledgeGaps.truncated')
+ .replace('{shown}', String(rows.length))
+ .replace('{total}', String(report.gaps.length))}
+
+ )}
+
+ )}
+
+ );
+};
+
+export default KnowledgeGapsPanel;
diff --git a/app/src/components/intelligence/KnowledgeGapsTab.test.tsx b/app/src/components/intelligence/KnowledgeGapsTab.test.tsx
new file mode 100644
index 00000000000..8f62d4e80eb
--- /dev/null
+++ b/app/src/components/intelligence/KnowledgeGapsTab.test.tsx
@@ -0,0 +1,61 @@
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { computeKnowledgeGaps } from '../../lib/memory/knowledgeGaps';
+import type { GraphRelation } from '../../utils/tauriCommands/memory';
+import KnowledgeGapsTab from './KnowledgeGapsTab';
+
+const mockLoad = vi.fn();
+const mockLoadNamespaces = vi.fn();
+
+vi.mock('../../services/api/knowledgeGapsApi', () => ({
+ loadKnowledgeGaps: (...args: unknown[]) => mockLoad(...args),
+ loadNamespaces: (...args: unknown[]) => mockLoadNamespaces(...args),
+}));
+
+function rel(subject: string, object: string): GraphRelation {
+ return {
+ namespace: 'n',
+ subject,
+ predicate: 'p',
+ object,
+ attrs: {},
+ updatedAt: 0,
+ evidenceCount: 1,
+ orderIndex: null,
+ documentIds: [],
+ chunkIds: [],
+ };
+}
+
+const report = computeKnowledgeGaps([rel('A', 'B'), rel('B', 'C')]);
+
+describe('