fix(doctor): use route path directly for agent beads check (#36)

The agent-beads-exist check was hardcoding '/mayor/rig' suffix when
constructing the beads path, but routes.jsonl can contain paths like
'my-saas' without this suffix.

This caused the check to look for beads in the wrong location:
- Expected: <town>/<route-path>/.beads (e.g., ~/gt/my-saas/.beads)
- Actual: <town>/<rig>/mayor/rig/.beads (e.g., ~/gt/my-saas/mayor/rig/.beads)

The fix stores the full route path and uses it directly when creating
the beads client, instead of reconstructing an assumed path structure.

Fixes agent beads not being found when routes use simple rig names.
This commit is contained in:
Jacob
2026-01-02 21:19:58 -05:00
committed by GitHub
parent c3e83a3d09
commit aaee7fed40

View File

@@ -33,6 +33,12 @@ func NewAgentBeadsCheck() *AgentBeadsCheck {
} }
} }
// rigInfo holds the rig name and its beads path from routes.
type rigInfo struct {
name string // rig name (first component of path)
beadsPath string // full path to beads directory relative to town root
}
// Run checks if agent beads exist for all expected agents. // Run checks if agent beads exist for all expected agents.
func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult { func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
// Load routes to get prefixes (routes.jsonl is source of truth for prefixes) // Load routes to get prefixes (routes.jsonl is source of truth for prefixes)
@@ -46,16 +52,19 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
} }
} }
// Build prefix -> rigName map from routes // Build prefix -> rigInfo map from routes
// Routes have format: prefix "gt-" -> path "gastown/mayor/rig" // Routes have format: prefix "gt-" -> path "gastown/mayor/rig" or "my-saas"
prefixToRig := make(map[string]string) // prefix (without hyphen) -> rigName prefixToRig := make(map[string]rigInfo) // prefix (without hyphen) -> rigInfo
for _, r := range routes { for _, r := range routes {
// Extract rig name from path like "gastown/mayor/rig" // Extract rig name from path (first component)
parts := strings.Split(r.Path, "/") parts := strings.Split(r.Path, "/")
if len(parts) >= 1 && parts[0] != "." { if len(parts) >= 1 && parts[0] != "." {
rigName := parts[0] rigName := parts[0]
prefix := strings.TrimSuffix(r.Prefix, "-") prefix := strings.TrimSuffix(r.Prefix, "-")
prefixToRig[prefix] = rigName prefixToRig[prefix] = rigInfo{
name: rigName,
beadsPath: r.Path, // Use the full route path
}
} }
} }
@@ -73,20 +82,21 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
// Find the first rig (by name, alphabetically) for global agents // Find the first rig (by name, alphabetically) for global agents
// Only consider gt-prefix rigs since other prefixes can't have agent beads yet // Only consider gt-prefix rigs since other prefixes can't have agent beads yet
var firstRigName string var firstRigName string
for prefix, rigName := range prefixToRig { for prefix, info := range prefixToRig {
if prefix != "gt" { if prefix != "gt" {
continue // Skip non-gt prefixes for first rig selection continue // Skip non-gt prefixes for first rig selection
} }
if firstRigName == "" || rigName < firstRigName { if firstRigName == "" || info.name < firstRigName {
firstRigName = rigName firstRigName = info.name
} }
} }
// Check each rig for its agents // Check each rig for its agents
for prefix, rigName := range prefixToRig { for prefix, info := range prefixToRig {
// Get beads client for this rig // Get beads client for this rig using the route path directly
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig") rigBeadsPath := filepath.Join(ctx.TownRoot, info.beadsPath)
bd := beads.New(rigBeadsPath) bd := beads.New(rigBeadsPath)
rigName := info.name
// Check rig-specific agents (using canonical naming: prefix-rig-role-name) // Check rig-specific agents (using canonical naming: prefix-rig-role-name)
witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName) witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName)
@@ -155,14 +165,17 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
return fmt.Errorf("loading routes.jsonl: %w", err) return fmt.Errorf("loading routes.jsonl: %w", err)
} }
// Build prefix -> rigName map from routes // Build prefix -> rigInfo map from routes
prefixToRig := make(map[string]string) prefixToRig := make(map[string]rigInfo)
for _, r := range routes { for _, r := range routes {
parts := strings.Split(r.Path, "/") parts := strings.Split(r.Path, "/")
if len(parts) >= 1 && parts[0] != "." { if len(parts) >= 1 && parts[0] != "." {
rigName := parts[0] rigName := parts[0]
prefix := strings.TrimSuffix(r.Prefix, "-") prefix := strings.TrimSuffix(r.Prefix, "-")
prefixToRig[prefix] = rigName prefixToRig[prefix] = rigInfo{
name: rigName,
beadsPath: r.Path, // Use the full route path
}
} }
} }
@@ -172,19 +185,21 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
// Find the first rig for global agents (only gt-prefix rigs) // Find the first rig for global agents (only gt-prefix rigs)
var firstRigName string var firstRigName string
for prefix, rigName := range prefixToRig { for prefix, info := range prefixToRig {
if prefix != "gt" { if prefix != "gt" {
continue continue
} }
if firstRigName == "" || rigName < firstRigName { if firstRigName == "" || info.name < firstRigName {
firstRigName = rigName firstRigName = info.name
} }
} }
// Create missing agents for each rig // Create missing agents for each rig
for prefix, rigName := range prefixToRig { for prefix, info := range prefixToRig {
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig") // Use the route path directly instead of hardcoding /mayor/rig
rigBeadsPath := filepath.Join(ctx.TownRoot, info.beadsPath)
bd := beads.New(rigBeadsPath) bd := beads.New(rigBeadsPath)
rigName := info.name
// Create rig-specific agents if missing (using canonical naming: prefix-rig-role-name) // Create rig-specific agents if missing (using canonical naming: prefix-rig-role-name)
witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName) witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName)