Skip to content

Reset frontend state on database hot-swap #900

Description

@malibio

Context

PR #899 implemented the AppServices container for hot-swappable database connections (Issue #894). The backend correctly switches databases at runtime — services are atomically replaced, MCP server and domain event forwarder are restarted with the new NodeService.

However, the frontend database-changed event handler only logs the event. It does not reset cached state from the old database, causing stale data to persist in the UI after a switch.

Problem

When the user switches databases, these frontend caches still hold data from the old database:

Store Stale Data Risk
SharedNodeStore Cached node objects Medium — user sees old node content
ReactiveStructureTree Parent→child hierarchy High — wrong children displayed
collectionsData Collections list + member caches High — sidebar shows old collections
Tab state Open tabs reference old node IDs Medium — tabs point to non-existent nodes
Status bar In-flight messages ("Embedding 3/10...") Low — confusing stale progress

Acceptance Criteria

  • On database-changed event, flush any pending writes before clearing state (sharedNodeStore.flushAllPending())
  • Clear the SharedNodeStore node cache (sharedNodeStore.clearAll())
  • Clear ReactiveStructureTree — no clear() method exists yet, so either add one or re-instantiate
  • Reset collections data (collectionsData.reset()) and reload from new database (collectionsData.loadCollections())
  • Reset tab state to default (resetTabState() — resets to single Daily Journal tab)
  • Reload app settings (loadSettings() — updates activeDatabasePath display)
  • Clear any in-flight status bar messages (statusBar store)
  • Verify domain event forwarder is emitting events from the new database after switch (should already work — just verify)

Implementation Guide

Where to implement

File: src/lib/components/layout/app-shell.svelte

The database-changed listener is at approximately line 298-302:

// CURRENT (just logs):
unlistenDatabaseChanged = listen('database-changed', (event) => {
  log.info('Database changed to:', event.payload);
});

What to implement

Replace with a handler that resets all cached state:

unlistenDatabaseChanged = listen('database-changed', async (event) => {
  log.info('Database changed, resetting frontend state...', event.payload);

  // 1. Flush any pending writes to OLD database before clearing
  await sharedNodeStore.flushAllPending();

  // 2. Clear node cache (old database nodes)
  sharedNodeStore.clearAll();

  // 3. Clear structure tree (old parent→child relationships)
  //    NOTE: ReactiveStructureTree has no clear() method yet — 
  //    you'll need to add one, or find another way to reset it.
  //    It's imported as `structureTree` in app-shell.

  // 4. Reset and reload collections from new database
  collectionsData.reset();
  await collectionsData.loadCollections();

  // 5. Reset tabs to default (old tabs reference non-existent node IDs)
  resetTabState();

  // 6. Reload settings (updates activeDatabasePath in UI)
  await loadSettings();

  // 7. Clear status bar (remove stale progress messages)
  statusBar.clear();  // or similar — check available methods

  log.info('Frontend state reset complete for new database');
});

ReactiveStructureTree — needs a clear() method

File: src/lib/stores/reactive-structure-tree.svelte.ts

This store currently has no clear() or reset() method. You'll need to add one that:

  • Clears the internal parent→children map
  • Clears any cached relationship data
  • Notifies reactive subscribers of the change

Look at the existing snapshot() and restore() methods for patterns on how to reset internal state.

Available APIs (already exist)

Store Method What it does
SharedNodeStore clearAll() Clears all cached nodes, versions, pending updates
SharedNodeStore flushAllPending() Flushes pending persistence operations
collectionsData reset() Clears all cached collections and members
collectionsData loadCollections() Fetches fresh collections from backend
Navigation resetTabState() Resets to single Daily Journal tab
Settings loadSettings() Reloads settings from backend

Imports needed in app-shell.svelte

Most stores are already imported. You may need to add:

  • loadSettings from $lib/stores/settings
  • Access to ReactiveStructureTree instance (check how it's currently referenced)

Testing

  • Manual: Switch database from settings → sidebar shows new database's collections
  • Manual: Switch database → open tabs reset to Daily Journal
  • Manual: Switch database → navigate nodes → shows new database content, not cached old content
  • Manual: Switch database while embedding is in progress → status bar clears, no stale progress
  • Manual: Create a node in new database after switch → domain events fire correctly, node appears in UI

Notes

  • The backend hot-swap is fully implemented (PR AppServices container for hot-swappable database connections #899). This issue is frontend-only.
  • The domain event forwarder is already restarted on the backend — future events will come from the new database. This issue is about clearing existing cached state so the UI doesn't show stale data from the old database.
  • Embedding processor: if a switch happens mid-queue, unprocessed nodes remain stale-flagged in the old database. When that database is opened again, the processor picks them up. No frontend work needed for this.

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions