From 7da5f4aa97e4c3756e3df388f7abcf9a9712b767 Mon Sep 17 00:00:00 2001 From: Sam Valladares Date: Wed, 8 Jul 2026 11:54:03 -0700 Subject: [PATCH] feat(observatory): integrate into the main dashboard graph The Observatory is no longer a separate URL you have to know about. It is now a first-class part of the main graph page, following the proven Memory Cinema pattern (which remains untouched per the protected-block rule): - The full experience is factored into a reusable ObservatoryStage component (engine, plans, overlays moved verbatim from the route). - The graph control bar gains an Observatory button beside Dream and Memory Cinema; clicking it takes over the screen full-bleed (z-50). - Inside the stage: an on-screen switcher for the 5 cognitive moments (RECALL / BIRTH / RESCUE / HORIZON / FIREWALL) and an EXIT control (Esc works too) that returns to the graph exactly as it was. Both are hidden in capture mode so recordings stay chrome-free. - {#key demo} remounts the stage per moment: fresh engine, plans, and clock, deterministic from frame 0. - The /observatory route stays as a thin wrapper so deep links and the ?frame=N / ?capture=1 recording contract are unchanged; switching a moment there rewrites the URL so links stay shareable. Verified live in the running dashboard: button renders between Dream and Cinema, click boots the WebGPU takeover (148 real nodes, 2865 edges), RESCUE chip switches the moment (telemetry: SALIENCE-RESCUE), EXIT unmounts cleanly back to the graph (canvas count 2 -> 1). svelte-check 940 files 0 errors 0 warnings; 96 observatory tests green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../lib/observatory/ObservatoryStage.svelte | 363 ++++++++++++++++++ .../src/routes/(app)/graph/+page.svelte | 40 ++ .../src/routes/(app)/observatory/+page.svelte | 314 ++------------- 3 files changed, 439 insertions(+), 278 deletions(-) create mode 100644 apps/dashboard/src/lib/observatory/ObservatoryStage.svelte diff --git a/apps/dashboard/src/lib/observatory/ObservatoryStage.svelte b/apps/dashboard/src/lib/observatory/ObservatoryStage.svelte new file mode 100644 index 0000000..8ad5d0f --- /dev/null +++ b/apps/dashboard/src/lib/observatory/ObservatoryStage.svelte @@ -0,0 +1,363 @@ + + + + + +
+ +
+ +
+ + + {#if showHud} +
+ + + + + {#if onexit} + + {/if} + + + {#if showSwitcher} +
+ {#each DEMO_MODES as mode (mode)} + + {/each} +
+ {/if} + + + {#if loading} +
+
+ LOADING MEMORY FIELD... +
+
+ {/if} + + + {#if error && !loading} +
+
+ {error} +
+
+ {/if} + + + + + + {#if demo === 'salience-rescue' && rescuePlan?.viable} + + {/if} + + + {#if demo === 'firewall' && firewallPlan?.viable} + + {/if} + + + {#if !loading && graphData && graphData.nodeCount === 0} +
+
+ NO MEMORIES IN FIELD +
+
+ {/if} +
+ {/if} +
+ + diff --git a/apps/dashboard/src/routes/(app)/graph/+page.svelte b/apps/dashboard/src/routes/(app)/graph/+page.svelte index fe9f6d5..d45c4dd 100644 --- a/apps/dashboard/src/routes/(app)/graph/+page.svelte +++ b/apps/dashboard/src/routes/(app)/graph/+page.svelte @@ -20,6 +20,10 @@ import type { GraphMutation } from '$lib/graph/events'; import { AHAGRAPH_COLORS, AHAGRAPH_DESCRIPTIONS, type ColorMode } from '$lib/graph/nodes'; import { filterByDate } from '$lib/graph/temporal'; + // Cognitive Observatory — the WebGPU cinematic moments, launched from THIS + // page (its primary home) as a full-bleed takeover, like Memory Cinema. + import ObservatoryStage from '$lib/observatory/ObservatoryStage.svelte'; + import type { DemoMode } from '$lib/observatory/types'; let graphData: GraphResponse | null = $state(null); let selectedMemory: Memory | null = $state(null); @@ -54,6 +58,12 @@ let liveNodeCount = $state(0); let liveEdgeCount = $state(0); + // Observatory takeover state — opens full-bleed over the graph; Esc or + // × EXIT returns here. {#key obsDemo} remounts the stage per moment so + // every demo starts deterministically from frame 0. + let showObservatory = $state(false); + let obsDemo = $state('recall-path'); + // Filtered graph data based on temporal mode let displayNodes = $derived.by((): GraphNode[] => { if (!graphData) return []; @@ -389,6 +399,18 @@ disown {isDreaming ? 'Dreaming…' : 'Dream'} + + + +{#if showObservatory} +
+ {#key obsDemo} + (obsDemo = d)} + onexit={() => (showObservatory = false)} + /> + {/key} +
+{/if} diff --git a/apps/dashboard/src/routes/(app)/observatory/+page.svelte b/apps/dashboard/src/routes/(app)/observatory/+page.svelte index 78dc362..3c38166 100644 --- a/apps/dashboard/src/routes/(app)/observatory/+page.svelte +++ b/apps/dashboard/src/routes/(app)/observatory/+page.svelte @@ -1,291 +1,49 @@ - - - -
- -
- -
- - - {#if showHud} -
- - - - - {#if loading} -
-
- LOADING MEMORY FIELD... -
-
- {/if} - - - {#if error && !loading} -
-
- {error} -
-
- {/if} - - - - - - {#if demoMode === 'salience-rescue' && rescuePlan?.viable} - - {/if} - - - {#if demoMode === 'firewall' && firewallPlan?.viable} - - {/if} - - - {#if !loading && graphData && graphData.nodeCount === 0} -
-
- NO MEMORIES IN FIELD -
-
- {/if} -
- {/if} -
- - + +{#key demo} + goto(`${base}/graph`)} + /> +{/key}