vestige/docs/launch/UI_ROADMAP_v2.1_v2.2.md
Sam Valladares 6a807698ef
v2.0.8 Pulse — Reasoning Theater + Pulse Toast + Terrarium + Intel Mac (#44)
* docs: anti-hedging banner + master state-and-plan + UI roadmap

Project-level CLAUDE.md line 1 is now the anti-hedging banner referencing
the global rule at /Users/entity002/CLAUDE.md. When Sam commits to an
execution path (content cadence, shipping velocity, outreach, launch),
forecasts compute as if the commitment is already met — no discounting by
execution probability.

VESTIGE_STATE_AND_PLAN.md (1135 lines, 14 parts): single-page reference
any AI agent can read to plan the next phase without re-reading the repo.
Covers current state of every crate, the 24 MCP tools, the 19 WebSocket
event types, the 8 IDE integrations, and v2.1 through v3.0 roadmap.

UI_ROADMAP_v2.1_v2.2.md: compiled UI-gap analysis from 4 parallel research
agents (April 19). 10 critical UI gaps + 2026 bleeding-edge patterns +
unclaimed territory (menu bar widget, voice-first, AR/VR, time-scrubber).

* feat(v2.2-pulse): InsightToast + multi-process STORAGE docs

Two independent ship items landing together on the v2.2 branch ahead of
the Tuesday launch — a new UI surface that makes Vestige's cognitive
events visible in real time, and honest documentation of the multi-process
safety story that underpins the Stigmergic Swarm narrative.

**InsightToast** (apps/dashboard/src/lib/components/InsightToast.svelte,
apps/dashboard/src/lib/stores/toast.ts):
  The dashboard already had a working WebSocket event stream on
  ws://localhost:3927/ws that broadcast every cognitive event (dream
  completions, consolidation sweeps, memory promotions/demotions, active-
  forgetting suppression and Rac1 cascades, bridge discoveries). None of
  that was surfaced to a user looking at anything other than the raw feed
  view. InsightToast subscribes to the existing eventFeed derived store,
  filters the spammy lifecycle events (Heartbeat, SearchPerformed,
  RetentionDecayed, ActivationSpread, ImportanceScored, MemoryCreated),
  and translates the narrative events into ephemeral toasts with a
  bioluminescent colored accent matching EVENT_TYPE_COLORS.

  Design notes:
  - Rate-limited ConnectionDiscovered at 1.5s intervals (dreams emit many).
  - Max 4 visible toasts, auto-dismiss at 4.5-7s depending on event weight.
  - Click or Enter/Space to dismiss early.
  - Bottom-right on desktop, top-banner on mobile.
  - Reduced-motion honored via prefers-reduced-motion.
  - Zero new websocket subscriptions — everything piggybacks on the
    existing derived store.

  Also added a "Preview Pulse" button to Settings -> Cognitive Operations
  that fires a synthetic sequence of four toasts (DreamCompleted,
  ConnectionDiscovered, MemorySuppressed, ConsolidationCompleted) so
  the animation is demoable without waiting for real cognitive activity.

**Multi-Process Safety section in docs/STORAGE.md**:
  Grounds the Stigmergic Swarm story with concrete tables of what the
  current WAL + 5s busy_timeout configuration actually supports vs what
  remains experimental. Key honest points:
  - Shared --data-dir + ONE vestige-mcp + N clients is the shipping
    pattern for multi-agent coordination.
  - Two vestige-mcp processes writing the same file is experimental —
    documented with the lsof + pkill recovery path.
  - Roadmap lists the three items that would promote it to "supported":
    advisory file lock, retry-with-jitter on SQLITE_BUSY, and a
    concurrent-writer load test.

Build + typecheck:
- npm run check: 0 errors, 0 warnings across 583 files
- npm run build: clean static build, adapter-static succeeds

* feat(v2.3-terrarium): Memory Birth Ritual + event pipeline fix

v2.3 "Terrarium" headline feature. When a MemoryCreated event arrives, a
glowing orb materialises in the cosmic center (camera-relative z=-40),
gestates for ~800ms growing from a tiny spark into a full orb, then arcs
along a dynamic quadratic Bezier curve to the live position of the real
node, and on arrival hands off to the existing RainbowBurst + Shockwave +
RippleWave cascade. The target position is re-resolved every frame so
the force simulation can move the destination during flight without the
orb losing its mark.

**New primitive — EffectManager.createBirthOrb()** (effects.ts):
  Accepts a camera, a color, a live target-position getter, and an
  arrival callback. Owns a sprite pair (outer halo + inner bright core),
  both depthTest:false with renderOrder 999/1000 so the orb is always
  visible through the starfield and the graph.
  - Gestation phase: easeOutCubic growth + sinusoidal pulse, halo tints
    from neutral to event color as the ritual charges.
  - Flight phase: QuadraticBezierCurve3 with control point at midpoint
    raised on Y by 30 + 15% of orb-to-target distance (shooting-star
    arc). Sampled with easeInOutQuad. Orb shrinks ~35% approaching target.
  - Arrival: fires onArrive callback once, then fades out over 8 frames
    while expanding slightly (energy dispersal).
  - Caller's onArrive triggers the burst cascade at arrivePos (NOT the
    original spawnPos — the force sim may have moved the target during
    the ritual, so we re-read nodeManager.positions on arrival).
  - Dispose path integrated with existing EffectManager.dispose().

**Event pipeline fix — Graph3D.processEvents()**:
  Previously tracked `processedEventCount` assuming APPEND order, but
  websocket.ts PREPENDS new events (index 0) and caps the array at
  MAX_EVENTS. Result: only the first MemoryCreated event after page
  load fired correctly; subsequent ones reprocessed the oldest entry.
  Fixed to walk from index 0 until hitting the last-processed event
  by reference identity — correct regardless of array direction or
  eviction pressure. Events are then processed oldest-first so causes
  precede effects. Found while wiring the v2.3 demo button; would have
  manifested as "first orb only" in production.

**Demo trigger** (Settings -> Birth Ritual Preview):
  Button that calls websocket.injectEvent() with a synthetic
  MemoryCreated event, cycling through node types (fact / concept /
  pattern / decision / person / place) to showcase the type-color
  mapping. Downstream consumers can't distinguish synthetic from real,
  so this drives the full ritual end-to-end. Intended for demo clip
  recording for the Wednesday launch.

**Test coverage:**
  - events.test.ts now tests the v2.3 birth ritual path: spawns 2+
    sprites in the scene immediately, and fires the full arrival
    cascade after driving the effects.update() loop past the ritual
    duration.
  - three-mock.ts extended with Vector3.addVectors, Vector3.applyQuaternion,
    Color.multiplyScalar, Quaternion, QuadraticBezierCurve3, Texture,
    and Object3D.quaternion/renderOrder so production code runs unaltered
    in tests.

Build + typecheck:
- npm run check: 0 errors, 0 warnings across 583 files
- npm test: 251/251 pass (net +0 from v2.2)
- npm run build: clean adapter-static output

The Sanhedrin Shatter (anti-birth ritual for hallucination veto) needs
server-side event plumbing and is deferred. Ship this as the Wednesday
visual mic-drop.

* fix(v2.3): 5 FATAL bugs + 4 god-tier upgrades from post-ship audit

Post-ship audit surfaced 6 FATALs and 4 upgrades. Shipping 5 of the 6 +
all 4 upgrades. FATAL 4 (VRAM hemorrhage from un-pooled label canvases
in createTextSprite) is pre-existing, not from this session, and scoped
separately for a proper texture-pool refactor.

**FATAL 1 — Toast Silent Lobotomy** (stores/toast.ts)
Subscriber tracked events[0] only. When Svelte batched multiple events
in one update tick (swarm firing DreamCompleted + ConnectionDiscovered
within the same millisecond), every event but the newest got silently
dropped. Fixed to walk from index 0 until hitting lastSeen — same
pattern as Graph3D.processEvents. Processes oldest-first to preserve
narrative order.

**FATAL 2 — Premature Birth** (graph/nodes.ts + graph/events.ts)
Orb flight is 138 frames; materialization was 30 frames. Node popped
fully grown ~100 frames before orb arrived — cheap UI glitch instead
of a biological birth. Added `addNode(..., { isBirthRitual: true })`
option that reserves the physics slot but hides mesh/glow/label and
skips the materializing queue. New `igniteNode(id)` flips visibility
and enqueues materialization. events.ts onArrive now calls igniteNode
at the exact docking moment, so the elastic spring-up peaks on impact.

**FATAL 3 — 120Hz ProMotion Time-Bomb** (components/Graph3D.svelte)
All physics + effect counters are frame-based. On a 120Hz display every
ritual ran at 2x speed. Added a `lastTime`-based governor in animate()
that early-returns if dt < 16ms, clamping effective rate to ~60fps.
`- (dt % 16)` carry avoids long-term drift. Zero API changes; tonight's
fast fix until physics is rewritten to use dt.

**FATAL 5 — Bezier GC Panic** (graph/effects.ts birth-orb update)
Flight phase allocated a new Vector3 (control point) and a new
QuadraticBezierCurve3 every frame per orb. With 3 orbs in flight that's
360 objects/sec for the GC to collect. Rewrote as inline algebraic
evaluation — zero allocations per frame, identical curve.

**FATAL 6 — Phantom Shockwave** (graph/events.ts)
A 166ms setTimeout fired the 2nd shockwave. If the user navigated
away during that window the scene was disposed, the timer still
fired, and .add() on a dead scene threw unhandled rejection. Dropped
the setTimeout entirely; both shockwaves fire immediately in onArrive
with different scales/colors for the same layered-crash feel.

**UPGRADE 1 — Sanhedrin Shatter** (graph/effects.ts birth-orb update)
If getTargetPos() returns undefined AFTER gestation (target node was
deleted mid-ritual — Stop hook sniping a hallucination), the orb
turns blood-red, triggers a violent implosion in place, and skips
the arrival cascade. Cognitive immune system made visible.

**UPGRADE 2 — Newton's Cradle** (graph/events.ts onArrive)
On docking the target mesh's scale gets bumped 1.8×, so the elastic
materialization + force-sim springs physically recoil instead of the
orb landing silently. The graph flinches when an idea is born into it.

**UPGRADE 3 — Hover Panic** (stores/toast.ts + InsightToast.svelte)
Paused dwell timer on mouseenter/focus, resume on mouseleave/blur.
Stored remaining ms at pause so resume schedules a correctly-sized
timer. CSS pairs via `animation-play-state: paused` on the progress
bar. A toast the user is reading no longer dismisses mid-sentence.

**UPGRADE 4 — Event Horizon Guard** (components/Graph3D.svelte)
If >MAX_EVENTS (200) events arrive in one tick, lastProcessedEvent
falls off the end of the array and the walk consumes ALL 200 entries
as "fresh" — GPU meltdown from 200 simultaneous births. Detect the
overflow and drop the batch with a console.warn, advancing the
high-water mark so next frame is normal.

Build + test:
- npm run check: 0 errors, 0 warnings
- npm test: 251/251 pass
- npm run build: clean static build

* test(v2.3): full e2e + integration coverage for Pulse + Birth Ritual

Post-ship verification pass — five parallel write-agents produced 229 new
tests across vitest units, vitest integration, and Playwright browser e2e.
Net suite: 361 vitest pass (up from 251, +110) and 9/9 Playwright pass on
back-to-back runs.

**toast.test.ts (NEW, 661 lines, 42 tests)**
  Silent-lobotomy batch walk proven (multi-event tick processes ALL, not
  just newest, oldest-first ordering preserved). Hover-panic pause/resume
  with remaining-ms math. All 9 event type translations asserted, all 11
  noise types asserted silent. ConnectionDiscovered 1500ms throttle.
  MAX_VISIBLE=4 eviction. clear() tears down all timers. fireDemoSequence
  staggers 4 toasts at 800ms intervals. vi.useFakeTimers + vi.mock of
  eventFeed; vi.resetModules in beforeEach for module-singleton isolation.

**websocket.test.ts (NEW, 247 lines, 30 tests)**
  injectEvent adds to front, respects MAX_EVENTS=200 with FIFO eviction,
  triggers eventFeed emissions. All 6 derived stores (isConnected,
  heartbeat, memoryCount, avgRetention, suppressedCount, uptimeSeconds)
  verified — defaults, post-heartbeat values, clearEvents preserves
  lastHeartbeat. 13 formatUptime boundary cases (0/59/60/3599/3600/
  86399/86400 seconds + negative / NaN / ±Infinity).

**effects.test.ts (EXTENDED, +501 lines, +21 tests, 51 total)**
  createBirthOrb full lifecycle — sprite count (halo + core), cosmic
  center via camera.quaternion, gestation phase (position lock, opacity
  rise, scale easing, color tint), flight Bezier arc above linear
  midpoint at t=0.5, dynamic mid-flight target redirect. onArrive fires
  exactly once at frame 139. Post-arrival fade + disposal cleans scene
  children. Sanhedrin Shatter: target goes undefined mid-flight →
  onArrive NEVER called, implosion spawned, halo blood-red, eventual
  cleanup. dispose() cleans active orbs. Multiple simultaneous orbs.
  Custom gestation/flight frame opts honored. Zero-alloc invariant
  smoke test (6 orbs × 150 frames, no leaks).

**nodes.test.ts (EXTENDED, +197 lines, +10 tests, 42 total)**
  addNode({isBirthRitual:true}) hides mesh/glow/label immediately,
  stamps birthRitualPending sentinel with correct totalFrames +
  targetScale, does NOT enqueue materialization. igniteNode flips
  visibility + enqueues materialization. Idempotent — second call
  no-op. Non-ritual nodes unaffected. Unknown id is safe no-op.
  Position stored in positions map while invisible (force sim still
  sees it). removeNode + late igniteNode is safe.

**events.test.ts (EXTENDED, +268 lines, +7 tests, 55 total)**
  MemoryCreated → mesh hidden immediately, 2 birth-orb sprites added,
  ZERO RingGeometry meshes and ZERO Points particles at spawn. Full
  ritual drive → onArrive fires, node visible + materializing, sentinel
  cleared. Newton's Cradle: target mesh scale exactly 0.001 * 1.8 right
  after arrival. Dual shockwave: exactly 2 Ring meshes added. Re-read
  live position on arrival — force-sim motion during ritual → burst
  lands at the NEW position. Sanhedrin abort path → rainbow burst,
  shockwave, ripple wave are NEVER called (vi.spyOn).

**three-mock.ts (EXTENDED)**
  Added Color.setRGB — production Three.js has it, the Sanhedrin-
  Shatter path in effects.ts uses it. Two write-agents independently
  monkey-patched the mock inline; consolidated as a 5-line mock
  addition so tests stay clean.

**e2e/pulse-toast.spec.ts (NEW, 235 lines, 6 Playwright tests)**
  Navigate /dashboard/settings → click Preview Pulse → assert first
  toast appears within 500ms → assert >= 2 toasts visible at peak.
  Click-to-dismiss removes clicked toast (matched by aria-label).
  Hover survives >8s past the 5.5s dwell. Keyboard Enter dismisses
  focused toast. CSS animation-play-state:paused on .toast-progress-
  fill while hovered, running on mouseleave. Screenshots attached to
  HTML report. Zero backend dependency (fireDemoSequence is purely
  client-side).

**e2e/birth-ritual.spec.ts (NEW, 199 lines, 3 Playwright tests)**
  Canvas mounts on /dashboard/graph (gracefully test.fixme if MCP
  backend absent). Settings button injection + SPA route to /graph
  → screenshot timeline at t=0/500/1200/2000/2400/3000ms attached
  to HTML report. pageerror + console-error listeners catch any
  crash (would re-surface FATAL 6 if reintroduced). Three back-to-
  back births — no errors, canvas still dispatches clicks.

Run commands:
  cd apps/dashboard && npm test           # 361/361 pass, ~600ms
  cd apps/dashboard && npx playwright test # 9/9 pass, ~25s

Typecheck: 0 errors, 0 warnings. Build: clean adapter-static.

* fix(graph): default /api/graph to newest-memory center, add sort param

memory_timeline PR #37 exposed the same class of bug in the graph
endpoint: the dashboard Graph page (and the /api/graph endpoint it
hits) defaulted to centering on the most-connected memory, ran BFS at
depth 3, and capped the subgraph at 150 nodes. On a mature corpus this
clustered the visualization around a historical hotspot and hid freshly
ingested memories that hadn't accumulated edges yet. User-visible
symptom: TimeSlider on /graph showing "Feb 21 → Mar 1 2026" when the
database actually contains memories through today (Apr 20).

**Backend (`crates/vestige-mcp/src/dashboard/handlers.rs`):**
- `GraphParams` gains `sort: Option<String>` (accepted: "recent" |
  "connected", unknown falls back to "recent").
- New internal `GraphSort` enum + case-insensitive `parse()`.
- Extracted `default_center_id(storage, sort)` so handler logic and
  tests share the same branching. Recent path picks `get_all_nodes(1,
  0)` (ORDER BY created_at DESC). Connected path picks
  `get_most_connected_memory`, degrading gracefully to recent if the
  DB has no edges yet.
- Default behaviour flipped from "connected" to "recent" — matches
  user expectation of "show me my recent stuff".

**Dashboard (`apps/dashboard`):**
- `api.graph()` accepts `sort?: 'recent' | 'connected'` with JSDoc
  explaining the rationale.
- `/graph/+page.svelte` passes `sort: 'recent'` when no query or
  center_id is active. Query / center_id paths unchanged — they
  already carry their own centering intent.

**Tests:** 6 new unit tests in `handlers::tests`:
- `graph_sort_parse_defaults_to_recent` (None, empty, garbage,
  "recent", "Recent", "RECENT")
- `graph_sort_parse_accepts_connected_case_insensitive`
- `default_center_id_recent_returns_newest_node` — ingest 3 nodes,
  assert newest is picked
- `default_center_id_connected_prefers_hub_over_newest` — wire a hub
  node with 3 spokes, then ingest a newer "lonely" node; assert the
  hub wins in Connected mode even though it's older
- `default_center_id_connected_falls_back_to_recent_when_no_edges`
  — fresh DB with no connections still returns newest, not 404
- `default_center_id_returns_not_found_on_empty_db` — both modes
  return 404 cleanly on empty storage

Build + test:
- cargo test -p vestige-mcp --lib handlers:: → 6/6 pass
- cargo test --workspace --lib → 830/830 pass, 0 failed
- cargo clippy -p vestige-core -p vestige-mcp --lib -- -D warnings →
  clean
- npm run check → 0 errors, 0 warnings
- npm test → 361/361 pass

Binary already installed at ~/.local/bin/vestige-mcp (copied from
cargo build --release -p vestige-mcp). New Claude Desktop / Code
sessions will pick it up automatically when they respawn their MCP
subprocess. The dashboard HTTP server on port 3927 needs a manual
relaunch from a terminal with the usual pattern:

    nohup bash -c 'tail -f /dev/null | \
        VESTIGE_DASHBOARD_ENABLED=true ~/.local/bin/vestige-mcp' \
        > /tmp/vestige-mcp.log 2>&1 & disown

* feat(v2.4): UI expansion — 8 new surfaces exposing the cognitive engine

Sam asked: "Build EVERY SINGLE MISSING UI PIECE." 10 parallel agents shipped
10 new viewports over the existing Rust backend, then 11 audit agents
line-by-line reviewed each one, extracted pure-logic helpers, fixed ~30
bugs, and shipped 549 new unit tests. Everything wired through the layout
with single-key shortcuts and a live theme toggle.

**Eight new routes**
- `/reasoning`  — Reasoning Theater: Cmd+K ask palette → animated 8-stage
  deep_reference pipeline + FSRS-trust-scored evidence cards +
  contradiction arcs rendered as live SVG between evidence nodes
- `/duplicates` — threshold-driven cluster detector with winner selection,
  Merge/Review/Dismiss actions, debounced slider
- `/dreams`     — Dream Cinema: trigger dream + scrubbable 5-stage replay
  (Replay → Cross-reference → Strengthen → Prune → Transfer) + insight
  cards with novelty glow
- `/schedule`   — FSRS Review Calendar: 6×7 grid with urgency color
  bands (overdue/today/week/future), retention sparkline, expand-day list
- `/importance` — 4-channel radar (Novelty/Arousal/Reward/Attention) with
  composite score + top-important list
- `/activation` — live spreading-activation view: search → SVG concentric
  rings with decay animation + live-mode event feed
- `/contradictions` — 2D cosmic constellation of conflicting memories,
  arcs colored by severity, tooltips with previews
- `/patterns`   — cross-project pattern transfer heatmap with category
  filters, top-transferred sidebar

**Three layout additions**
- `AmbientAwarenessStrip` — slim top band with retention vitals, at-risk
  count, active intentions, recent dream, activity sparkline, dreaming
  indicator, Sanhedrin-watch flash. Pure `$derived` over existing stores.
- `ThemeToggle` — dark/light/auto cycle with matchMedia listener,
  localStorage persistence, SSR-safe, reduced-motion-aware. Rendered in
  sidebar footer next to the connection dot.
- `MemoryAuditTrail` — per-memory Sources panel integrated as a
  Content/Audit tab into the existing /memories expansion.

**Pure-logic helper modules extracted (for testability + reuse)**
  reasoning-helpers, duplicates-helpers, dream-helpers, schedule-helpers,
  audit-trail-helpers, awareness-helpers, contradiction-helpers,
  activation-helpers, patterns-helpers, importance-helpers.

**Bugs fixed during audit (not exhaustive)**
- Trust-color inconsistency between EvidenceCard and the page confidence
  ring (0.75 boundary split emerald vs amber)
- `new Date('garbage').toLocaleDateString()` returned literal "Invalid Date"
  in 3 components — all now return em-dash or raw string
- NaN propagation in `Math.max(0, Math.min(1, NaN))` across clamps
- Off-by-one PRNG in audit-trail seeded mock (seed === UINT32_MAX yielded
  rand() === 1.0 → index out of bounds)
- Duplicates dismissals keyed by array index broke on re-fetch; now keyed
  by sorted cluster member IDs with stale-dismissal pruning
- Empty-cluster crash in DuplicateCluster.pickWinner
- Undefined tags crash in DuplicateCluster.safeTags
- Debounce timer leak in threshold slider (missing onDestroy cleanup)
- Schedule day-vs-hour granularity mismatch between calendar cell and
  sidebar list ("today" in one, "in 1d" in the other)
- Schedule 500-memory hard cap silently truncated; bumped to 2000 + banner
- Schedule DST boundary bug in daysBetween (wall-clock math vs
  startOfDay-normalized)
- Dream stage clamp now handles NaN/Infinity/floats
- Dream double-click debounce via `if (dreaming) return`
- Theme setTheme runtime validation; initTheme idempotence (listener +
  style-element dedup on repeat calls)
- ContradictionArcs node radius unclamped (trust < 0 or > 1 rendered
  invalid sizes); tooltip position clamp (could push off-canvas)
- ContradictionArcs $state closure capture (width/height weren't reactive
  in the derived layout block)
- Activation route was MISSING from the repo — audit agent created it
  with identity-based event filtering and proper RAF cleanup
- Layout: ThemeToggle was imported but never rendered — now in sidebar
  footer; sidebar overflow-y-auto added for the 16-entry nav

**Tests — 549 new, 910 total passing (0 failures)**
  ReasoningChain     42 | EvidenceCard       50
  DuplicateCluster   64 | DreamStageReplay   19
  DreamInsightCard   43 | FSRSCalendar       32
  MemoryAuditTrail   45 | AmbientAwareness   60
  theme (store)      31 | ContradictionArcs  43
  ActivationNetwork  54 | PatternTransfer    31
  ImportanceRadar    35 | + existing 361 tests still green

**Gates passed**
- `npm run check`:  0 errors, 0 warnings across 623 files
- `npm test`:       910/910 passing, 22 test files
- `npm run build`:  clean adapter-static output

**Layout wiring**
- Nav array expanded 8 → 16 entries (existing 8 + 8 new routes)
- Single-key shortcuts added: R/A/D/C/P/U/X/N (no conflicts with
  existing G/M/T/F/E/I/S/,)
- Cmd+K palette search works across all 16
- Mobile nav = top 5 (Graph, Reasoning, Memories, Timeline, Feed)
- AmbientAwarenessStrip mounted as first child of <main>
- ThemeToggle rendered in sidebar footer (was imported-but-unmounted)
- Theme initTheme() + teardown wired into onMount cleanup chain

Net branch delta: 47 files changed, +13,756 insertions, -6 deletions

* chore(release): v2.0.8 "Pulse"

Bundled release: Reasoning Theater wired to the 8-stage deep_reference
cognitive pipeline, Pulse InsightToast, Memory Birth Ritual (v2.3
Terrarium), 7 new dashboard surfaces (/duplicates, /dreams, /schedule,
/importance, /activation, /contradictions, /patterns), 3D graph
brightness system with auto distance-compensation + user slider, and
contradiction-detection + primary-selection hardening in the
cross_reference tool. Intel Mac (x86_64-apple-darwin) also flows through
to the release matrix from PR #43.

Added:
- POST /api/deep_reference — HTTP surface for the 8-stage pipeline
- DeepReferenceCompleted WebSocket event (primary + supporting +
  contradicting memory IDs for downstream graph animation)
- /reasoning route, full UI + Cmd+K Ask palette
- 7 new dashboard surfaces exposing the cognitive engine
- Graph brightness slider + localStorage persistence + distance-based
  emissive compensation so nodes don't disappear into fog at zoom-out

Fixed:
- Contradiction-detection false positives: adjacent-domain memories no
  longer flagged as conflicts (NEGATION_PAIRS wildcards removed,
  shared-words floor 2 → 4, topic-sim floor 0.15 → 0.55, STAGE 5
  overlap floor 0.15 → 0.4)
- Primary-memory selection: unified composite 0.5 × relevance + 0.2 ×
  trust + 0.3 × term_presence with hard topic-term filter, closing the
  class of bug where off-topic high-trust memories won queries about
  specific subjects
- Graph default-load fallback from sort=recent to sort=connected when
  the newest memory is isolated, both backend and client

Changed:
- Reasoning page information hierarchy: chain renders first as hero,
  confidence meter + Primary Source citation footer below
- Cargo feature split: embeddings code-only + ort-download | ort-dynamic
  backends; defaults preserve identical behavior for existing consumers
- CI release-build now gates PRs too so multi-platform regressions
  surface pre-merge
2026-04-23 02:21:11 -05:00

13 KiB

Vestige UI Roadmap — v2.1.0 and v2.2.0

Compiled April 19, 2026 from 4 parallel UI research agents (backend-to-UI gap audit, competitor scour, bleeding-edge April 2026 patterns, wow-frame design). Local-only planning doc — not for commit to main until scope is locked.


THE HEADLINE FINDING

Vestige ships ~50 KB of unreachable cognitive capability. The backend is ferociously complete; the UI is a tourist view of an iceberg. Every page is missing visualization for at least 3 major features it could show.

  • 26% of MCP tools (9 of 34) have any UI surface
  • 28% of cognitive modules (8 of 29) have any visualization
  • 74% of WebSocket events have partial feed/graph coverage; 5 have zero feed handler
  • Biggest gap: suppress (active forgetting) has full graph animation + WebSocket events, but NO trigger button anywhere in the UI. Users literally cannot trigger the signature v2.0.5 feature from the dashboard.

The v2.1.0 UI story writes itself: "Vestige v2.1 makes the invisible visible."


TOP 10 CRITICAL UI GAPS (from Agent 1, ordered by user-visible impact)

  1. suppress tool has zero frontend trigger. Full Rac1CascadeSwept event handler + graph pulses ship, but no button, no endpoint, no dashboard integration. Users can't forget anything without raw MCP access.
  2. Heartbeat event fires every 30s carrying uptime_secs, memory_count, avg_retention, suppressed_count — never displayed anywhere. Real-time health that costs nothing to show.
  3. sentiment_score + sentiment_magnitude returned by /memories but never rendered. Emotional coloring is invisible.
  4. Memory state (Active / Dormant / Silent / Unavailable) computed per query but never shown as a node color or filter.
  5. Intention page is list-only. No endpoints for status change, snooze, or complete. Users can see intentions but not act on them from the dashboard.
  6. Rac1 cascade shows animation with zero data summary. Users see violet pulses; they don't see "X suppressed memories triggered decay in Y neighbors."
  7. Synaptic tagging 9h window is invisible. Retroactive importance boost happens silently.
  8. Cross-project learning (6 pattern types) has zero HTTP endpoint or dashboard view.
  9. Consolidation internals hidden. Which nodes decayed, which got new embeddings — all computed, all hidden.
  10. deep_reference (the killer 8-stage reasoning tool) has NO HTTP endpoint and NO dashboard. The v2.0.4 headline feature is unreachable from the UI.

COMPETITOR LANDSCAPE (from Agent 2)

Currently shipping hard April 2026:

  • Zep — dashboard overhaul March 10: bulk multi-select, server-side sort, Graph Viz 2.0 (nodes sized by connection count, no render cap, click-node details). Closest competitor on graph.
  • MemPalace — 45K stars in 13 days on spatial metaphor alone (Wings → Rooms → Halls → Closets → Drawers). 13 releases in 13 days.
  • Cognee v0.3.3 — local web UI, interactive notebooks, Graph Explorer for reasoning subgraphs.
  • Letta ADE — 3-panel Agent Development Environment at app.letta.com. Context window viewer, memory blocks, archival search.

Stagnant:

  • HippoRAG (Python only, no UI)
  • claude-mem (CLI-dominant, basic localhost viewer)
  • ChatGPT memory (text list)
  • Cursor memory (removed in 2.1)

What NOBODY has (unclaimed UI territory):

  1. Ambient always-on memory widget (menu bar / tray)
  2. Watch / ring interface
  3. Voice-first memory UI
  4. Collaborative multi-user graph (Figma cursors for memory)
  5. AR/VR memory palace (native Vision Pro / Quest)
  6. Temporal time-scrubber (drag slider to rewind graph state)
  7. Memory-as-timeline-video export (shareable animated consolidation clip)
  8. Contradiction surfacing UI ("Disputes" page)
  9. FSRS retention heatmap calendar (GitHub-contribution-grid style)
  10. Live browser sidebar (Arc/Chrome panel showing memories relevant to current tab)

Vestige's visual moat that nobody else has: 3D force-directed graph + live WebSocket events + bloom + dream-mode aurora. Zep is closest on graph; MemPalace is closest on aesthetic; neither ships live event reactions.


BLEEDING-EDGE APRIL 2026 UI PATTERNS (from Agent 3)

Top 13 patterns scoured. The 5 most applicable to Vestige:

  1. Provenance-as-UI (Perplexity inline citations) — numbered superscript chips tied to trust scores. Vestige has FSRS trust; just doesn't surface it inline.
  2. Ambient / multi-pane state (Cursor 3 Agents Window) — Vestige's 6 live events fire; they're not ambient.
  3. Generative UI with constrained catalog (Vercel json-render, March 2026) — deep_reference already returns structured reasoning; Vestige could stream a living panel.
  4. Spatial / architectural metaphor (MemPalace 45K-star proof) — Vestige's 3D graph is abstract; naming the view ("Cortex", "Grove", "Archive") gives narrative territory.
  5. Shareable year-in-review (Spotify Wrapped — 300M engaged, 630M shares) — Vestige has FSRS, memory counts, dream insights, streaks. All the ingredients for a free distribution loop.

Other patterns worth tracking:

  • Apple Liquid Glass (macOS 26 / iOS 26) — translucent refractive material
  • shadcn Sera + shadcn apply (April 2026) — style system that changes geometry, not just colors
  • Dia Browser URL-bar-as-AI
  • Limitless Pendant voice-to-structured-memory
  • Granola ambient capture (invisible-by-default)
  • Figma multiplayer cursors as a primitive

Agent 3's commit: the ONE breakthrough UI for Vestige = "Provenance Scrub."

Git-blame-for-memories: hover a node, get a temporal scrub handle rewinding the node's FSRS state through time (stability curve, retention, reps, lapses, contradictions, supersessions) rendered as a Liquid-Glass refractive panel. Click any point on the scrub to see memory content at that time. Inline Perplexity citations tag every fact.

Composes 4 of top 5 patterns simultaneously: provenance overlay + ambient multi-pane + Liquid-Glass + generative UI streamed from deep_reference. Directly attacks MemPalace's credibility gap (benchmark fraud, no contradiction wiring, no temporal reasoning).

Engineering cost: 9 days. Floor: 3D scrub + trust chips in 4 days as v2.1 patch. Ceiling: full Liquid-Glass + generative panel as v2.2 headlining launch.


WOW FRAMES (from Agent 4) — ranked by ship priority

Ship in v2.1.0 (5.5 engineering days, two HN thumbnails)

1. Activation Wildfire (1 day)

  • Fires: every search call → emit ActivationSpread iteratively per hop with decay 0.7.
  • Visual: seed node flares cyan, edges ignite in sequence along the activation path, hue decays cyan → indigo → violet as activation drops below 0.1.
  • Neuroscience: Collins & Loftus 1975, spreading_activation.rs:1-58.
  • Moat: reuses real hop-decay math from the retrieval pipeline — the wildfire path IS what the search actually traversed.

2. Reconsolidation Shimmer (2 days) — HN thumbnail candidate

  • Fires: any memory({action:"get"}) → 5-minute labile window begins.
  • Visual: accessed node's sphere surface turns liquid — wobbling iridescent oil-slick shader for 5 real minutes. Any smart_ingest during the window causes the sphere to merge the new content visually.
  • Neuroscience: Nader 2000, reconsolidation.rs:405.
  • Moat: a memory being editable only when recalled is pure Nader. The shimmer is the meme shot.

3. Dream Stitching (2.5 days) — HN thumbnail candidate (video)

  • Fires: dream tool → stream DreamProgress{from_id, to_id, insight} per new connection.
  • Visual: camera auto-orbits into existing dream-mode aurora. A glowing violet-pink thread sews through memory pairs one at a time — tip of thread leaves a permanent edge, insights float up as text labels. Ends with a supernova at graph centroid.
  • Neuroscience: MemoryDreamer 5-stage consolidation.
  • Moat: dreams creating new edges is Vestige-exclusive.

Queue for v2.2.0

4. Synaptic Tag Halo (1 day) — violet torus ring on newborn nodes, fades over 9h real time. Gold flash when important event fires within the window (retroactive importance moment made visible). synaptic_tagging.rs.

5. Competition Duel (1 day) — top-3 search results duel. Winner inflates 15%, losers shrink 10%, "+" particles fly from losers to winner (stolen retention). Anderson 1994 retrieval-induced forgetting.

6. Rac1 Slow Burn (1.5 days) — suppressed seed blackens into graphite. Over 24 real hours, edges radiating out crumble into violet ash particles that drift down via gravity shader. Dead branches literally fall away.

7. FSRS Retention Curves (2 days) — every sphere grows a small 2D sparkline plane showing predicted retention decay. Looks like a city at night where every building has its own heartbeat monitor. Nodes approaching Dormant threshold pulse amber.


COMPOSED v2.1.0 AND v2.2.0 UI ROADMAP

v2.1.0 "Decide" (May 5-6 launch) — UI track

On top of the already-planned v2.1.0 scope (decide MCP tool, session_primer, Qwen3 embedding, Claude Code plugin):

Add 3 wow frames (~5.5 days):

  1. Activation Wildfire — 1 day
  2. Reconsolidation Shimmer — 2 days (HN thumbnail screenshot)
  3. Dream Stitching — 2.5 days (HN thumbnail video)

Add 5 of the top-10 gap fixes (~5 days):

  1. suppress trigger button + HTTP endpoint — 1 day
  2. Heartbeat display widget (uptime + avg retention + suppressed count) — 0.5 day
  3. Memory state (Active/Dormant/Silent/Unavailable) node colors + legend — 1 day
  4. Intention update/snooze/complete endpoints + UI — 1 day
  5. deep_reference dashboard page (the 8-stage reasoning viewer) — 1.5 days

Total v2.1.0 UI scope: ~10.5 engineering days on top of the existing 19.5 day Qwen3 + decide + plugin scope. Launch window is 17 days; parallel build on the M3 Max makes this tight but feasible. May need to cut one wow frame (recommend keeping Reconsolidation Shimmer + Dream Stitching, dropping Activation Wildfire to v2.1.1 if time-pressed).

v2.2.0 "Provenance" (target late May / early June)

Headline: "Git-blame for memories." The Provenance Scrub compose (Agent 3's breakthrough).

  • 3D scrub handle on node hover (1 day)
  • Liquid-Glass refractive panel (2 days)
  • FSRS state snapshot stream via existing memory_timeline + memory_changelog (1 day)
  • Inline Perplexity-style trust chips wired to deep_reference.evidence[] (1.5 days)
  • Generative side-panel streaming deep_reference.reasoning json-render-style (2 days)
  • Polish + demo clip (1.5 days)

Plus the remaining 4 wow frames (Synaptic Tag Halo, Competition Duel, Rac1 Slow Burn, FSRS Retention Curves — 5.5 days).

Total v2.2.0 UI scope: ~14.5 days. Ship target: June graduation week (June 13).

v2.3.0 "Unclaimed Territory" (post-graduation)

Pick one of the "nobody has this" territories from Agent 2:

  • Ambient menubar widget (2 days)
  • Temporal time-scrubber on the main graph (3 days)
  • Contradiction surfacing "Disputes" page (2 days)
  • FSRS retention heatmap-calendar (1 day — GitHub-contribution-grid style)
  • Memory-as-timeline-video export via canvas-record / gifski-wasm (3 days)

Ship 2-3 of these in v2.3. Each is an unclaimed moat.


WHAT NOT TO DO

  • Don't add memory palace metaphor (Wings/Rooms/Halls). MemPalace owns that narrative territory with 45K stars. Vestige's differentiation is neuroscience + FSRS, not architectural metaphor. Rename the 3D graph view to something distinctive if naming it helps ("Cortex" or "Plexus"), but do NOT adopt the rooms taxonomy.
  • Don't chase every 2026 pattern. Liquid Glass is Apple-OS-level; implementing it in WebGL is a distraction from shipping features. Save for v2.2 selectively.
  • Don't build mobile yet. Adoption curve isn't there. Desktop dashboard + MCP server first.
  • Don't build multi-user. Single-user local is the AGPL-3.0 story. Multi-tenant is vestige-cloud (proprietary), separate roadmap.

Cross-research composition insights (found by me during synthesis)

Never-composed #1: Agent 1's gap (suppress has no frontend trigger) + Agent 4's Reconsolidation Shimmer + Agent 3's Provenance Scrub. Three pieces of the "make the invisible visible" story. Ship them together as v2.1.0 UI narrative.

Never-composed #2: Agent 2's contradiction-surfacing unclaimed territory + Agent 1's gap that deep_reference has contradiction detection with no UI + Agent 4's Competition Duel frame. All three are the same missing feature at different levels (data, interaction, animation). Ship as v2.2 "Disputes" page + Competition Duel micro-animation together.

Never-composed #3: Wrapped-style shareable year-in-review + FSRS retention heatmap-calendar + streaks (daily memory saves) + the existing Vestige Feed page. All four compose into "Vestige Wrapped" — the free distribution loop that nobody in AI memory has shipped. Ship as v2.3 "Year in Memory" — summer 2026, after launch stabilizes.


What this document is FOR

  • Reference when scoping v2.1.0 and v2.2.0 UI work
  • Guide when the M3 Max arrives and you start the Qwen3 + decide + session_primer build — you'll know which UI frames to interleave
  • Moat argument for the HN launch — Vestige's backend-to-UI ratio is 3:1, the fix is the launch story
  • Defence against scope creep — the NOT-to-do list should be re-read before every design decision

Sources: 4 parallel research agents (backend audit, competitor scour, April 2026 patterns, wow-frame design), ~280+ file reads, 50+ web sources. Full raw outputs preserved in Claude Code session logs.