diff --git a/internal/beads/beads.go b/internal/beads/beads.go index 466e5789..fa3b2105 100644 --- a/internal/beads/beads.go +++ b/internal/beads/beads.go @@ -719,21 +719,28 @@ func (b *Beads) GetAgentBead(id string) (*Issue, *AgentFields, error) { // - gt-gastown-crew-max (rig-level named agent) // - gt-gastown-polecat-Toast (rig-level named agent) -// AgentBeadID generates the canonical agent bead ID. +// AgentBeadIDWithPrefix generates an agent bead ID using the specified prefix. +// The prefix should NOT include the hyphen (e.g., "gt", "bd", not "gt-", "bd-"). // For town-level agents (mayor, deacon), pass empty rig and name. // For rig-level singletons (witness, refinery), pass empty name. // For named agents (crew, polecat), pass all three. -func AgentBeadID(rig, role, name string) string { +func AgentBeadIDWithPrefix(prefix, rig, role, name string) string { if rig == "" { - // Town-level agent: gt-mayor, gt-deacon - return "gt-" + role + // Town-level agent: prefix-mayor, prefix-deacon + return prefix + "-" + role } if name == "" { - // Rig-level singleton: gt-gastown-witness, gt-gastown-refinery - return "gt-" + rig + "-" + role + // Rig-level singleton: prefix-rig-witness, prefix-rig-refinery + return prefix + "-" + rig + "-" + role } - // Rig-level named agent: gt-gastown-crew-max, gt-gastown-polecat-Toast - return "gt-" + rig + "-" + role + "-" + name + // Rig-level named agent: prefix-rig-role-name + return prefix + "-" + rig + "-" + role + "-" + name +} + +// AgentBeadID generates the canonical agent bead ID using "gt" prefix. +// For non-gastown rigs, use AgentBeadIDWithPrefix with the rig's configured prefix. +func AgentBeadID(rig, role, name string) string { + return AgentBeadIDWithPrefix("gt", rig, role, name) } // MayorBeadID returns the Mayor agent bead ID. @@ -746,24 +753,44 @@ func DeaconBeadID() string { return "gt-deacon" } -// WitnessBeadID returns the Witness agent bead ID for a rig. +// WitnessBeadIDWithPrefix returns the Witness agent bead ID for a rig using the specified prefix. +func WitnessBeadIDWithPrefix(prefix, rig string) string { + return AgentBeadIDWithPrefix(prefix, rig, "witness", "") +} + +// WitnessBeadID returns the Witness agent bead ID for a rig using "gt" prefix. func WitnessBeadID(rig string) string { - return AgentBeadID(rig, "witness", "") + return WitnessBeadIDWithPrefix("gt", rig) } -// RefineryBeadID returns the Refinery agent bead ID for a rig. +// RefineryBeadIDWithPrefix returns the Refinery agent bead ID for a rig using the specified prefix. +func RefineryBeadIDWithPrefix(prefix, rig string) string { + return AgentBeadIDWithPrefix(prefix, rig, "refinery", "") +} + +// RefineryBeadID returns the Refinery agent bead ID for a rig using "gt" prefix. func RefineryBeadID(rig string) string { - return AgentBeadID(rig, "refinery", "") + return RefineryBeadIDWithPrefix("gt", rig) } -// CrewBeadID returns a Crew worker agent bead ID. +// CrewBeadIDWithPrefix returns a Crew worker agent bead ID using the specified prefix. +func CrewBeadIDWithPrefix(prefix, rig, name string) string { + return AgentBeadIDWithPrefix(prefix, rig, "crew", name) +} + +// CrewBeadID returns a Crew worker agent bead ID using "gt" prefix. func CrewBeadID(rig, name string) string { - return AgentBeadID(rig, "crew", name) + return CrewBeadIDWithPrefix("gt", rig, name) } -// PolecatBeadID returns a Polecat agent bead ID. +// PolecatBeadIDWithPrefix returns a Polecat agent bead ID using the specified prefix. +func PolecatBeadIDWithPrefix(prefix, rig, name string) string { + return AgentBeadIDWithPrefix(prefix, rig, "polecat", name) +} + +// PolecatBeadID returns a Polecat agent bead ID using "gt" prefix. func PolecatBeadID(rig, name string) string { - return AgentBeadID(rig, "polecat", name) + return PolecatBeadIDWithPrefix("gt", rig, name) } // ParseAgentBeadID parses an agent bead ID into its components. diff --git a/internal/cmd/crew_add.go b/internal/cmd/crew_add.go index 6400708d..c16d9325 100644 --- a/internal/cmd/crew_add.go +++ b/internal/cmd/crew_add.go @@ -81,7 +81,12 @@ func runCrewAdd(cmd *cobra.Command, args []string) error { // Create agent bead for the crew worker rigBeadsPath := filepath.Join(r.Path, "mayor", "rig") bd := beads.New(rigBeadsPath) - crewID := beads.CrewBeadID(rigName, name) + // Use the rig's configured prefix, defaulting to "gt" for backward compatibility + prefix := "gt" + if r.Config != nil && r.Config.Prefix != "" { + prefix = r.Config.Prefix + } + crewID := beads.CrewBeadIDWithPrefix(prefix, rigName, name) if _, err := bd.Show(crewID); err != nil { // Agent bead doesn't exist, create it fields := &beads.AgentFields{ diff --git a/internal/doctor/agent_beads_check.go b/internal/doctor/agent_beads_check.go index 708b7188..e343efdf 100644 --- a/internal/doctor/agent_beads_check.go +++ b/internal/doctor/agent_beads_check.go @@ -16,10 +16,7 @@ import ( // - Crew workers - stored in each rig's beads // // Agent beads are created by gt rig add (see gt-h3hak, gt-pinkq) and gt crew add. -// -// NOTE: Currently, the beads library validates that agent IDs must start -// with 'gt-'. Rigs with different prefixes (like 'bd-') cannot have agent -// beads created until that validation is fixed in the beads repo. +// Each rig uses its configured prefix (e.g., "gt-" for gastown, "bd-" for beads). type AgentBeadsCheck struct { FixableCheck } @@ -86,22 +83,14 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult { } // Check each rig for its agents - var skipped []string for prefix, rigName := range prefixToRig { - // Skip non-gt prefixes - beads library currently requires gt- prefix for agents - // TODO: Remove this once beads validation is fixed to accept any prefix - if prefix != "gt" { - skipped = append(skipped, fmt.Sprintf("%s (%s-*)", rigName, prefix)) - continue - } - // Get beads client for this rig rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig") bd := beads.New(rigBeadsPath) // Check rig-specific agents (using canonical naming: prefix-rig-role-name) - witnessID := beads.WitnessBeadID(rigName) - refineryID := beads.RefineryBeadID(rigName) + witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName) + refineryID := beads.RefineryBeadIDWithPrefix(prefix, rigName) if _, err := bd.Show(witnessID); err != nil { missing = append(missing, witnessID) @@ -116,7 +105,7 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult { // Check crew worker agents crewWorkers := listCrewWorkers(ctx.TownRoot, rigName) for _, workerName := range crewWorkers { - crewID := beads.CrewBeadID(rigName, workerName) + crewID := beads.CrewBeadIDWithPrefix(prefix, rigName, workerName) if _, err := bd.Show(crewID); err != nil { missing = append(missing, crewID) } @@ -141,31 +130,18 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult { } if len(missing) == 0 { - msg := fmt.Sprintf("All %d agent beads exist", checked) - var details []string - if len(skipped) > 0 { - details = append(details, fmt.Sprintf("Skipped %d rig(s) with non-gt prefix (beads library limitation): %s", - len(skipped), strings.Join(skipped, ", "))) - } return &CheckResult{ Name: c.Name(), Status: StatusOK, - Message: msg, - Details: details, + Message: fmt.Sprintf("All %d agent beads exist", checked), } } - details := missing - if len(skipped) > 0 { - details = append(details, fmt.Sprintf("Skipped %d rig(s) with non-gt prefix: %s", - len(skipped), strings.Join(skipped, ", "))) - } - return &CheckResult{ Name: c.Name(), Status: StatusError, Message: fmt.Sprintf("%d agent bead(s) missing", len(missing)), - Details: details, + Details: missing, FixHint: "Run 'gt doctor --fix' to create missing agent beads", } } @@ -207,16 +183,11 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error { // Create missing agents for each rig for prefix, rigName := range prefixToRig { - // Skip non-gt prefixes - beads library currently requires gt- prefix for agents - if prefix != "gt" { - continue - } - rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig") bd := beads.New(rigBeadsPath) // Create rig-specific agents if missing (using canonical naming: prefix-rig-role-name) - witnessID := beads.WitnessBeadID(rigName) + witnessID := beads.WitnessBeadIDWithPrefix(prefix, rigName) if _, err := bd.Show(witnessID); err != nil { fields := &beads.AgentFields{ RoleType: "witness", @@ -230,7 +201,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error { } } - refineryID := beads.RefineryBeadID(rigName) + refineryID := beads.RefineryBeadIDWithPrefix(prefix, rigName) if _, err := bd.Show(refineryID); err != nil { fields := &beads.AgentFields{ RoleType: "refinery", @@ -247,7 +218,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error { // Create crew worker agents if missing crewWorkers := listCrewWorkers(ctx.TownRoot, rigName) for _, workerName := range crewWorkers { - crewID := beads.CrewBeadID(rigName, workerName) + crewID := beads.CrewBeadIDWithPrefix(prefix, rigName, workerName) if _, err := bd.Show(crewID); err != nil { fields := &beads.AgentFields{ RoleType: "crew", diff --git a/internal/rig/manager.go b/internal/rig/manager.go index 24631093..5af62246 100644 --- a/internal/rig/manager.go +++ b/internal/rig/manager.go @@ -417,13 +417,13 @@ func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig boo // Always create rig-specific agents (using canonical naming: prefix-rig-role-name) agents = append(agents, agentDef{ - id: beads.WitnessBeadID(rigName), + id: beads.WitnessBeadIDWithPrefix(prefix, rigName), roleType: "witness", rig: rigName, desc: fmt.Sprintf("Witness for %s - monitors polecat health and progress.", rigName), }, agentDef{ - id: beads.RefineryBeadID(rigName), + id: beads.RefineryBeadIDWithPrefix(prefix, rigName), roleType: "refinery", rig: rigName, desc: fmt.Sprintf("Refinery for %s - processes merge queue.", rigName),