Bootstrap default thread on server welcome and simplify dev runner behavior#101
Bootstrap default thread on server welcome and simplify dev runner behavior#101juliusmarminge merged 6 commits intomainfrom
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds server auto-bootstrap that finds or creates a project and thread on connect, includes their IDs in Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Web Client
participant Server as WS Server
participant DB as Project/Thread Store
Client->>Server: WebSocket Connect
Server->>DB: Find non-deleted project for cwd
alt project not found
Server->>DB: Create project (returns projectId)
end
Server->>DB: Find non-deleted thread for project
alt thread not found
Server->>DB: Create thread (returns threadId)
end
Server->>Client: Push serverWelcome (includes optional bootstrapProjectId/bootstrapThreadId)
Client->>Client: Sync snapshot/state
alt client pathname is root and IDs present and not handled
Client->>Client: Dispatch SET_PROJECT_EXPANDED for projectId
Client->>Router: Navigate to /$threadId (replace: true)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Bootstrap IDs missing when project/thread already exist
- Added an else branch to assign welcomeBootstrapProjectId and welcomeBootstrapThreadId from the existing project and thread when they already exist.
- ✅ Fixed: Pathname dependency causes effect re-subscription on every navigation
- Replaced the pathname dependency with a pathnameRef that tracks the current pathname without being in the useEffect dependency array, preventing teardown/re-subscription on navigation.
Or push these changes by commenting:
@cursor push b003595d60
Preview (b003595d60)
diff --git a/apps/server/src/wsServer.ts b/apps/server/src/wsServer.ts
--- a/apps/server/src/wsServer.ts
+++ b/apps/server/src/wsServer.ts
@@ -387,6 +387,9 @@
});
welcomeBootstrapProjectId = bootstrapProjectId;
welcomeBootstrapThreadId = threadId;
+ } else {
+ welcomeBootstrapProjectId = bootstrapProjectId;
+ welcomeBootstrapThreadId = existingThread.id;
}
}
diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx
--- a/apps/web/src/routes/__root.tsx
+++ b/apps/web/src/routes/__root.tsx
@@ -125,6 +125,8 @@
const queryClient = useQueryClient();
const navigate = useNavigate();
const pathname = useRouterState({ select: (state) => state.location.pathname });
+ const pathnameRef = useRef(pathname);
+ pathnameRef.current = pathname;
const lastConfigIssuesSignatureRef = useRef<string | null>(null);
const handledBootstrapThreadIdRef = useRef<string | null>(null);
@@ -187,7 +189,7 @@
projectId: payload.bootstrapProjectId,
expanded: true,
});
- if (pathname !== "/") {
+ if (pathnameRef.current !== "/") {
return;
}
if (handledBootstrapThreadIdRef.current === payload.bootstrapThreadId) {
@@ -249,7 +251,7 @@
unsubWelcome();
unsubServerConfigUpdated();
};
- }, [dispatch, navigate, pathname, queryClient]);
+ }, [dispatch, navigate, queryClient]);
return null;
}861a786 to
567b095
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
apps/server/src/wsServer.test.ts (1)
440-507: Add a bootstrap reuse test case to lock behavior.This test covers the creation path well. Please add a case with an already-existing project/thread and assert welcome still includes
bootstrapProjectId/bootstrapThreadId(and no extra default thread is created).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/server/src/wsServer.test.ts` around lines 440 - 507, Add a new test that starts a server with autoBootstrapProjectFromCwd true but with an existing project/thread already present, using the same helpers (createTestServer, connectWs, waitForMessage, sendRequest) and channels (WS_CHANNELS, ORCHESTRATION_WS_METHODS); on connect assert the welcome message still contains bootstrapProjectId and bootstrapThreadId, then call getSnapshot and assert snapshot.projects contains the existing project (by id/workspaceRoot/title/defaultModel) and snapshot.threads contains the existing thread (by id/projectId/title/model) and no additional default thread was created (e.g., thread count for that project did not increase or no second thread with title "New thread" exists).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/server/src/wsServer.ts`:
- Around line 371-390: The bootstrap reuse path fails to set
welcomeBootstrapProjectId/welcomeBootstrapThreadId when an existing thread is
found and also doesn't ensure the thread has the default title "New thread";
update the lookup and assignment: when checking snapshot.threads (the
snapshot.threads.find call) restrict the match to projectId ===
bootstrapProjectId, deletedAt === null AND title === "New thread" (or, if any
thread is acceptable, still assign IDs), and if an existing thread is found
assign welcomeBootstrapProjectId = bootstrapProjectId and
welcomeBootstrapThreadId = existingThread.id (or equivalent identifier used in
the thread object) so that the welcome payload is populated; otherwise keep the
current creation path that uses ThreadId.makeUnsafe/crypto.randomUUID and
orchestrationEngine.dispatch({ type: "thread.create", ... }) to create and set
the bootstrap IDs.
In `@apps/web/src/routes/__root.tsx`:
- Around line 178-203: In the onServerWelcome callback registered to
unsubWelcome, add an immediate disposed check after the await syncSnapshot()
(i.e., insert "if (disposed) return;") so the async continuation doesn't call
dispatch() or navigate() on an unmounted/disposed effect; also move the
assignment to handledBootstrapThreadIdRef.current so it happens after successful
navigation (set it after the await navigate(...) completes) to ensure the flag
only flips when navigation actually finished.
---
Nitpick comments:
In `@apps/server/src/wsServer.test.ts`:
- Around line 440-507: Add a new test that starts a server with
autoBootstrapProjectFromCwd true but with an existing project/thread already
present, using the same helpers (createTestServer, connectWs, waitForMessage,
sendRequest) and channels (WS_CHANNELS, ORCHESTRATION_WS_METHODS); on connect
assert the welcome message still contains bootstrapProjectId and
bootstrapThreadId, then call getSnapshot and assert snapshot.projects contains
the existing project (by id/workspaceRoot/title/defaultModel) and
snapshot.threads contains the existing thread (by id/projectId/title/model) and
no additional default thread was created (e.g., thread count for that project
did not increase or no second thread with title "New thread" exists).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
apps/server/src/wsServer.test.tsapps/server/src/wsServer.tsapps/web/src/routes/__root.tsxapps/web/src/store.tsapps/web/src/wsNativeApi.test.tspackages/contracts/src/ws.ts
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: SET_PROJECT_EXPANDED always lost due to sync race
- Changed syncSnapshot() to return the in-flight syncPromise when syncing is true, so the welcome handler actually waits for the sync to complete and dispatches SET_PROJECT_EXPANDED against a populated store.
Or push these changes by commenting:
@cursor push 1f0e7a6eae
Preview (1f0e7a6eae)
diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx
--- a/apps/web/src/routes/__root.tsx
+++ b/apps/web/src/routes/__root.tsx
@@ -149,20 +149,25 @@
}
};
- const syncSnapshot = async () => {
+ let syncPromise: Promise<void> = Promise.resolve();
+
+ const syncSnapshot = (): Promise<void> => {
if (syncing) {
pending = true;
- return;
+ return syncPromise;
}
syncing = true;
- try {
- pending = false;
- await flushSnapshotSync();
- } catch {
- // Keep prior state and wait for next domain event to trigger a resync.
- } finally {
- syncing = false;
- }
+ syncPromise = (async () => {
+ try {
+ pending = false;
+ await flushSnapshotSync();
+ } catch {
+ // Keep prior state and wait for next domain event to trigger a resync.
+ } finally {
+ syncing = false;
+ }
+ })();
+ return syncPromise;
};
void syncSnapshot().catch(() => undefined);There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/web/src/routes/__root.tsx (1)
180-204:⚠️ Potential issue | 🟠 MajorAdd a post-await disposal guard in the welcome handler.
After
await syncSnapshot()(Line 182), the callback may continue after cleanup and still rundispatch/navigate. Add an immediatedisposedcheck before further side effects.Suggested fix
const unsubWelcome = onServerWelcome((payload) => { void (async () => { await syncSnapshot(); + if (disposed) return; if (!payload.bootstrapProjectId || !payload.bootstrapThreadId) { return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/routes/__root.tsx` around lines 180 - 204, The welcome handler can continue after cleanup; add a post-await disposed guard: introduce a local flag (e.g., let disposed = false) captured by the async handler created for onServerWelcome, check if (disposed) return immediately after await syncSnapshot() to avoid dispatch/navigate after teardown, and ensure the unsubscribe returned by onServerWelcome is wrapped or augmented so calling unsubWelcome() sets disposed = true; reference the existing unsubWelcome, onServerWelcome, syncSnapshot, handledBootstrapThreadIdRef, pathnameRef, dispatch and navigate identifiers when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/web/src/routes/__root.tsx`:
- Around line 180-204: The welcome handler can continue after cleanup; add a
post-await disposed guard: introduce a local flag (e.g., let disposed = false)
captured by the async handler created for onServerWelcome, check if (disposed)
return immediately after await syncSnapshot() to avoid dispatch/navigate after
teardown, and ensure the unsubscribe returned by onServerWelcome is wrapped or
augmented so calling unsubWelcome() sets disposed = true; reference the existing
unsubWelcome, onServerWelcome, syncSnapshot, handledBootstrapThreadIdRef,
pathnameRef, dispatch and navigate identifiers when making this change.
70ac3b5 to
4ac2e0d
Compare
- Create or reuse cwd project, create initial thread, and include bootstrap IDs in `server.welcome` - Update web event routing to expand the project and auto-navigate from `/` to the bootstrapped thread - Extend WebSocket welcome contract and add server/web tests for bootstrap payload handling
- Track latest `pathname` in a ref for bootstrap event handling - Prevent effect resubscriptions by removing `pathname` from deps - Keep auto-navigation gated to root route using current path
- Server welcome now includes bootstrap project/thread IDs when cwd project/thread already exist - Added regression test for reconnecting with persisted bootstrap state - Root route ignores post-dispose welcome handling and marks thread as handled after navigate
- Fetch orchestration snapshot when bootstrap project/thread IDs are absent - Resolve project by current workspace and choose latest non-deleted thread - Continue auto-navigation and expansion using resolved fallback IDs
- Remove snapshot fallback lookup for missing bootstrap project/thread IDs - Only expand project and navigate when server payload includes both IDs
ae4e353 to
9e8363c
Compare
- inject `NodeServices.layer` into sqlite persistence setup in `wsServer.test.ts` - fixes test runtime dependencies for existing-state bootstrap path

Summary
"New thread"exists, then includesbootstrapProjectId/bootstrapThreadIdinserver.welcome./to the bootstrapped thread.scripts/dev-runner.mjsby removing CLI flag-forwarding/state-dir override plumbing and deleting related tests/docs.~/.t3/userdata(noT3CODE_STATE_DIRoverride).Testing
apps/server/src/wsServer.test.ts: verifies welcome payload includes bootstrap IDs and snapshot includes bootstrapped project/thread.apps/web/src/wsNativeApi.test.ts: verifies welcome payload handling preserves optional bootstrap IDs.apps/server/integration/orchestrationEngine.integration.test.ts: removed shutdown-failure temp-dir cleanup case due to harness disposal simplification.apps/web/src/lib/diffRendering.test.ts: removed cache-scope-specific key test to match cache key usage changes.Note
Medium Risk
Changes startup orchestration by auto-creating/reusing projects/threads and wiring those IDs into client routing, which can affect persisted state and navigation flows. Scope is contained to bootstrap/welcome plumbing with updated contract validation and tests.
Overview
Auto-bootstrap now ensures both a cwd project and a default
"New thread"exist, reusing non-deleted records when present, and includesbootstrapProjectId/bootstrapThreadIdin theserver.welcomepush.The web client consumes these optional IDs to sync the snapshot, expand the bootstrapped project, and (when currently on
/) redirect to the bootstrapped thread; the store addsSET_PROJECT_EXPANDEDto support explicit expansion. WebSocket contracts/tests are updated to validate/preserve the optional bootstrap IDs, and server tests now assert bootstrap ID stability across restarts with a real sqlite persistence layer.Written by Cursor Bugbot for commit 8effcf3. This will update automatically on new commits. Configure here.
Note
Bootstrap a default project and thread on server start and include
bootstrapProjectIdandbootstrapThreadIdinserverWelcomewsServer.createServernow creates or reuses a project and a thread for the current cwd whenautoBootstrapProjectFromCwdis enabled and addsbootstrapProjectIdandbootstrapThreadIdto theserverWelcomepayload; the web EventRouter consumes these fields to expand the project and navigate to the thread, and tests and schemas are updated accordingly.📍Where to Start
Start in the server bootstrap flow inside
wsServer.createServerin wsServer.ts, then review the UI handling in apps/web/src/routes/__root.tsx.Macroscope summarized 8effcf3.
Summary by CodeRabbit
New Features
Updates
Tests