Add existing worktree import flow#16
Conversation
There was a problem hiding this comment.
Thank you very much for this and sorry for the late reply. Somehow I wasn't receiving notifications for PRs opened here....
Code Review
Overall: Well-structured PR. The implementation follows codebase patterns closely, the externalWorktree flag is properly threaded through close/merge/persistence paths, and the backend worktree parsing is solid. Security is handled correctly (path validation, array-form exec, preload allowlist).
Important Issues
1. Misleading close message in TilingLayout.tsx:117-119
The force-close confirmation says "The worktree and branch will be deleted" for external worktrees, which is incorrect — they're intentionally kept. This file wasn't updated in the PR:
// Current (incorrect for external worktrees):
const msg = task.directMode
? 'Close this task? Running agents and shells will be stopped.'
: 'Close this task? The worktree and branch will be deleted.';
// Suggested:
const msg = task.directMode || task.externalWorktree
? 'Close this task? Running agents and shells will be stopped.'
: 'Close this task? The worktree and branch will be deleted.';2. No duplicate import guard in createImportedTask (src/store/tasks.ts)
The UI filters already-tracked paths via trackedWorktreePaths, but createImportedTask itself has no guard against importing the same worktree path twice. A rapid double-click or programmatic call could create duplicates. Compare with createDirectTask which has the hasDirectModeTask() guard at line 144. Suggestion:
const allTaskIds = [...store.taskOrder, ...store.collapsedTaskOrder];
if (allTaskIds.some((id) => store.tasks[id]?.worktreePath === worktree.path)) {
throw new Error('Worktree is already tracked as a task');
}Minor Notes
- Partial import failure (
ImportWorktreesDialog.tsx): Sequential import means if one fails mid-batch, earlier ones are already created. The dialog shows the error but doesn't indicate how many succeeded (e.g. "2 of 5 imported"). Works correctly on reopen though (filters out already-tracked). - File length:
ImportWorktreesDialog.tsxis 364 lines vs. the 300-line guideline. TheStatusBadgehelper at the bottom could be extracted.
What's done well
externalWorktreeflag correctly propagated through all persistence layers (types, save, restore, autosave)- Close task properly skips git cleanup for external worktrees
- Merge dialog defaults
initialCleanup: falsefor imports - Backend:
validatePathon IPC, array-formexec,safeRealpathfallback - Auto-import prompt on project add is a nice UX touch
|
Thank you very much! <3 |
Code Review — Additional FindingThe existing review by @johannesjo is thorough. One new issue not yet noted: Missing required
|
johannesjo
left a comment
There was a problem hiding this comment.
TypeScript error: createImportedTask in src/store/tasks.ts constructs a Task without the required gitIsolation: GitIsolationMode field. This causes undefined at runtime and surfaces as the wrong close-confirmation message in TilingLayout.tsx. Fix: add gitIsolation: 'worktree' to the task literal.
The existing review findings (missing duplicate-import guard) also still need addressing. Details in the review comment.
Resolve conflicts from unifying directMode -> gitIsolation on main: - Task/PersistedTask: keep gitIsolation + externalWorktree side-by-side - createImportedTask: set gitIsolation: 'worktree' and add duplicate-path guard (addresses review comments on PR johannesjo#16) - closeTask: skip worktree deletion when externalWorktree is set, so user-owned worktrees stay intact - CloseTaskDialog: treat imported tasks as non-danger close, keep per-mode messaging - TilingLayout: fix force-close copy for imported worktrees - TaskTitleBar: add "Imported" badge - TaskBranchInfoBar: surface "Existing worktree" label - Sidebar: route both "Add project" buttons through handleAddProject so the auto-import scan runs in all entry points; drop dead handleRemoveProject helper (main's confirmRemove dialog handles this)
|
Very cool! Thank you very much for this! And sorry for the delay... |
Multi-review of the merge-resolution commit flagged two correctness bugs and one data-integrity gap: 1. MergeDialog rendered a "delete branch and worktree after merge" checkbox for imported worktrees. Default was unchecked, but the user could still tick it — at which point removeWorktree hardcodes .worktrees/<branchName> (so the external worktree is not touched) but `git branch -D` runs anyway, orphaning the user's worktree from its branch. Fix: hide the checkbox when task.externalWorktree, and defensively force cleanup=false in the store-level mergeTask even if the IPC payload says otherwise. 2. mergeTask's "detached HEAD / branch mismatch" safety check only fired when .worktrees/<branchName> existed on disk. For external worktrees, the check was silently skipped, so an agent checking out a different branch inside the imported worktree would have its work discarded by the merge. Fix: thread task.worktreePath through the IPC payload and use it for the existence+branch check; fall back to the managed-layout path when not provided. 3. createImportedTask constructed tasks without baseBranch, so diff computation and merge target detection ignored the project's configured default (e.g. `develop`/release branches). Fix: read project.defaultBaseBranch at import time. Also replace handleAddProject's `console.warn` swallow with a user- visible notification (project "never swallow errors" guideline).
|
Thank you very much for this! <3 |
* origin/main: Feat/focus mode (johannesjo#76) Add existing worktree import flow (johannesjo#16) # Conflicts: # electron/ipc/channels.ts # electron/ipc/register.ts # electron/preload.cjs # src/components/EditProjectDialog.tsx # src/store/tasks.ts
Summary