diff --git a/apps/web/src/App.test.tsx b/apps/web/src/App.test.tsx index 5053640..c1add67 100644 --- a/apps/web/src/App.test.tsx +++ b/apps/web/src/App.test.tsx @@ -161,6 +161,68 @@ describe("React app dashboard state view", () => { }); }); + test("does not restore aggregate health snapshots over the fresh shell", () => { + const restored = restoreDashboardTileSnapshots( + { + state: "ready", + document: dimensionLabDashboardFixture, + schemaVersion: "dashboard.v1", + currentRevisionId: "revision-a", + }, + [ + { + ageMs: 60_000, + response: { + state: "ready", + tile: { kind: "status", stripId: "footer-status", id: "system-status" }, + item: { + id: "system-status", + label: "System Status", + value: "20 services down", + severity: "stale", + }, + }, + }, + { + ageMs: 60_000, + response: { + state: "ready", + tile: { kind: "module", id: "runtime-health-summary" }, + item: { + id: "runtime-health-summary", + kind: "summary", + title: "Runtime Health", + value: "20 services down", + detail: "0 warnings - 8 services ok - stale 60s", + severity: "stale", + }, + }, + }, + ], + ); + + expect(restored.restoredItemIds).toEqual(new Set()); + if (restored.dashboard.state !== "ready") { + throw new Error("Expected dashboard to be ready"); + } + expect( + restored.dashboard.document.statusStrips[0].items.find((item) => + item.id === "system-status" + ), + ).toMatchObject({ + id: "system-status", + value: "Fallback operational", + }); + expect( + restored.dashboard.document.modules?.find((module) => + module.id === "runtime-health-summary" + ), + ).toMatchObject({ + id: "runtime-health-summary", + value: "fallback", + }); + }); + test("uses structured tile match keys for delimiter-bearing ids", () => { expect( dashboardTileMatchKey({ kind: "service", groupId: "a:b", id: "c" }), diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index 826c1a3..403bef9 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -15,6 +15,7 @@ import { createDashboardTileSnapshotStore, createDashboardTileBackoff, createDashboardRequestAborter, + isPersistableDashboardTileSnapshot, runViewportAwareDashboardHydrationQueue, shouldPauseDashboardRefresh, subscribeToDashboardTileEvents, @@ -690,7 +691,9 @@ export function restoreDashboardTileSnapshots( }; } - return snapshots.reduce( + return snapshots.filter((snapshot) => + isPersistableDashboardTileSnapshot(snapshot.response.tile) + ).reduce( (current, snapshot) => ({ dashboard: { ...current.dashboard, diff --git a/apps/web/src/lib/client/dashboard-refresh.test.ts b/apps/web/src/lib/client/dashboard-refresh.test.ts index 7da37ea..77ea795 100644 --- a/apps/web/src/lib/client/dashboard-refresh.test.ts +++ b/apps/web/src/lib/client/dashboard-refresh.test.ts @@ -518,6 +518,77 @@ describe("dashboard refresh lifecycle", () => { }, ]); }); + + test("ignores restored aggregate health snapshots", () => { + const storage = createMemoryStorage(); + storage.setItem( + "dimensionlab.dashboard.tiles.v1", + JSON.stringify({ + currentRevisionId: "revision-a", + schemaVersion: "dashboard.v1", + tiles: [ + { + item: { + id: "system-status", + label: "System Status", + value: "20 services down", + severity: "danger", + }, + savedAt: 1_000, + tile: { kind: "status", stripId: "footer-status", id: "system-status" }, + }, + { + item: { + id: "runtime-health-summary", + kind: "summary", + title: "Runtime Health", + value: "20 services down", + detail: "0 warnings - 8 services ok", + severity: "danger", + }, + savedAt: 1_000, + tile: { kind: "module", id: "runtime-health-summary" }, + }, + { + item: { + id: "infra-ram", + label: "Infra RAM", + value: { kind: "percent", value: 42 }, + detail: "live", + severity: "ok", + }, + savedAt: 1_000, + tile: { kind: "telemetry", id: "infra-ram" }, + }, + ], + version: 1, + }), + ); + + const store = createDashboardTileSnapshotStore(storage, { + now: () => 16_000, + }); + + expect(store.restore({ + currentRevisionId: "revision-a", + schemaVersion: "dashboard.v1", + })).toEqual([ + { + ageMs: 15_000, + response: { + state: "ready", + tile: { kind: "telemetry", id: "infra-ram" }, + item: { + id: "infra-ram", + label: "Infra RAM", + value: { kind: "percent", value: 42 }, + detail: "live - stale 15s", + severity: "stale", + }, + }, + }, + ]); + }); }); async function waitFor(predicate: () => boolean) { diff --git a/apps/web/src/lib/client/dashboard-refresh.ts b/apps/web/src/lib/client/dashboard-refresh.ts index 989816c..7c50359 100644 --- a/apps/web/src/lib/client/dashboard-refresh.ts +++ b/apps/web/src/lib/client/dashboard-refresh.ts @@ -617,7 +617,9 @@ export function createDashboardTileSnapshotStore( return { currentRevisionId: payload.currentRevisionId, schemaVersion: payload.schemaVersion, - tiles: payload.tiles.filter(isDashboardTileSnapshotRecord), + tiles: payload.tiles + .filter(isDashboardTileSnapshotRecord) + .filter((record) => isPersistableDashboardTileSnapshot(record.tile)), version: 1, }; } catch { @@ -660,6 +662,8 @@ export function createDashboardTileSnapshotStore( response: Extract; }, ): void { + if (!isPersistableDashboardTileSnapshot(input.response.tile)) return; + const payload = matchingPayload(input); const key = dashboardTileSnapshotKey(input.response.tile); const nextRecord: DashboardTileSnapshotRecord = { @@ -720,6 +724,14 @@ function dashboardTileSnapshotKey(tile: DashboardTileReference): string { return JSON.stringify(tile); } +export function isPersistableDashboardTileSnapshot( + tile: DashboardTileReference, +): boolean { + if (tile.kind === "status" && tile.id === "system-status") return false; + if (tile.kind === "module" && tile.id === "runtime-health-summary") return false; + return true; +} + function isDashboardTileSnapshotRecord( value: unknown, ): value is DashboardTileSnapshotRecord {