diff --git a/internal/cmd/doctor.go b/internal/cmd/doctor.go index f45f3415..531d00d0 100644 --- a/internal/cmd/doctor.go +++ b/internal/cmd/doctor.go @@ -55,6 +55,7 @@ func runDoctor(cmd *cobra.Command, args []string) error { // Register built-in checks d.Register(doctor.NewDaemonCheck()) d.Register(doctor.NewBeadsDatabaseCheck()) + d.Register(doctor.NewBeadsSyncBranchCheck()) // Run checks var report *doctor.Report diff --git a/internal/doctor/beads_sync_check.go b/internal/doctor/beads_sync_check.go new file mode 100644 index 00000000..870fc7ee --- /dev/null +++ b/internal/doctor/beads_sync_check.go @@ -0,0 +1,110 @@ +package doctor + +import ( + "os" + "path/filepath" + "strings" +) + +// BeadsSyncBranchCheck verifies that rig-level beads have sync-branch configured. +// Rig beads need sync-branch for multi-clone coordination (polecats, crew, etc.) +// Town-level beads should NOT have sync-branch (single clone, commits to main). +type BeadsSyncBranchCheck struct { + FixableCheck +} + +// NewBeadsSyncBranchCheck creates a new sync-branch configuration check. +func NewBeadsSyncBranchCheck() *BeadsSyncBranchCheck { + return &BeadsSyncBranchCheck{ + FixableCheck: FixableCheck{ + BaseCheck: BaseCheck{ + CheckName: "beads-sync-branch", + CheckDescription: "Verify rig beads have sync-branch configured", + }, + }, + } +} + +// Run checks if rig-level beads have sync-branch properly configured. +func (c *BeadsSyncBranchCheck) Run(ctx *CheckContext) *CheckResult { + // Only check if a rig is specified + if ctx.RigName == "" { + return &CheckResult{ + Name: c.Name(), + Status: StatusOK, + Message: "No rig specified, skipping rig beads sync-branch check", + } + } + + // Check rig-level beads config + rigBeadsDir := filepath.Join(ctx.RigPath(), ".beads") + configPath := filepath.Join(rigBeadsDir, "config.yaml") + + content, err := os.ReadFile(configPath) + if err != nil { + if os.IsNotExist(err) { + return &CheckResult{ + Name: c.Name(), + Status: StatusWarning, + Message: "No .beads/config.yaml in rig", + FixHint: "Run 'bd init' in the rig directory", + } + } + return &CheckResult{ + Name: c.Name(), + Status: StatusError, + Message: "Could not read .beads/config.yaml: " + err.Error(), + } + } + + // Check for sync-branch setting + if !strings.Contains(string(content), "sync-branch:") { + return &CheckResult{ + Name: c.Name(), + Status: StatusWarning, + Message: "Rig beads missing sync-branch configuration", + Details: []string{ + "Rig: " + ctx.RigName, + "Rig beads need sync-branch for multi-clone coordination", + "Without this, polecats and crew members can't share beads", + }, + FixHint: "Run 'gt doctor --fix' or add 'sync-branch: beads-sync' to .beads/config.yaml", + } + } + + return &CheckResult{ + Name: c.Name(), + Status: StatusOK, + Message: "Rig beads sync-branch is configured", + } +} + +// Fix adds sync-branch to the rig beads config. +func (c *BeadsSyncBranchCheck) Fix(ctx *CheckContext) error { + if ctx.RigName == "" { + return nil + } + + rigBeadsDir := filepath.Join(ctx.RigPath(), ".beads") + configPath := filepath.Join(rigBeadsDir, "config.yaml") + + content, err := os.ReadFile(configPath) + if err != nil { + return err + } + + // Check if already configured + if strings.Contains(string(content), "sync-branch:") { + return nil + } + + // Append sync-branch setting + f, err := os.OpenFile(configPath, os.O_APPEND|os.O_WRONLY, 0644) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString("sync-branch: beads-sync\n") + return err +} diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 3256be59..7315ed13 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -329,6 +329,7 @@ func (m *Manager) initAgentStates(rigPath string) error { } // initBeads initializes the beads database at rig level. +// Rig beads use sync-branch for multi-clone coordination (polecats, crew, etc.) func (m *Manager) initBeads(rigPath, prefix string) error { beadsDir := filepath.Join(rigPath, ".beads") if err := os.MkdirAll(beadsDir, 0755); err != nil { @@ -341,10 +342,20 @@ func (m *Manager) initBeads(rigPath, prefix string) error { if err := cmd.Run(); err != nil { // bd might not be installed or --no-agents not supported, create minimal structure configPath := filepath.Join(beadsDir, "config.yaml") - configContent := fmt.Sprintf("prefix: %s\n", prefix) + // Rig beads need sync-branch for multi-clone coordination + configContent := fmt.Sprintf("prefix: %s\nsync-branch: beads-sync\n", prefix) if writeErr := os.WriteFile(configPath, []byte(configContent), 0644); writeErr != nil { return writeErr } + } else { + // bd init succeeded, but we still need to add sync-branch + // Append to config.yaml (bd init doesn't set sync-branch by default) + configPath := filepath.Join(beadsDir, "config.yaml") + f, err := os.OpenFile(configPath, os.O_APPEND|os.O_WRONLY, 0644) + if err == nil { + defer f.Close() + f.WriteString("sync-branch: beads-sync\n") + } } return nil }