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.
func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
// 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
// Routes have format: prefix "gt-" -> path "gastown/mayor/rig"
prefixToRig := make(map[string]string) // prefix (without hyphen) -> rigName
// Build prefix -> rigInfo map from routes
// Routes have format: prefix "gt-" -> path "gastown/mayor/rig" or "my-saas"
prefixToRig := make(map[string]rigInfo) // prefix (without hyphen) -> rigInfo
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, "/")
if len(parts) >= 1 && parts[0] != "." {
rigName := parts[0]
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
// Only consider gt-prefix rigs since other prefixes can't have agent beads yet
var firstRigName string
for prefix, rigName := range prefixToRig {
for prefix, info := range prefixToRig {
if prefix != "gt" {
continue // Skip non-gt prefixes for first rig selection
}
if firstRigName == "" || rigName < firstRigName {
firstRigName = rigName
if firstRigName == "" || info.name < firstRigName {
firstRigName = info.name
}
}
// Check each rig for its agents
for prefix, rigName := range prefixToRig {
// Get beads client for this rig
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
for prefix, info := range prefixToRig {
// Get beads client for this rig using the route path directly
rigBeadsPath := filepath.Join(ctx.TownRoot, info.beadsPath)
bd := beads.New(rigBeadsPath)
rigName := info.name
// Check rig-specific agents (using canonical naming: prefix-rig-role-name)
witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName)
@@ -155,14 +165,17 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
return fmt.Errorf("loading routes.jsonl: %w", err)
}
// Build prefix -> rigName map from routes
prefixToRig := make(map[string]string)
// Build prefix -> rigInfo map from routes
prefixToRig := make(map[string]rigInfo)
for _, r := range routes {
parts := strings.Split(r.Path, "/")
if len(parts) >= 1 && parts[0] != "." {
rigName := parts[0]
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)
var firstRigName string
for prefix, rigName := range prefixToRig {
for prefix, info := range prefixToRig {
if prefix != "gt" {
continue
}
if firstRigName == "" || rigName < firstRigName {
firstRigName = rigName
if firstRigName == "" || info.name < firstRigName {
firstRigName = info.name
}
}
// Create missing agents for each rig
for prefix, rigName := range prefixToRig {
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
for prefix, info := range prefixToRig {
// Use the route path directly instead of hardcoding /mayor/rig
rigBeadsPath := filepath.Join(ctx.TownRoot, info.beadsPath)
bd := beads.New(rigBeadsPath)
rigName := info.name
// Create rig-specific agents if missing (using canonical naming: prefix-rig-role-name)
witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName)