From ca487a8fed24dee40dca38ba2f37a77100b7c77c Mon Sep 17 00:00:00 2001 From: PepijnSenders Date: Fri, 2 Jan 2026 22:34:15 +0100 Subject: [PATCH] fix(beads): fix agent bead creation during rig add MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues were causing errors when running `gt rig add`: 1. **bd init flag**: Removed non-existent `--no-agents` flag from bd init command that was causing silent failures. 2. **BEADS_DIR for init**: Added explicit BEADS_DIR to bd init and migrate commands to prevent bd from finding parent directory databases. 3. **Agent beads location**: Agent beads now go in town beads (gt-* prefix) instead of rig beads. This is necessary because: - Agent IDs use canonical gt-* prefix (e.g., gt-tribal-witness) - Rig beads use rig-specific prefixes (e.g., tr-*) - bd strictly validates ID prefix against database prefix - Town beads must be initialized with `gt` prefix 4. **beads.run() BEADS_DIR**: Modified to explicitly pass BEADS_DIR in child process environment to ensure bd uses the correct database. 5. **Agent ID prefix**: Use WitnessBeadID/RefineryBeadID (canonical gt-*) instead of WithPrefix variants. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- internal/beads/beads.go | 15 ++++++++++++ internal/rig/manager.go | 51 +++++++++++++++++++++++------------------ 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/internal/beads/beads.go b/internal/beads/beads.go index d45c24cb..10db5466 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -225,6 +225,21 @@ func (b *Beads) run(args ...string) ([]byte, error) { cmd := exec.Command("bd", fullArgs...) cmd.Dir = b.workDir + // Explicitly set BEADS_DIR for child process to ensure bd uses the correct + // database. Without this, bd may search parent directories and find a different + // .beads/ directory with a different prefix, causing "prefix mismatch" errors. + beadsDir := filepath.Join(b.workDir, ".beads") + env := os.Environ() + // Filter out any existing BEADS_DIR to avoid conflicts + 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) + cmd.Env = filteredEnv + var stdout, stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 0d9076d4..8d9b587f 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -422,9 +422,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 @@ -441,6 +453,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() @@ -448,26 +461,18 @@ 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 rig root where .beads/ was initialized - // Set BEADS_DIR explicitly to ensure bd finds the database - 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 + bd := beads.New(m.townRoot) // Define agents to create type agentDef struct { @@ -479,16 +484,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),