In git worktrees, any bd command was slow with a 2-3s pause because:
- git.IsWorktree() was called 4+ times per command
- Each call spawned 2 git processes (git-dir and git-common-dir)
- git.GetRepoRoot() and git.GetMainRepoRoot() also called multiple times
Fix: Cache results using sync.Once since these values do not change during
a single command execution:
- IsWorktree() - caches worktree detection
- GetRepoRoot() - caches repo root path
- GetMainRepoRoot() - caches main repo root for worktrees
Added ResetCaches() for test cleanup between subtests that change directories.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add TOML parsing using BurntSushi/toml
- Update formula loader to try .toml first, fall back to .json
- Add `bd formula convert` command for JSON→TOML migration
- Multi-line string support for readable descriptions
- Cache git worktree/reporoot checks for performance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add git.GetRepoRoot() with Windows path normalization
- Update beads.findGitRoot() to delegate to git.GetRepoRoot()
- Replace findBeadsDir() with beads.FindBeadsDir() across 8 files
- Remove duplicate findBeadsDir() and findGitRoot() function definitions
- Remove dead test code (TestInfoCommand, TestInfoWithNoDaemon)
- Update tests to work with consolidated APIs
Part of Code Health Review Dec 2025 epic (bd-tggf).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The error message 'path exists but is not a valid git worktree' was appearing
in daemon.log when the daemon attempted to use an existing worktree that was
in the git worktree list but had other issues (broken sparse checkout, etc.).
Root cause:
- CreateBeadsWorktree only checked isValidWorktree (is it in git worktree list)
- CheckWorktreeHealth was called separately and checked additional things
- If the worktree passed isValidWorktree but failed health check, an error
was logged and repair was attempted
Fix:
- CreateBeadsWorktree now performs a full health check when it finds an
existing worktree that's in the git worktree list
- If the health check fails, it automatically removes and recreates the
worktree
- Removed redundant CheckWorktreeHealth calls in daemon_sync_branch.go and
syncbranch/worktree.go since CreateBeadsWorktree now handles this internally
This eliminates the confusing error message and ensures worktrees are always
in a healthy state after CreateBeadsWorktree returns successfully.
Problem:
When the daemon auto-sync runs with --auto-commit --auto-push, the sync
branch pull operation consistently fails with:
fatal: '.git/beads-worktrees/beads-metadata' is a missing but already
registered worktree; use 'add -f' to override, or 'prune' or 'remove'
to clear
This occurs because:
1. Daemon creates worktree at .git/beads-worktrees/<branch>
2. Git registers it in .git/worktrees/<branch>
3. After the operation, worktree contents are removed
4. Git registration persists, pointing to the now-empty path
5. Subsequent CreateBeadsWorktree calls fail because os.Stat() returns
error (path missing), so no cleanup happens, then git worktree add
fails because git still has it registered
Root cause:
The git worktree add commands in CreateBeadsWorktree() did not use the
-f (force) flag, which is needed to override the "missing but already
registered" state.
Solution:
Add -f flag to both git worktree add commands (for existing branch and
new branch cases). Per git documentation, -f overrides the safeguard
that prevents creating a worktree when the path is already registered
but missing.
The existing git worktree prune call (line 30-32) was intended to handle
this, but it runs before the path check and may not always clear the
registration in time. The -f flag provides a robust fallback.
Testing:
- All existing worktree tests pass
- Added regression test TestCreateBeadsWorktree_MissingButRegistered
that simulates the exact issue #609 scenario
Fixes#609
Co-authored-by: Charles P. Cross <cpdata@users.noreply.github.com>
When worktrees are nested under the main repo (e.g.,
/project/.worktrees/feature/), bd now correctly finds .beads/ in the
parent repo.
The fix simplifies GetMainRepoRoot() to use `git rev-parse --git-common-dir`
which always returns the main repo's .git directory, regardless of whether
we're in a regular repo, a worktree, or a nested worktree.
- Simplified GetMainRepoRoot() implementation
- Added tests for nested worktree scenarios
- Updated CHANGELOG.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds comprehensive Git worktree support for beads issue tracking:
Core changes:
- New internal/git/gitdir.go package for worktree detection
- GetGitDir() returns proper .git location (main repo, not worktree)
- Updated all hooks to use git.GetGitDir() instead of local helper
- BeadsDir() now prioritizes main repository's .beads directory
Features:
- Hooks auto-install in main repo when run from worktree
- Shared .beads directory across all worktrees
- Config option no-install-hooks to disable auto-install
- New bd worktree subcommand for diagnostics
Documentation:
- New docs/WORKTREES.md with setup instructions
- Updated CHANGELOG.md and AGENT_INSTRUCTIONS.md
Testing:
- Updated tests to use exported git.GetGitDir()
- Added worktree detection tests
Co-authored-by: Claude <noreply@anthropic.com>
Closes: #478
Fix daemon auto-sync delete mutation not reflected in sync branch
When deleting an issue with `bd delete <id> --force`, the daemon auto-sync now properly removes the deleted issue from the sync branch.
**Problem:** The merge logic saw fewer local issues (due to deletion) and would re-add the deleted issue.
**Solution:** Add `ForceOverwrite` option to bypass merge logic when mutations occur. Mutation-triggered exports are authoritative and should overwrite, not merge.
Reviewed-by: stevey
When syncing JSONL to worktree, if the worktree has more issues than
local, merge them instead of blindly overwriting. This prevents fresh
clones from accidentally deleting remote issues when they sync with
fewer issues than the sync branch.
Root cause of GitHub #464: A fresh clone with sync-branch configured
would start with an empty database (since JSONL is on sync-branch, not
HEAD). When syncing, the local 1-issue JSONL would overwrite the
remotes 10-issue JSONL, and the subsequent 3-way merge would see this
as local deleted 9 issues causing deletion to win.
The fix compares issue counts and triggers a merge when local has fewer
issues than the worktree (remote). Uses 3-way merge with empty base to
combine both sets of issues.
Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- config: Add tests for GetStringSlice, GetMultiRepoConfig, and nil viper
behavior. Coverage improved from 65.3% to 84.0%.
- git: Add tests for error paths in RemoveBeadsWorktree, SyncJSONLToWorktree,
CheckWorktreeHealth, and sparse checkout functions. Coverage improved
from 72.9% to 82.7%.
Closes: bd-t3b, bd-4h3, bd-ge7
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated tests to match the new branchExists() signature that returns
bool instead of (bool, error).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add #nosec directives with explanations for all gosec warnings in worktree operations
- Tighten directory permissions from 0755 to 0750 for better security
- Fix misspellings: archaeological -> archeological, cancelled -> canceled
- Remove unused jsonlPath parameter from syncBranchCommitAndPush
- Change branchExists to return bool instead of (bool, error) - error was never used
All changes maintain backward compatibility and improve code quality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>