fix(beads): Auto-create .beads/redirect for crew and polecats

Fixes gt-b6qm: redirect files can get deleted by git clean, causing
"no beads database found" errors.

Changes:
- crew.Manager.Add() now creates .beads/redirect during setup
- gt prime regenerates missing redirects silently on startup

The redirect points to the shared beads database at either:
- rig/mayor/rig/.beads/ (preferred)
- rig/.beads/ (fallback)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-21 21:16:39 -08:00
parent 5ae7a8415b
commit 91b4b8b4cc
2 changed files with 137 additions and 0 deletions

View File

@@ -98,6 +98,12 @@ func (m *Manager) Add(name string, createBranch bool) (*CrewWorker, error) {
return nil, fmt.Errorf("creating mail dir: %w", err)
}
// Set up shared beads: crew uses rig's shared beads via redirect file
if err := m.setupSharedBeads(crewPath); err != nil {
// Non-fatal - crew can still work, warn but don't fail
fmt.Printf("Warning: could not set up shared beads: %v\n", err)
}
// Create CLAUDE.md with crew worker prompting
if err := m.createClaudeMD(name, crewPath); err != nil {
_ = os.RemoveAll(crewPath)
@@ -380,3 +386,53 @@ type PristineResult struct {
Synced bool `json:"synced"`
SyncError string `json:"sync_error,omitempty"`
}
// setupSharedBeads creates a redirect file so the crew worker uses the rig's shared .beads database.
// This eliminates the need for git sync between crew clones - all crew members share one database.
//
// Structure:
//
// rig/
// mayor/rig/.beads/ <- Shared database (the canonical location)
// crew/
// <name>/
// .beads/
// redirect <- Contains "../../mayor/rig/.beads"
func (m *Manager) setupSharedBeads(crewPath string) error {
// The shared beads database is at rig/mayor/rig/.beads/
// Crew clones are at rig/crew/<name>/
// So the relative path is ../../mayor/rig/.beads
sharedBeadsPath := filepath.Join(m.rig.Path, "mayor", "rig", ".beads")
// Verify the shared beads exists
if _, err := os.Stat(sharedBeadsPath); os.IsNotExist(err) {
// Fall back to rig root .beads if mayor/rig doesn't exist
sharedBeadsPath = filepath.Join(m.rig.Path, ".beads")
if _, err := os.Stat(sharedBeadsPath); os.IsNotExist(err) {
return fmt.Errorf("no shared beads database found")
}
}
// Create crew's .beads directory
crewBeadsDir := filepath.Join(crewPath, ".beads")
if err := os.MkdirAll(crewBeadsDir, 0755); err != nil {
return fmt.Errorf("creating crew .beads dir: %w", err)
}
// Calculate relative path from crew/.beads/ to shared beads
// crew/<name>/.beads/ -> ../../mayor/rig/.beads or ../../.beads
var redirectContent string
if _, err := os.Stat(filepath.Join(m.rig.Path, "mayor", "rig", ".beads")); err == nil {
redirectContent = "../../mayor/rig/.beads\n"
} else {
redirectContent = "../../.beads\n"
}
// Create redirect file
redirectPath := filepath.Join(crewBeadsDir, "redirect")
if err := os.WriteFile(redirectPath, []byte(redirectContent), 0644); err != nil {
return fmt.Errorf("creating redirect file: %w", err)
}
return nil
}