From 2927bbc4aa84c002b37bd8421ddcd168e92101c3 Mon Sep 17 00:00:00 2001 From: Jahvon Dockery Date: Tue, 13 Oct 2026 01:32:34 -0400 Subject: [PATCH] fix(workspace): discover worktrees nested in boundary dirs Dynamic workspace discovery skips a flow.yaml under .claude, vendor, and similar directories inside a registered workspace, so vendored copies never become the workspace. Claude Code creates worktrees at /.claude/worktrees/, so commands run in those worktrees resolved to the main checkout instead. A linked worktree (a .git file whose gitdir has a commondir) is now always a valid root. Submodules have no commondir and stay behind the boundary. No ticket --- pkg/filesystem/resolve.go | 25 +++++++++++++++++++++++++ pkg/filesystem/resolve_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/pkg/filesystem/resolve.go b/pkg/filesystem/resolve.go index 2b916ef3..f78897d9 100644 --- a/pkg/filesystem/resolve.go +++ b/pkg/filesystem/resolve.go @@ -196,6 +196,11 @@ func discoveryBoundary(cfg *config.Config) func(string) bool { if _, registered := cfg.NameForWorkspacePath(root); registered { return false } + // A linked worktree is a working copy of the user's own repo, not a dependency, wherever + // a tool chose to put it (Claude Code, for one, nests them under .claude/worktrees). + if isLinkedWorktree(root) { + return false + } parent, found := cfg.WorkspaceForPath(root) if !found { return false @@ -213,6 +218,26 @@ func discoveryBoundary(cfg *config.Config) func(string) bool { } } +// isLinkedWorktree reports whether dir is the root of a `git worktree add` checkout. Its .git is +// a file pointing at a gitdir that holds a commondir file; a submodule also has a .git file, but +// its gitdir has no commondir, so a vendored submodule stays behind the boundary. +func isLinkedWorktree(dir string) bool { + data, err := os.ReadFile(filepath.Clean(filepath.Join(dir, ".git"))) + if err != nil { + return false + } + gitDir, ok := strings.CutPrefix(strings.TrimSpace(string(data)), "gitdir:") + if !ok { + return false + } + gitDir = strings.TrimSpace(gitDir) + if !filepath.IsAbs(gitDir) { + gitDir = filepath.Join(dir, gitDir) + } + info, err := os.Stat(filepath.Join(gitDir, "commondir")) + return err == nil && !info.IsDir() +} + // loadResolved builds a result for a registered workspace, creating its flow.yaml if the user // registered a path that does not have one yet. func loadResolved(name, path string, src WorkspaceSource) (*ResolvedWorkspace, error) { diff --git a/pkg/filesystem/resolve_test.go b/pkg/filesystem/resolve_test.go index eb8727c0..93c22b02 100644 --- a/pkg/filesystem/resolve_test.go +++ b/pkg/filesystem/resolve_test.go @@ -107,6 +107,33 @@ var _ = Describe("ResolveWorkspace", func() { Expect(res.Path).To(Equal(parent)) }) + It("discovers a linked worktree nested under a boundary directory", func() { + parent := mkWorkspace("repo") + worktree := mkWorkspace("repo/.claude/worktrees/feature") + gitDir := mkDir("repo/.git/worktrees/feature") + Expect(os.WriteFile(filepath.Join(gitDir, "commondir"), []byte("../..\n"), 0600)).To(Succeed()) + Expect(os.WriteFile(filepath.Join(worktree, ".git"), []byte("gitdir: "+gitDir+"\n"), 0600)).To(Succeed()) + cfg := dynamic(map[string]string{"repo": parent}, "repo") + res, err := filesystem.ResolveWorkspace(cfg, filesystem.ResolveOptions{ + Dir: mkDir("repo/.claude/worktrees/feature/cmd"), + }) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Path).To(Equal(worktree)) + Expect(res.Registered).To(BeFalse()) + Expect(res.Source).To(Equal(filesystem.SourceDiscovered)) + }) + + It("walks past a vendored submodule inside a registered workspace", func() { + parent := mkWorkspace("repo") + sub := mkWorkspace("repo/vendor/dep") + mkDir("repo/.git/modules/dep") + Expect(os.WriteFile(filepath.Join(sub, ".git"), []byte("gitdir: ../../.git/modules/dep\n"), 0600)).To(Succeed()) + cfg := dynamic(map[string]string{"repo": parent}, "repo") + res, err := filesystem.ResolveWorkspace(cfg, filesystem.ResolveOptions{Dir: sub}) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Path).To(Equal(parent)) + }) + It("still discovers a standalone clone under a directory named external", func() { // The boundary only applies inside a registered workspace; an unrelated path that // happens to contain "external" is a legitimate root.