-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem
When the server starts/stops, serverStore calls syncProjectRunning() which directly mutates projectStore.projects and projectStore.activeProject — reaching across store boundaries. This creates two sources of truth for is_running:
serverStore.isRunning(current project, set optimistically)project.is_running(per project in projectStore, set by manual patch)
The syncProjectRunning function (serverStore.ts:6-17) hand-rolls a map over store.projects that must match projectStore's internal shape — a fragile duplication. If activeProject is not null, it also patches that separately. The backend is the actual canonical source (serverCommands.isRunning), but neither store consistently defers to it.
Race conditions are possible: projectStore.load() could overwrite the manual patch, or the server-status-changed event could fire setStatus() without calling syncProjectRunning.
Proposed Interface
Zero caller changes. Replace syncProjectRunning() with projectStore.load():
// serverStore.ts — start action (stop is symmetrical)
start: async (projectId) => {
if (get().starting || get().isRunning) return
set({ starting: true })
try {
const result = await serverCommands.start(projectId)
set({ isRunning: true, port: result.port, https: result.https, starting: false })
projectCommands.updateRunningStatus(projectId, true).catch(() => {})
useProjectStore.getState().load() // <-- replaces syncProjectRunning()
} catch (e) {
console.error('Failed to start server:', e)
set({ starting: false })
}
},syncProjectRunning is deleted entirely. projectStore.load() fetches the authoritative project list from the backend, which already includes the correct is_running state.
Dependency Strategy
- Category: In-process + remote-owned (Tauri backend is canonical)
- serverStore already imports projectStore — no new dependency
- Direction: serverStore calls
projectStore.load()(a public method), neversetStatedirectly - projectStore owns its own state; serverStore triggers a refresh, not a mutation
- The Tauri event path (
server-status-changed->setStatus) continues to work independently
Testing Strategy
- New boundary tests: Call
serverStore.start(), verifyprojectStore.load()was called (or that projects reflect the updated state after an async tick) - Old tests to delete: Any test that asserts
syncProjectRunningpatches specific fields - Test environment: Standard Vitest with mocked IPC commands
Implementation Recommendations
- serverStore should own
isRunning,port,httpsfor the current project - projectStore should own
project.is_runningas a backend-derived field refreshed viaload() - No store should directly
setStateon another store — call public methods only - The brief hydration lag (~1-5ms local SQLite read) between serverStore's optimistic update and projectStore's authoritative refresh is imperceptible
- If the sidebar dot lag is ever noticeable, add
projectStore.setRunning(id, running)as a projectStore-owned method rather than reverting to cross-store mutation