From b4a6f599ac057cb00650c871fbe55c1d00d1ca69 Mon Sep 17 00:00:00 2001 From: kustrun Date: Fri, 2 Jan 2026 20:13:27 +0100 Subject: [PATCH] fix(beads): Use conditional routing based on source repo beads location Route to the correct beads location based on whether the source repo has .beads/ tracked in git: - If source has .beads/ tracked: route to mayor/rig/.beads - Otherwise: route to rig root .beads/ (created by initBeads) Updates both route registration in rig.go and polecat manager's NewManager/setupSharedBeads to use consistent conditional logic. --- internal/cmd/rig.go | 15 ++++++++-- internal/polecat/manager.go | 56 ++++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/internal/cmd/rig.go b/internal/cmd/rig.go index 7d3fde3a..dbd3c4b0 100644 --- a/internal/cmd/rig.go +++ b/internal/cmd/rig.go @@ -317,11 +317,22 @@ func runRigAdd(cmd *cobra.Command, args []string) error { return fmt.Errorf("saving rigs config: %w", err) } - // Add route to town-level routes.jsonl for prefix-based routing + // Add route to town-level routes.jsonl for prefix-based routing. + // Route points to the canonical beads location: + // - If source repo has .beads/ tracked in git, route to mayor/rig + // - Otherwise route to rig root (where initBeads creates the database) + // The conditional routing is necessary because initBeads creates the database at + // "/.beads", while repos with tracked beads have their database at mayor/rig/.beads. if newRig.Config.Prefix != "" { + routePath := name + mayorRigBeads := filepath.Join(townRoot, name, "mayor", "rig", ".beads") + if _, err := os.Stat(mayorRigBeads); err == nil { + // Source repo has .beads/ tracked - route to mayor/rig + routePath = name + "/mayor/rig" + } route := beads.Route{ Prefix: newRig.Config.Prefix + "-", - Path: name + "/mayor/rig", + Path: routePath, } if err := beads.AppendRoute(townRoot, route); err != nil { // Non-fatal: routing will still work, just not from town root diff --git a/internal/polecat/manager.go b/internal/polecat/manager.go index 3a2a96ac..623f0e72 100644 --- a/internal/polecat/manager.go +++ b/internal/polecat/manager.go @@ -47,8 +47,16 @@ type Manager struct { // NewManager creates a new polecat manager. func NewManager(r *rig.Rig, g *git.Git) *Manager { - // Use the rig root for beads operations (rig-level beads at .beads/) - rigPath := r.Path + // Determine the canonical beads location: + // - If mayor/rig/.beads exists (source repo has beads tracked), use that + // - Otherwise use rig root .beads/ (created by initBeads during gt rig add) + // This matches the conditional logic in setupSharedBeads and route registration. + // For repos that have .beads/ tracked in git, the canonical database lives in mayor/rig/. + mayorRigBeads := filepath.Join(r.Path, "mayor", "rig", ".beads") + beadsPath := r.Path + if _, err := os.Stat(mayorRigBeads); err == nil { + beadsPath = filepath.Join(r.Path, "mayor", "rig") + } // Try to load rig settings for namepool config settingsPath := filepath.Join(r.Path, "settings", "config.json") @@ -73,7 +81,7 @@ func NewManager(r *rig.Rig, g *git.Git) *Manager { return &Manager{ rig: r, git: g, - beads: beads.New(rigPath), + beads: beads.New(beadsPath), namePool: pool, } } @@ -763,15 +771,41 @@ func (m *Manager) loadFromBeads(name string) (*Polecat, error) { // polecats/ // / // .beads/ -// redirect <- Contains "../../.beads" +// redirect <- Contains "../../.beads" or "../../mayor/rig/.beads" // // IMPORTANT: If the polecat was created from a branch that had .beads/ tracked in git, // those files will be present. We must clean them out and replace with just the redirect. +// +// The redirect target is conditional: repos with .beads/ tracked in git have their canonical +// database at mayor/rig/.beads, while fresh rigs use the database at rig root .beads/. func (m *Manager) setupSharedBeads(polecatPath string) error { - // Ensure rig root has .beads/ directory - rigBeadsDir := filepath.Join(m.rig.Path, ".beads") - if err := os.MkdirAll(rigBeadsDir, 0755); err != nil { - return fmt.Errorf("creating rig .beads dir: %w", err) + // Determine the shared beads location: + // - If mayor/rig/.beads exists (source repo has beads tracked in git), use that + // - Otherwise fall back to rig/.beads (created by initBeads during gt rig add) + // This matches the crew manager's logic for consistency. + mayorRigBeads := filepath.Join(m.rig.Path, "mayor", "rig", ".beads") + rigRootBeads := filepath.Join(m.rig.Path, ".beads") + + var sharedBeadsPath string + var redirectContent string + + if _, err := os.Stat(mayorRigBeads); err == nil { + // Source repo has .beads/ tracked - use mayor/rig/.beads + sharedBeadsPath = mayorRigBeads + redirectContent = "../../mayor/rig/.beads\n" + } else { + // No beads in source repo - use rig root .beads (from initBeads) + sharedBeadsPath = rigRootBeads + redirectContent = "../../.beads\n" + // Ensure rig root has .beads/ directory + if err := os.MkdirAll(rigRootBeads, 0755); err != nil { + return fmt.Errorf("creating rig .beads dir: %w", err) + } + } + + // Verify shared beads exists + if _, err := os.Stat(sharedBeadsPath); os.IsNotExist(err) { + return fmt.Errorf("no shared beads database found at %s", sharedBeadsPath) } // Clean up any existing .beads/ contents from the branch @@ -790,12 +824,8 @@ func (m *Manager) setupSharedBeads(polecatPath string) error { return fmt.Errorf("creating polecat .beads dir: %w", err) } - // Create redirect file pointing to mayor/rig/.beads (the canonical beads location) - // Path is relative from polecats//.beads/ to mayor/rig/.beads/ - // We go directly to mayor/rig/.beads, not through rig root, to match crew workers + // Create redirect file pointing to the shared beads location redirectPath := filepath.Join(polecatBeadsDir, "redirect") - redirectContent := "../../mayor/rig/.beads\n" - if err := os.WriteFile(redirectPath, []byte(redirectContent), 0644); err != nil { return fmt.Errorf("creating redirect file: %w", err) }