fix(health): count 'entity' pages in graph health metrics (#2639)

getHealth's entity_pages CTE and the top-linked-pages query only match the
legacy 'person' and 'company' types, so brains using the gbrain-base-v2
pack's 'entity' type report 0% entity link/timeline coverage in `gbrain
health` even when doctor's graph_coverage shows real coverage. Add 'entity'
to both queries in both engines (PGLite + Postgres, in lockstep per the
engine-parity rule) and extend the getHealth graph-metrics test with an
entity-typed page.

Validation: bun test test/pglite-engine.test.ts --test-name-pattern 'getHealth graph metrics' (5 pass).
This commit is contained in:
Tyler Robinson
2026-07-23 11:01:46 -07:00
committed by GitHub
parent 3454dca0b4
commit 8fc93c8fac
3 changed files with 13 additions and 12 deletions
+9 -8
View File
@@ -1264,6 +1264,7 @@ describe('PGLiteEngine: getHealth graph metrics', () => {
await engine.putPage('people/alice', { ...testPage, type: 'person', title: 'Alice' });
await engine.putPage('people/bob', { ...testPage, type: 'person', title: 'Bob' });
await engine.putPage('companies/acme', { ...testPage, type: 'company', title: 'Acme' });
await engine.putPage('entities/project-x', { ...testPage, type: 'entity', title: 'Project X' });
});
test('link_coverage = 0 when no links exist', async () => {
@@ -1272,17 +1273,17 @@ describe('PGLiteEngine: getHealth graph metrics', () => {
});
test('link_coverage = % of entity pages with >= 1 inbound link', async () => {
// Acme gets 1 inbound link (from Alice), Alice/Bob get 0 inbound.
// 1 of 3 entity pages has inbound links -> 33%.
// Acme gets 1 inbound link (from Alice), Alice/Bob/Reddit get 0 inbound.
// 1 of 4 entity pages has inbound links -> 25%.
await engine.addLink('people/alice', 'companies/acme', '', 'works_at');
const h = await engine.getHealth();
expect(h.link_coverage).toBeCloseTo(1 / 3, 2);
expect(h.link_coverage).toBeCloseTo(1 / 4, 2);
});
test('timeline_coverage = % with >= 1 timeline entry', async () => {
await engine.addTimelineEntry('people/alice', { date: '2026-01-15', summary: 'Joined' });
const h = await engine.getHealth();
expect(h.timeline_coverage).toBeCloseTo(1 / 3, 2);
expect(h.timeline_coverage).toBeCloseTo(1 / 4, 2);
});
test('most_connected lists top entities by link count', async () => {
@@ -1295,14 +1296,14 @@ describe('PGLiteEngine: getHealth graph metrics', () => {
});
test('orphan_pages: pages with neither inbound nor outbound links', async () => {
// All 3 pages start with no links. Expect 3 orphans.
// All 4 pages start with no links. Expect 4 orphans.
const h = await engine.getHealth();
expect(h.orphan_pages).toBe(3);
expect(h.orphan_pages).toBe(4);
// Add alice -> acme. Alice has outbound, acme has inbound, only Bob is orphan.
// Add alice -> acme. Alice has outbound, acme has inbound, Bob and Reddit are orphan.
await engine.addLink('people/alice', 'companies/acme', '', 'works_at');
const h2 = await engine.getHealth();
expect(h2.orphan_pages).toBe(1);
expect(h2.orphan_pages).toBe(2);
});
});