diff --git a/internal/rig/manager.go b/internal/rig/manager.go index dec61fda..478921a0 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -451,9 +451,21 @@ func (m *Manager) initBeads(rigPath, prefix string) error { return err } - // Run bd init if available, with --no-agents to skip AGENTS.md creation - cmd := exec.Command("bd", "init", "--prefix", prefix, "--no-agents") + // Build environment with explicit BEADS_DIR to prevent bd from + // finding a parent directory's .beads/ database + env := os.Environ() + filteredEnv := make([]string, 0, len(env)+1) + for _, e := range env { + if !strings.HasPrefix(e, "BEADS_DIR=") { + filteredEnv = append(filteredEnv, e) + } + } + filteredEnv = append(filteredEnv, "BEADS_DIR="+beadsDir) + + // Run bd init if available + cmd := exec.Command("bd", "init", "--prefix", prefix) cmd.Dir = rigPath + cmd.Env = filteredEnv _, err := cmd.CombinedOutput() if err != nil { // bd might not be installed or failed, create minimal structure @@ -470,6 +482,7 @@ func (m *Manager) initBeads(rigPath, prefix string) error { // Without fingerprint, the bd daemon fails to start silently. migrateCmd := exec.Command("bd", "migrate", "--update-repo-id") migrateCmd.Dir = rigPath + migrateCmd.Env = filteredEnv // Ignore errors - fingerprint is optional for functionality _, _ = migrateCmd.CombinedOutput() @@ -477,33 +490,19 @@ func (m *Manager) initBeads(rigPath, prefix string) error { } // initAgentBeads creates agent beads for this rig and optionally global agents. -// - Always creates: -witness-, -refinery- -// - First rig only: -deacon, -mayor +// - Always creates: gt--witness, gt--refinery +// - First rig only: gt-deacon, gt-mayor +// +// Agent beads are stored in the TOWN beads (not rig beads) because they use +// the canonical gt-* prefix for cross-rig coordination. The town beads must +// be initialized with 'gt' prefix for this to work. // // Agent beads track lifecycle state for ZFC compliance (gt-h3hak, gt-pinkq). func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig bool) error { - // Run bd commands from the canonical beads location. - // - If source repo has .beads/ tracked (mayor/rig/.beads exists), use that - // - Otherwise use rig root .beads/ (created by initBeads during rig add) - mayorRigBeads := filepath.Join(rigPath, "mayor", "rig", ".beads") - var beadsDir string - if _, err := os.Stat(mayorRigBeads); err == nil { - beadsDir = mayorRigBeads - } else { - beadsDir = filepath.Join(rigPath, ".beads") - } - prevBeadsDir, hadBeadsDir := os.LookupEnv("BEADS_DIR") - if err := os.Setenv("BEADS_DIR", beadsDir); err != nil { - return fmt.Errorf("setting BEADS_DIR: %w", err) - } - defer func() { - if hadBeadsDir { - _ = os.Setenv("BEADS_DIR", prevBeadsDir) - } else { - _ = os.Unsetenv("BEADS_DIR") - } - }() - bd := beads.New(rigPath) + // Agent beads go in town beads (gt-* prefix), not rig beads. + // This enables cross-rig agent coordination via canonical IDs. + townBeadsDir := filepath.Join(m.townRoot, ".beads") + bd := beads.NewWithBeadsDir(m.townRoot, townBeadsDir) // Define agents to create type agentDef struct { @@ -515,16 +514,18 @@ func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig boo var agents []agentDef - // Always create rig-specific agents (using canonical naming: prefix-rig-role-name) + // Always create rig-specific agents using canonical gt- prefix. + // Agent bead IDs use the gastown namespace (gt-) regardless of the rig's + // beads prefix. Format: gt-- (e.g., gt-tribal-witness) agents = append(agents, agentDef{ - id: beads.WitnessBeadIDWithPrefix(prefix, rigName), + id: beads.WitnessBeadID(rigName), roleType: "witness", rig: rigName, desc: fmt.Sprintf("Witness for %s - monitors polecat health and progress.", rigName), }, agentDef{ - id: beads.RefineryBeadIDWithPrefix(prefix, rigName), + id: beads.RefineryBeadID(rigName), roleType: "refinery", rig: rigName, desc: fmt.Sprintf("Refinery for %s - processes merge queue.", rigName), diff --git a/internal/rig/manager_test.go b/internal/rig/manager_test.go index f7268387..cd71153b 100644 --- a/internal/rig/manager_test.go +++ b/internal/rig/manager_test.go @@ -329,13 +329,16 @@ exit 1 } } -func TestInitAgentBeadsUsesRigBeadsDir(t *testing.T) { - rigPath := t.TempDir() - beadsDir := filepath.Join(rigPath, ".beads") +func TestInitAgentBeadsUsesTownBeadsDir(t *testing.T) { + // Agent beads use town beads (gt-* prefix) for cross-rig coordination. + // The Manager.townRoot determines where agent beads are created. + townRoot := t.TempDir() + townBeadsDir := filepath.Join(townRoot, ".beads") + rigPath := filepath.Join(townRoot, "testrip") mayorRigPath := filepath.Join(rigPath, "mayor", "rig") - if err := os.MkdirAll(beadsDir, 0755); err != nil { - t.Fatalf("mkdir beads dir: %v", err) + if err := os.MkdirAll(townBeadsDir, 0755); err != nil { + t.Fatalf("mkdir town beads dir: %v", err) } if err := os.MkdirAll(mayorRigPath, 0755); err != nil { t.Fatalf("mkdir mayor rig: %v", err) @@ -386,10 +389,10 @@ esac binDir := writeFakeBD(t, script) t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH")) - t.Setenv("EXPECT_BEADS_DIR", beadsDir) + t.Setenv("EXPECT_BEADS_DIR", townBeadsDir) t.Setenv("BEADS_DIR", "") - manager := &Manager{} + manager := &Manager{townRoot: townRoot} if err := manager.initAgentBeads(rigPath, "demo", "gt", false); err != nil { t.Fatalf("initAgentBeads: %v", err) }