* refactor(worktree): drop unused flat-layout worktrees/ path
Every worktree creation now routes through the task-dir layout
(~/.kandev/tasks/{title}_{suffix}/{repo}/); the legacy flat layout
under ~/.kandev/worktrees/ has been unreachable since the subtasks
PR populated TaskDirName/RepoName unconditionally. Removes the dead
Config.BasePath, Manager.createWorktree, and the flat reconciler.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(worktree): remove unused Reconcile, add guard regression test
Address PR review feedback:
- Reconcile (and reconcileTasksDir / isOrphanedTaskDir) had no callers
anywhere in the tree after the flat-layout removal. Deleted rather than
left dangling; if startup orphan cleanup is wired up later it can return
alongside its actual call site. Also moots the stale doc comment that
CodeRabbit flagged.
- Add TestCreate_MissingTaskDirFields_ReturnsErrTaskDirRequired covering
the three permutations (missing RepoName, missing TaskDirName, both
missing) so a regression in the ErrTaskDirRequired guard surfaces in
CI rather than silently.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(worktree): bound git ref-inspection with context timeout
branchExists and currentBranch spawned raw exec.Command with no context
and no timeout, and both were called while Manager.Create held the
per-repo mutex. A hung git subprocess (credential helper, stuck filter,
filesystem stall) kept the lock in Manager.repoLocks forever, blocking
every subsequent task on the same repository until the backend was
restarted.
Route both helpers through newNonInteractiveGitCmd with a new
defaultGitInspectTimeout (10s) so ctx cancellation and bounded
deadlines propagate to the subprocess. Also log lock-hold duration on
release for future diagnosis.
* fix(worktree): address review feedback on inspect-timeout change
- branchExists/currentBranch now log the ctx error when the bound fires,
so a hung git surfaces in logs instead of only as a misleading "branch
not found" (CodeRabbit).
- inspectTimeout is a Manager field (defaults to defaultGitInspectTimeout)
so tests can shrink it; added an isolation test that verifies the
internal bound fires even when the caller ctx has no deadline (Claude).
- Route setUpstreamIfExists through newNonInteractiveGitCmd so it also
gets non-interactive env + WaitDelay while repoLock is held (Claude).
- Simplify TestCreate_HangingRevParseReleasesRepoLock: drop redundant
sync.WaitGroup and extract hangOnRevParseScript constant shared by all
four tests (CodeRabbit).
* fix(worktree): handle submodules in worktree creation
Repos with git submodules failed during worktree creation when git-crypt
was also present. The `git checkout HEAD -- .` command in
unlockGitCryptAndCheckout() tried to process submodule gitlink entries,
but git resolved their git dirs relative to the worktree-specific git
dir instead of the common dir, causing "not a git repository" errors.
- Detect submodule paths via `git ls-tree -r HEAD` (mode 160000)
- Exclude submodule paths from git-crypt checkout using :(exclude) pathspecs
- Initialize submodules after worktree creation via `git submodule update --init`
(non-fatal — unreachable URLs are logged as warnings)
- Add submodule init to all worktree creation paths (git-crypt and non-git-crypt)
* fix(worktree): address review — recursive submodule init, consistent call sites
- Add --recursive to git submodule update --init for nested submodules
- Call initSubmodules unconditionally in git-crypt path (matching manager.go)
* fix(worktree): restore submodule index entries after git-crypt checkout
The :(exclude) pathspec used to skip submodules during git checkout also
prevents their gitlink entries from being written to the index. This
leaves git status showing them as staged deletions.
After checkout, run git reset HEAD for each excluded submodule path to
copy the gitlink entry from HEAD back into the index.
* test(worktree): add git status assertion to submodule unlock test
Validate that the index reset after :(exclude) checkout leaves no
staged deletions for submodule paths.
* fix(worktree): use literal pathspec for submodule exclusion
Use :(literal,exclude) instead of :(exclude) to prevent glob
metacharacters in submodule paths from being interpreted as patterns.
* fix(worktree): fix initSubmodules docstring to match --recursive flag
When creating a worktree from a remote-tracking ref (e.g. origin/feature/foo),
git automatically sets the new branch's upstream to that ref. This caused new
task branches to incorrectly track the original remote branch instead of
starting with no upstream.
Add -c branch.autoSetupMerge=false to gitAddWorktree and
gitAddWorktreeWithGitCrypt. The explicit CheckoutBranch flow (PR reviews)
is unaffected — it sets upstream via setUpstreamIfExists after creation.