fix(rig): return rig root from BeadsPath() to respect redirect system

BeadsPath() was incorrectly returning <rig>/mayor/rig when HasMayor was
true, bypassing the redirect system at <rig>/.beads/redirect. This caused
beads operations to fail when the user's repo doesn't have tracked beads.

The redirect architecture is:
- <rig>/.beads/redirect -> mayor/rig/.beads (when repo tracks .beads/)
- <rig>/.beads/ contains local database (when repo doesn't track .beads/)

By always returning the rig root, all callers now go through the redirect
system which is set up by initBeads() during rig creation.

Affected callers (all now work correctly):
- internal/refinery/manager.go - Queue() for merge requests
- internal/swarm/manager.go - swarm operations
- internal/cmd/swarm.go - swarm CLI commands
- internal/cmd/status.go - rig status display
- internal/cmd/mq_next.go - merge queue operations
- internal/cmd/mq_list.go - merge queue listing
- internal/cmd/rig_dock.go - dock/undock operations

Fixes #317

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
julianknutsen
2026-01-11 06:35:20 +00:00
parent 982ce6c5d1
commit e7d7a1bd6b
3 changed files with 238 additions and 6 deletions

View File

@@ -308,6 +308,89 @@ func TestEnsureGitignoreEntry_AppendsToExisting(t *testing.T) {
}
}
func TestInitBeads_TrackedBeads_CreatesRedirect(t *testing.T) {
t.Parallel()
// When the cloned repo has tracked beads (mayor/rig/.beads exists),
// initBeads should create a redirect file at <rig>/.beads/redirect
// pointing to mayor/rig/.beads instead of creating a local database.
rigPath := t.TempDir()
// Simulate tracked beads in the cloned repo
mayorBeadsDir := filepath.Join(rigPath, "mayor", "rig", ".beads")
if err := os.MkdirAll(mayorBeadsDir, 0755); err != nil {
t.Fatalf("mkdir mayor beads: %v", err)
}
// Create a config file to simulate a real beads directory
if err := os.WriteFile(filepath.Join(mayorBeadsDir, "config.yaml"), []byte("prefix: gt\n"), 0644); err != nil {
t.Fatalf("write mayor config: %v", err)
}
manager := &Manager{}
if err := manager.initBeads(rigPath, "gt"); err != nil {
t.Fatalf("initBeads: %v", err)
}
// Verify redirect file was created
redirectPath := filepath.Join(rigPath, ".beads", "redirect")
content, err := os.ReadFile(redirectPath)
if err != nil {
t.Fatalf("reading redirect file: %v", err)
}
expected := "mayor/rig/.beads\n"
if string(content) != expected {
t.Errorf("redirect content = %q, want %q", string(content), expected)
}
// Verify no local database was created (no config.yaml at rig level)
rigConfigPath := filepath.Join(rigPath, ".beads", "config.yaml")
if _, err := os.Stat(rigConfigPath); !os.IsNotExist(err) {
t.Errorf("expected no config.yaml at rig level when using redirect, but it exists")
}
}
func TestInitBeads_LocalBeads_CreatesDatabase(t *testing.T) {
// Cannot use t.Parallel() due to t.Setenv
// When the cloned repo does NOT have tracked beads (no mayor/rig/.beads),
// initBeads should create a local database at <rig>/.beads/
rigPath := t.TempDir()
// Create mayor/rig directory but WITHOUT .beads (no tracked beads)
mayorRigDir := filepath.Join(rigPath, "mayor", "rig")
if err := os.MkdirAll(mayorRigDir, 0755); err != nil {
t.Fatalf("mkdir mayor/rig: %v", err)
}
// Use fake bd that succeeds
script := `#!/usr/bin/env bash
set -e
if [[ "$1" == "init" ]]; then
# Simulate successful bd init
exit 0
fi
exit 0
`
binDir := writeFakeBD(t, script)
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
manager := &Manager{}
if err := manager.initBeads(rigPath, "gt"); err != nil {
t.Fatalf("initBeads: %v", err)
}
// Verify NO redirect file was created
redirectPath := filepath.Join(rigPath, ".beads", "redirect")
if _, err := os.Stat(redirectPath); !os.IsNotExist(err) {
t.Errorf("expected no redirect file for local beads, but it exists")
}
// Verify .beads directory was created
beadsDir := filepath.Join(rigPath, ".beads")
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
t.Errorf("expected .beads directory to be created")
}
}
func TestInitBeadsWritesConfigOnFailure(t *testing.T) {
// Cannot use t.Parallel() due to t.Setenv
rigPath := t.TempDir()