perf: Faster task startup (reserve worktrees + direct CLI spawn)#674
perf: Faster task startup (reserve worktrees + direct CLI spawn)#674rabanspiegel merged 36 commits intomainfrom
Conversation
- Add WorktreePoolService to pre-create reserve worktrees in background - Reserves are claimed instantly (~100ms) instead of sync creation (3-7s) - Background replenishment after each claim - 5-minute freshness check discards stale reserves - Cleanup reserves on app shutdown - Fallback to sync creation when no reserve available
- Show task in UI immediately after worktree is ready - Move database save, telemetry, and issue seeding to background - Reduces perceived task creation time from ~230ms to ~120ms
- Scan common worktree directories on app startup (after 2s delay) - Clean up reserves in parallel for fast cleanup - Handles crashes/forced quits that leave orphaned reserves
…ation - Add direct PTY spawn (bypasses shell config loading, saves ~1-2s) - Add USE_RESERVE toggle in App.tsx for benchmarking - Add USE_DIRECT_SPAWN toggle in TerminalSessionManager.ts - Add benchmark logging for worktree and PTY timing - Defer worktree shell spawn to not compete with CLI - Clean up WorktreePoolService logging - Remove warm PTY code (replaced by direct spawn)
- Remove USE_RESERVE and USE_DIRECT_SPAWN toggles - Remove benchmark timing passthrough (taskCreateStartTime, worktreeTimeMs) - Remove logFirstDataTiming() and benchmark box output - Always use reserve worktree (with fallback to create) - Always use direct spawn for provider CLIs (shell-based for terminals) Benchmark results (7059ms → 3706ms, 47% faster): - Reserve worktree: 2354ms → 85ms - Direct CLI spawn: 3070ms → 1425ms
- Increase MAX_RESERVE_AGE_MS from 5 to 30 minutes - When direct-spawned CLI exits (ctrl+c, /exit), spawn shell automatically - User can continue working in terminal (git commands, etc.) - Shell respawn notifies renderer via pty:shellSpawned event
- Don't delete owner on direct CLI exit (needed for shell respawn) - Remove unnecessary 100ms delay - Shell now spawns immediately and user can type
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Resolve conflicts keeping: - Direct CLI spawn (providerId prop) for faster startup - Main's conversation/multi-chat improvements - Main's disableSnapshots and forwardRef for TerminalPane - Main's improved skipResume logic for Claude sessions
d37dc3a to
fe83464
Compare
|
Bugbot Autofix prepared a fix for all 6 of the 6 bugs found in the latest run.
|
- Add await to startPty in CLI exit handler (was returning Promise, not IPty) - Add WebContents destroyed cleanup to pty:startDirect (prevent orphaned processes) - Add resume flag support to startDirectPty (conversation continuity) - Show toast warning on database save failure (user awareness) - Warn user if base ref switch fails during reserve claim - Handle glob patterns properly in preserveFiles (.env.*.local)
…spawn Without this check, Claude would start with -c -r flag even for new tasks where no session exists, causing it to exit immediately and trigger shell spawn instead of starting the CLI.
|
Bugbot Autofix prepared a fix for 2 of the 2 bugs found in the latest run.
|
Resolve conflicts with provider->agent rename while preserving direct CLI spawn optimization (providerId prop).
|
Bugbot Autofix prepared a fix for all 6 of the 6 bugs found in the latest run.
|
Remove E2E timing console.log that was appearing in production. Removes clickTime and worktreeMs tracking used only for benchmarks.
ae5828e to
34b21af
Compare
Dead code from optimization work - tracked spawn times but was never consumed by the renderer.
25c7bb2 to
1300bf4
Compare
When ptyStartDirect fails because the CLI path isn't in the cache (e.g., cache corruption, CLI uninstalled, race at startup), fall back to shell-based spawn instead of leaving the terminal in a broken state.
src/renderer/App.tsx
Outdated
| setSelectedProject((prev) => | ||
| prev ? { ...prev, tasks: prev.tasks?.filter((t) => t.id !== groupId) } : null | ||
| ); | ||
| setActiveTask((current) => (current?.projectId === selectedProject.id ? null : current)); |
There was a problem hiding this comment.
Incorrect task ID check clears wrong active task
Medium Severity
The setActiveTask calls in the multi-agent background task creation use current?.projectId === selectedProject.id to check which task to update, but this incorrectly matches ANY task in the project rather than the specific optimistic task (groupId). Since worktree creation happens asynchronously, users can switch to a different task during this time. If an error occurs, removeOptimisticTask clears the wrong task. Similarly in the success path, the finalTask overwrites whatever task is currently active in the project rather than updating only the original optimistic task.
Additional Locations (1)
| autoApprove={autoApproveEnabled} | ||
| env={undefined} | ||
| keepAlive={true} | ||
| disableSnapshots={false} |
There was a problem hiding this comment.
Terminal renders before conversation loads causing ID mismatch
Medium Severity
Removing the conversationsLoaded guard causes TerminalPane to render immediately with a fallback terminalId (${agent}-main-${task.id}). When conversations subsequently load and a non-main conversation is the active one, terminalId changes to ${agent}-chat-${conversationId}. This triggers a session switch: the old session detaches (but stays alive due to keepAlive={true}) while a new session spawns. This creates orphaned PTY processes and causes visible terminal flicker for users with non-main conversations active.
1. Check current?.id === groupId instead of matching any task in the project by projectId (affects removeOptimisticTask and success path) 2. Restore conversationsLoaded guard for TerminalPane to prevent terminal ID mismatch when conversations load after initial render
1. Use safeSendToOwner() in direct spawn and shell respawn handlers to prevent crashes when WebContents is destroyed during data send 2. Clean up reserve worktree when deleting a project to prevent orphaned worktrees accumulating on disk
| maybeMarkProviderFinish(id, exitCode, signal); | ||
| // DON'T delete owner/listeners - shell will be spawned and reuse them | ||
| listeners.delete(id); | ||
| }); |
There was a problem hiding this comment.
Memory leak in direct PTY fallback exit handler
Medium Severity
The pty:startDirect exit handler intentionally skips calling owners.delete(id) because for direct CLI spawns, a shell is spawned afterward that reuses the owner. However, when the direct spawn falls back to startPty (lines 402-418), no subsequent shell is spawned because the PTY record lacks isDirectSpawn: true. The fallback shell exits normally but owners is never cleaned up, causing WebContents references to accumulate until the window is destroyed.
| disableSnapshots={ | ||
| !conversations.find((c) => c.id === activeConversationId)?.isMain | ||
| } | ||
| disableSnapshots={false} |
There was a problem hiding this comment.
Snapshot setting change enables snapshots for all conversations
Medium Severity
The disableSnapshots prop was changed from conditionally checking !conversations.find((c) => c.id === activeConversationId)?.isMain to a hardcoded false. This means terminal snapshots are now saved for all conversations, including non-main chats, which was previously disabled by design. This behavioral change appears unintentional given the PR is focused on performance optimizations, not snapshot behavior.
1. Wrap applySnapshot in try/catch to prevent unhandled rejections 2. Fix misleading comment about owner/listeners cleanup in exit handler
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
|
lgtm :) |
Summary
Task creation is now ~50% faster through two optimizations:
1. Reserve Worktree Pool
2. Direct CLI Spawn
Benchmark Results
A couple more notes here: This is all vs Claude. Claude itself takes 1.0 - 1.5 seconds to spawn (on my machine). We should look into improving the terminal mount times. Also percentage improvement depends on how long the worktree creation would've taken synchronously
Testing Checklist
Note
Improves perceived and actual task startup time by removing synchronous worktree creation and shell startup overhead.
WorktreePoolServiceto pre-create/claim worktrees; startup orphan cleanup; shutdown cleanup; IPC endpoints (worktree:ensureReserve|hasReserve|claimReserve|removeReserve) exposed via preloadpty:startDirectpath using cached provider CLI; minimal env passthrough; falls back to shell; auto-spawns a shell after CLI exit; safer WC destroy handlingTerminalSessionManager/TerminalPane/registry acceptproviderId; smarter snapshot vs resume logic; reuse detection; performance timing hooksWritten by Cursor Bugbot for commit f6b52bc. This will update automatically on new commits. Configure here.