Skip to content

perf(workspaces): switch projects without re-running startup or reloading beads - #2

Draft
andreiverdes wants to merge 1 commit into
pr1-registry-durabilityfrom
pr2-workspace-switch
Draft

perf(workspaces): switch projects without re-running startup or reloading beads#2
andreiverdes wants to merge 1 commit into
pr1-registry-durabilityfrom
pr2-workspace-switch

Conversation

@andreiverdes

Copy link
Copy Markdown
Member

Layer 2 of 3. Base: pr1-registry-durability (#1).

Switching to a project already verified this session re-ran the whole startup sequence: health check, then a fresh bd load. Now a workspace that passed its health check keeps that pass for the session, and each workspace's view state (epic tree, activity feed, pipeline) is held in a per-workspace cache, so switching back paints immediately.

On not duplicating bd state — the constraint CONTRIBUTING sets:

  • The cache is ephemeral and in-memory, cleared on restart. Nothing is persisted; it is never a second source of truth.
  • It is invalidated by the existing bd change signal, so a CLI-side edit still lands. bd stays authoritative; this only avoids re-asking for an answer we already have and have not been told is stale.
  • Keyed by workspace id and bounded at 6 payloads per cache.

Verified by hand against tauri:dev, since test:e2e collects nothing today (playwright.config.ts ignores every spec) and the client throws RpcUnavailableError without a Tauri runtime:

  • Warm switch alpha -> beta -> alpha: skeleton only on each project's first visit, one bd epics per switch.
  • Switch storm (~10 rapid clicks): no wrong-project tree, no stuck spinner, last click wins.
  • Beads -> Activity -> Beads -> Activity: this case is what surfaced the need for the feed and pipeline session caches.

…ding beads

Every workspace switch re-ran the startup health check — which unmounts
the route and shows "Starting up..." — and then refetched the epic tree
from bd, so a project that had been on screen seconds earlier came back
as a full-screen spinner followed by a skeleton.

- StartupGate remembers which workspaces passed a check this session and
  adopts a switch to one of them silently: no re-check, children stay
  mounted. A workspace's first visit still runs the check (that is the
  one that can fail) and a workspace that later fails loses its pass.
  The id recorded is the one the check covered, not the live cookie, so a
  switch that lands mid-check gets its own check instead of inheriting
  the previous workspace's verdict.
- Routes now resolve the active workspace from the cookie through a
  subscription (hooks/use-active-workspace.ts, and the equivalent
  listener in useWorkspaceLifecycle) since they are no longer remounted.
  A cookie pointing at an unknown id keeps the current workspace instead
  of silently jumping to the first one.
- lib/epics-session-cache.ts keeps the last tree per databasePath (LRU,
  6 entries) so a switch back paints immediately and refreshes in the
  background. It mirrors rendered state, so beads closed or deleted
  locally do not reappear. A switch also bumps the load generation and
  raises isLoading, so the previous project's beads cannot render under
  the new project's name.
- lib/workspace-actions.ts extracts the activate/unregister sequences the
  dashboard and the lifecycle hook each had their own copy of, and
  useWorkspaceLifecycle drops its dead handleWorkspaceChange (the cookie
  listener is the single switch path), calls rpc handlers through the
  proxy instead of capturing them at module load, and no longer writes
  the cookie from inside a setState updater.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant