From 4c6a24a35a048c915a1fcd50b173a13bf2ecf1f0 Mon Sep 17 00:00:00 2001 From: Sam Valladares Date: Mon, 13 Jul 2026 11:31:26 +0700 Subject: [PATCH] =?UTF-8?q?feat(journey):=20wire=20Receipt=20=E2=86=92=20c?= =?UTF-8?q?entered=20Graph=20=E2=86=92=20auto-Cinema=20=E2=86=92=20Explore?= =?UTF-8?q?,=20all=20base-safe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hero journey now flows through visible controls with cognitive context that survives navigation (os-nav.ts gets its first real consumers): - /dashboard/ redirects to /dashboard/palace (the home), base-safe (was a bare goto('/graph') that escaped the base + landed on the wrong organ). - ReceiptCard 'Open in Cinema' uses osGoto with {memory, focus, receipt, cinema} — fixes the base-escape 404 AND carries the full evidence set. - Graph consumes ?memory= (canonical; ?center= legacy alias), centers on it, and a Graph-OWNED one-shot bridge auto-launches the PROTECTED Cinema by clicking its own .cinema-launch control (MemoryCinema untouched). - Graph 'Explore Connections' now carries the selected memory; Explore seeds the walk from THAT memory instead of re-seeding from the newest. Verified live: /→palace, graph?memory=X&cinema=1 centers + auto-opens Cinema (0 errors), explore?memory=X seeds a walk. check 0/0. Co-Authored-By: Claude Opus 4.8 --- .../src/lib/components/ReceiptCard.svelte | 15 +++++-- .../src/routes/(app)/explore/+page.svelte | 26 +++++++++--- .../src/routes/(app)/graph/+page.svelte | 41 +++++++++++++++---- apps/dashboard/src/routes/+page.svelte | 7 +++- 4 files changed, 71 insertions(+), 18 deletions(-) diff --git a/apps/dashboard/src/lib/components/ReceiptCard.svelte b/apps/dashboard/src/lib/components/ReceiptCard.svelte index 71ea574..afafe7f 100644 --- a/apps/dashboard/src/lib/components/ReceiptCard.svelte +++ b/apps/dashboard/src/lib/components/ReceiptCard.svelte @@ -8,9 +8,9 @@ // the receipt's primary memory, starting the (protected) Cinema flythrough // over the exact memory set the receipt names. // ═══════════════════════════════════════════════════════════════════════ - import { goto } from '$app/navigation'; import Icon from './Icon.svelte'; import type { Receipt } from '$lib/stores/api'; + import { osGoto } from '$lib/os-nav'; interface Props { receipt: Receipt; @@ -24,11 +24,20 @@ high: '#f43f5e' }; + // Hero-journey handoff: open the exact receipt in the Graph, centered on its + // primary memory, with the full retrieved set focused, then AUTO-LAUNCH Cinema + // over that evidence. Base-safe via osGoto (a bare goto('/graph') escaped the + // /dashboard base and 404'd); the cognitive context (memory/focus/receipt/ + // cinema) rides the URL so the whole set survives the navigation. function openInCinema() { const primary = receipt.retrieved[0]; if (!primary) return; - const focus = receipt.retrieved.join(','); - goto(`/graph?center=${encodeURIComponent(primary)}&focus=${encodeURIComponent(focus)}`); + osGoto('/graph', { + memory: primary, + focus: receipt.retrieved, + receipt: receipt.receipt_id, + cinema: true + }); } diff --git a/apps/dashboard/src/routes/(app)/explore/+page.svelte b/apps/dashboard/src/routes/(app)/explore/+page.svelte index a8cb2c0..214f7bf 100644 --- a/apps/dashboard/src/routes/(app)/explore/+page.svelte +++ b/apps/dashboard/src/routes/(app)/explore/+page.svelte @@ -52,9 +52,16 @@ // may apply results / clear the walking label. (GPT-5.6-sol cross-review.) let loadGen = 0; + // A memory id passed from another organ (e.g. Graph's "Explore Connections") + // seeds the walk from THAT thought instead of re-seeding from the newest one, + // so the selected memory survives the Graph → Explore handoff. + let seedMemoryId: string | null = $state(null); + onMount(() => { const params = new URLSearchParams(window.location.search); searchQuery = params.get('q') ?? ''; + // `memory` is the canonical cross-organ selection contract (center = legacy). + seedMemoryId = params.get('memory') ?? params.get('center'); void loadNeighbors(); }); @@ -145,13 +152,20 @@ // immediately alive, and every click walks onward from a real thought. if (!searchQuery.trim()) { try { - const seed = await api.memories.list({ limit: '1' }); + // If another organ handed us a memory (?memory=), seed the walk + // from THAT thought so the selection survives the handoff. Otherwise + // auto-seed from the newest memory so landing here is always alive. + const seedMemory = seedMemoryId + ? await api.memories.get(seedMemoryId).catch(() => null) + : null; + const start = seedMemory ?? (await api.memories.list({ limit: '1' })).memories?.[0] ?? null; if (gen !== loadGen) return; // a newer walk superseded this load - const newest = seed.memories?.[0]; - if (newest?.content) { - searchQuery = walkQuery(newest.content); - centerId = newest.id; - seededFrom = newest.id.slice(0, 8); + if (start?.content) { + searchQuery = walkQuery(start.content); + centerId = start.id; + seededFrom = start.id.slice(0, 8); + // Consume the one-shot seed so a later re-walk doesn't re-pin it. + seedMemoryId = null; // The seeded expedition must be shareable/reproducible too — a // reload mid-ingest would otherwise seed from a different memory. syncQueryToUrl(); diff --git a/apps/dashboard/src/routes/(app)/graph/+page.svelte b/apps/dashboard/src/routes/(app)/graph/+page.svelte index c865350..686d2ff 100644 --- a/apps/dashboard/src/routes/(app)/graph/+page.svelte +++ b/apps/dashboard/src/routes/(app)/graph/+page.svelte @@ -1,6 +1,7 @@