Skip to content

fix: check cwd for .beads/ before git-worktree resolution#2823

Open
julianknutsen wants to merge 1 commit intosteveyegge:mainfrom
julianknutsen:fix/cwd-beads-dir-priority
Open

fix: check cwd for .beads/ before git-worktree resolution#2823
julianknutsen wants to merge 1 commit intosteveyegge:mainfrom
julianknutsen:fix/cwd-beads-dir-priority

Conversation

@julianknutsen
Copy link
Collaborator

Summary

When a subdirectory has its own .beads/ inside a git worktree that also has .beads/ at its root, the git-worktree check (step 2b in FindBeadsDir) finds the root's .beads/ before the cwd walk (step 3) can discover the local one.

This causes incorrect database connections when orchestrators like Gas City manage multiple "rigs" (project subdirectories) that each have their own beads database, nested inside a parent git repo that also tracks beads.

Problem

Reproduction scenario:

/project/                    ← git worktree root
├── .beads/                  ← project's beads (prefix: mc, port: 49880)
│   ├── config.yaml
│   └── metadata.json
└── rig-a/                   ← orchestrator-managed rig
    └── .beads/              ← rig's beads (prefix: tr, port: 46345)
        ├── config.yaml
        └── metadata.json

When bd list runs from rig-a/, it should connect to tr on port 46345. Instead, FindBeadsDir() returns /project/.beads/ (the git root's), connecting to the wrong database.

Root cause: FindBeadsDir() step 2b checks git.GetRepoRoot() for .beads/ before step 3 walks up from cwd. Since git.GetRepoRoot() returns /project/ (the worktree toplevel), it finds the root's .beads/ first. The same issue exists in findDatabaseInTree() which is used by FindDatabasePath().

Fix

Add a cwd check before git-worktree resolution in both FindBeadsDir() and findDatabaseInTree(). If cwd has a valid .beads/ directory (with project files or a database), return it immediately.

Priority order is now:

  1. BEADS_DIR env var (unchanged)
  2. cwd .beads/ (NEW — most-local directory wins)
  3. Git worktree root .beads/ (unchanged)
  4. Walk up from cwd (unchanged)

Tests

6 new tests in internal/beads/cwd_priority_test.go:

Test Scenario
TestFindBeadsDir_CwdPriority Rig .beads/ wins over git root .beads/
TestFindDatabasePath_CwdPriority Same for database path discovery
TestFindBeadsDir_CwdWithoutBeads_FallsBackToWalk Normal walk-up when cwd has no .beads/
TestFindBeadsDir_CwdBeadsDirWithRedirect Redirect in cwd .beads/ is followed
TestFindBeadsDir_BEADS_DIR_StillTakesPriority BEADS_DIR env still wins over cwd
TestFindBeadsDir_CwdEmptyBeadsDir_SkipsToCwdWalk Empty .beads/ dir (no project files) skipped

All existing tests continue to pass.

Backward Compatibility

This change only affects users who have nested .beads/ directories inside a git worktree — a scenario specific to multi-rig orchestration. Single-project repos, worktrees, and redirects are unaffected because the cwd check only fires when cwd literally contains .beads/ with valid project files.

When a subdirectory has its own .beads/ inside a git worktree that
also has .beads/ at its root, the git-worktree check (step 2b) finds
the root's .beads/ before the cwd walk (step 3) can discover the
local one.

This causes incorrect database connections when orchestrators like
Gas City manage multiple "rigs" (project subdirectories) that each
have their own beads database, nested inside a parent git repo that
also tracks beads.

Fix: Add a cwd check before git-worktree resolution in both
FindBeadsDir() and findDatabaseInTree(). If cwd has a valid .beads/
directory (with project files or a database), return it immediately.

Priority order is now:
  1. BEADS_DIR env var (unchanged)
  1b. cwd .beads/ (NEW — most-local directory wins)
  2. Git worktree root .beads/ (unchanged)
  3. Walk up from cwd (unchanged)

This preserves all existing behavior for single-project repos,
worktrees, and redirects. The cwd check only fires when cwd
literally contains .beads/ with valid project files.

Tests added:
  - TestFindBeadsDir_CwdPriority: rig .beads/ wins over root .beads/
  - TestFindDatabasePath_CwdPriority: same for database discovery
  - TestFindBeadsDir_CwdWithoutBeads_FallsBackToWalk: normal fallback
  - TestFindBeadsDir_CwdBeadsDirWithRedirect: redirect in cwd followed
  - TestFindBeadsDir_BEADS_DIR_StillTakesPriority: env var still wins
  - TestFindBeadsDir_CwdEmptyBeadsDir_SkipsToCwdWalk: empty dir skipped
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant