fix(init): prevent parent hub contamination during bd init (GH#896)

When running bd init in a subdirectory of a hub (e.g., ~/Repos/project
where ~/Repos/.beads exists), the new database was incorrectly inheriting
issues from the parent hub.

Root cause: checkGitForIssues() computed the relative path from gitRoot
to beadsDir but did not validate that beadsDir was actually inside the
git repository. When beadsDir was outside (e.g., ../.beads), it would
still attempt to import, causing contamination.

Fix: Add a guard to reject beadsDir paths that start with .. (outside
the git repository boundary).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
beads/crew/grip
2026-01-04 15:43:31 -08:00
committed by Steve Yegge
parent 9d69099803
commit b3ebedb063
2 changed files with 62 additions and 0 deletions

View File

@@ -4,10 +4,12 @@ import (
"context"
"encoding/json"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/steveyegge/beads/internal/beads"
"github.com/steveyegge/beads/internal/git"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/types"
)
@@ -282,6 +284,59 @@ func TestCheckGitForIssues_NoBeadsDir(t *testing.T) {
}
}
// TestCheckGitForIssues_ParentHubNotInherited tests the GH#896 fix: when running
// bd init in a subdirectory of a hub, the parent hub's .beads should NOT be used
// for importing issues. This prevents contamination from parent hub issues.
func TestCheckGitForIssues_ParentHubNotInherited(t *testing.T) {
// Create a structure simulating the bug scenario:
// tmpDir/
// .beads/ <- parent hub (NOT a git repo)
// issues.jsonl
// config.yaml
// newproject/ <- new project (IS a git repo)
// .git/
tmpDir := t.TempDir()
// Create parent hub's .beads with issues
parentBeadsDir := filepath.Join(tmpDir, ".beads")
if err := os.MkdirAll(parentBeadsDir, 0755); err != nil {
t.Fatalf("Failed to create parent .beads: %v", err)
}
if err := os.WriteFile(filepath.Join(parentBeadsDir, "config.yaml"), []byte("prefix: hub\n"), 0644); err != nil {
t.Fatalf("Failed to create parent config.yaml: %v", err)
}
if err := os.WriteFile(filepath.Join(parentBeadsDir, "issues.jsonl"), []byte(`{"id":"hub-001","title":"Parent Issue"}`+"\n"), 0644); err != nil {
t.Fatalf("Failed to create parent issues.jsonl: %v", err)
}
// Create newproject as a git repo
newProject := filepath.Join(tmpDir, "newproject")
if err := os.MkdirAll(newProject, 0755); err != nil {
t.Fatalf("Failed to create newproject: %v", err)
}
// Initialize git repo in newproject
t.Chdir(newProject)
cmd := exec.Command("git", "init")
if err := cmd.Run(); err != nil {
t.Fatalf("Failed to git init: %v", err)
}
// Reset git context cache (important for test isolation)
git.ResetCaches()
// Now checkGitForIssues should NOT find the parent hub's issues
// because the parent .beads is outside the git root (newproject)
count, path, _ := checkGitForIssues()
if count != 0 {
t.Errorf("GH#896: checkGitForIssues found %d issues from parent hub (should be 0)", count)
}
if path != "" {
t.Errorf("GH#896: checkGitForIssues returned path %q (should be empty)", path)
}
}
func TestBoolToFlag(t *testing.T) {
t.Parallel()
tests := []struct {