feat: Standardize agent bead naming to prefix-rig-role-name (gt-zvte2)
Implements canonical naming convention for agent bead IDs: - Town-level: gt-mayor, gt-deacon (unchanged) - Rig-level: gt-<rig>-witness, gt-<rig>-refinery (was gt-witness-<rig>) - Named: gt-<rig>-crew-<name>, gt-<rig>-polecat-<name> (was gt-crew-<rig>-<name>) Changes: - Added AgentBeadID helper functions to internal/beads/beads.go - Updated all ID generation call sites to use helpers - Fixed session parsing in theme.go, statusline.go, agents.go - Updated doctor check and fix to use canonical format - Updated tests for new format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+90
-1
@@ -607,7 +607,8 @@ func ParseAgentFields(description string) *AgentFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateAgentBead creates an agent bead for tracking agent lifecycle.
|
// CreateAgentBead creates an agent bead for tracking agent lifecycle.
|
||||||
// The ID format is: <prefix>-<role>-<rig>-<name> (e.g., gt-polecat-gastown-Toast)
|
// The ID format is: <prefix>-<rig>-<role>-<name> (e.g., gt-gastown-polecat-Toast)
|
||||||
|
// Use AgentBeadID() helper to generate correct IDs.
|
||||||
func (b *Beads) CreateAgentBead(id, title string, fields *AgentFields) (*Issue, error) {
|
func (b *Beads) CreateAgentBead(id, title string, fields *AgentFields) (*Issue, error) {
|
||||||
description := FormatAgentDescription(title, fields)
|
description := FormatAgentDescription(title, fields)
|
||||||
|
|
||||||
@@ -706,3 +707,91 @@ func (b *Beads) GetAgentBead(id string) (*Issue, *AgentFields, error) {
|
|||||||
fields := ParseAgentFields(issue.Description)
|
fields := ParseAgentFields(issue.Description)
|
||||||
return issue, fields, nil
|
return issue, fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Agent bead ID naming convention:
|
||||||
|
// prefix-rig-role-name
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// - gt-mayor (town-level, no rig)
|
||||||
|
// - gt-deacon (town-level, no rig)
|
||||||
|
// - gt-gastown-witness (rig-level singleton)
|
||||||
|
// - gt-gastown-refinery (rig-level singleton)
|
||||||
|
// - gt-gastown-crew-max (rig-level named agent)
|
||||||
|
// - gt-gastown-polecat-Toast (rig-level named agent)
|
||||||
|
|
||||||
|
// AgentBeadID generates the canonical agent bead ID.
|
||||||
|
// 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 {
|
||||||
|
if rig == "" {
|
||||||
|
// Town-level agent: gt-mayor, gt-deacon
|
||||||
|
return "gt-" + role
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
// Rig-level singleton: gt-gastown-witness, gt-gastown-refinery
|
||||||
|
return "gt-" + rig + "-" + role
|
||||||
|
}
|
||||||
|
// Rig-level named agent: gt-gastown-crew-max, gt-gastown-polecat-Toast
|
||||||
|
return "gt-" + rig + "-" + role + "-" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
// MayorBeadID returns the Mayor agent bead ID.
|
||||||
|
func MayorBeadID() string {
|
||||||
|
return "gt-mayor"
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeaconBeadID returns the Deacon agent bead ID.
|
||||||
|
func DeaconBeadID() string {
|
||||||
|
return "gt-deacon"
|
||||||
|
}
|
||||||
|
|
||||||
|
// WitnessBeadID returns the Witness agent bead ID for a rig.
|
||||||
|
func WitnessBeadID(rig string) string {
|
||||||
|
return AgentBeadID(rig, "witness", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefineryBeadID returns the Refinery agent bead ID for a rig.
|
||||||
|
func RefineryBeadID(rig string) string {
|
||||||
|
return AgentBeadID(rig, "refinery", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CrewBeadID returns a Crew worker agent bead ID.
|
||||||
|
func CrewBeadID(rig, name string) string {
|
||||||
|
return AgentBeadID(rig, "crew", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PolecatBeadID returns a Polecat agent bead ID.
|
||||||
|
func PolecatBeadID(rig, name string) string {
|
||||||
|
return AgentBeadID(rig, "polecat", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseAgentBeadID parses an agent bead ID into its components.
|
||||||
|
// Returns rig, role, name, and whether parsing succeeded.
|
||||||
|
// For town-level agents, rig will be empty.
|
||||||
|
// For singletons, name will be empty.
|
||||||
|
func ParseAgentBeadID(id string) (rig, role, name string, ok bool) {
|
||||||
|
if !strings.HasPrefix(id, "gt-") {
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
rest := strings.TrimPrefix(id, "gt-")
|
||||||
|
parts := strings.Split(rest, "-")
|
||||||
|
|
||||||
|
switch len(parts) {
|
||||||
|
case 1:
|
||||||
|
// Town-level: gt-mayor, gt-deacon
|
||||||
|
return "", parts[0], "", true
|
||||||
|
case 2:
|
||||||
|
// Rig-level singleton: gt-gastown-witness
|
||||||
|
return parts[0], parts[1], "", true
|
||||||
|
case 3:
|
||||||
|
// Rig-level named: gt-gastown-crew-max
|
||||||
|
return parts[0], parts[1], parts[2], true
|
||||||
|
default:
|
||||||
|
// Handle names with hyphens: gt-gastown-polecat-my-agent-name
|
||||||
|
if len(parts) >= 3 {
|
||||||
|
return parts[0], parts[1], strings.Join(parts[2:], "-"), true
|
||||||
|
}
|
||||||
|
return "", "", "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ func categorizeSession(name string) *AgentSession {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
// Witness sessions use different format: gt-witness-<rig>
|
// Witness sessions: legacy format gt-witness-<rig> (fallback)
|
||||||
if strings.HasPrefix(suffix, "witness-") {
|
if strings.HasPrefix(suffix, "witness-") {
|
||||||
session.Type = AgentWitness
|
session.Type = AgentWitness
|
||||||
session.Rig = strings.TrimPrefix(suffix, "witness-")
|
session.Rig = strings.TrimPrefix(suffix, "witness-")
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func runCrewAdd(cmd *cobra.Command, args []string) error {
|
|||||||
// Create agent bead for the crew worker
|
// Create agent bead for the crew worker
|
||||||
rigBeadsPath := filepath.Join(r.Path, "mayor", "rig")
|
rigBeadsPath := filepath.Join(r.Path, "mayor", "rig")
|
||||||
bd := beads.New(rigBeadsPath)
|
bd := beads.New(rigBeadsPath)
|
||||||
crewID := fmt.Sprintf("gt-crew-%s-%s", rigName, name)
|
crewID := beads.CrewBeadID(rigName, name)
|
||||||
if _, err := bd.Show(crewID); err != nil {
|
if _, err := bd.Show(crewID); err != nil {
|
||||||
// Agent bead doesn't exist, create it
|
// Agent bead doesn't exist, create it
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ import (
|
|||||||
// Note: Agent field parsing is now in internal/beads/fields.go (AgentFields, ParseAgentFieldsFromDescription)
|
// Note: Agent field parsing is now in internal/beads/fields.go (AgentFields, ParseAgentFieldsFromDescription)
|
||||||
|
|
||||||
// buildAgentBeadID constructs the agent bead ID from an agent identity.
|
// buildAgentBeadID constructs the agent bead ID from an agent identity.
|
||||||
|
// Uses canonical naming: prefix-rig-role-name
|
||||||
// Examples:
|
// Examples:
|
||||||
// - "mayor" -> "gt-mayor"
|
// - "mayor" -> "gt-mayor"
|
||||||
// - "deacon" -> "gt-deacon"
|
// - "deacon" -> "gt-deacon"
|
||||||
// - "gastown/witness" -> "gt-witness-gastown"
|
// - "gastown/witness" -> "gt-gastown-witness"
|
||||||
// - "gastown/refinery" -> "gt-refinery-gastown"
|
// - "gastown/refinery" -> "gt-gastown-refinery"
|
||||||
// - "gastown/nux" (polecat) -> "gt-polecat-gastown-nux"
|
// - "gastown/nux" (polecat) -> "gt-gastown-polecat-nux"
|
||||||
|
// - "gastown/crew/max" -> "gt-gastown-crew-max"
|
||||||
//
|
//
|
||||||
// If role is unknown, it tries to infer from the identity string.
|
// If role is unknown, it tries to infer from the identity string.
|
||||||
func buildAgentBeadID(identity string, role Role) string {
|
func buildAgentBeadID(identity string, role Role) string {
|
||||||
@@ -32,22 +34,22 @@ func buildAgentBeadID(identity string, role Role) string {
|
|||||||
if role == RoleUnknown || role == Role("") {
|
if role == RoleUnknown || role == Role("") {
|
||||||
switch {
|
switch {
|
||||||
case identity == "mayor":
|
case identity == "mayor":
|
||||||
return "gt-mayor"
|
return beads.MayorBeadID()
|
||||||
case identity == "deacon":
|
case identity == "deacon":
|
||||||
return "gt-deacon"
|
return beads.DeaconBeadID()
|
||||||
case len(parts) == 2 && parts[1] == "witness":
|
case len(parts) == 2 && parts[1] == "witness":
|
||||||
return "gt-witness-" + parts[0]
|
return beads.WitnessBeadID(parts[0])
|
||||||
case len(parts) == 2 && parts[1] == "refinery":
|
case len(parts) == 2 && parts[1] == "refinery":
|
||||||
return "gt-refinery-" + parts[0]
|
return beads.RefineryBeadID(parts[0])
|
||||||
case len(parts) == 2:
|
case len(parts) == 2:
|
||||||
// Assume rig/name is a polecat
|
// Assume rig/name is a polecat
|
||||||
return "gt-polecat-" + parts[0] + "-" + parts[1]
|
return beads.PolecatBeadID(parts[0], parts[1])
|
||||||
case len(parts) == 3 && parts[1] == "crew":
|
case len(parts) == 3 && parts[1] == "crew":
|
||||||
// rig/crew/name - crew member (no agent bead)
|
// rig/crew/name - crew member
|
||||||
return ""
|
return beads.CrewBeadID(parts[0], parts[2])
|
||||||
case len(parts) == 3 && parts[1] == "polecats":
|
case len(parts) == 3 && parts[1] == "polecats":
|
||||||
// rig/polecats/name - explicit polecat
|
// rig/polecats/name - explicit polecat
|
||||||
return "gt-polecat-" + parts[0] + "-" + parts[2]
|
return beads.PolecatBeadID(parts[0], parts[2])
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -55,28 +57,28 @@ func buildAgentBeadID(identity string, role Role) string {
|
|||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case RoleMayor:
|
case RoleMayor:
|
||||||
return "gt-mayor"
|
return beads.MayorBeadID()
|
||||||
case RoleDeacon:
|
case RoleDeacon:
|
||||||
return "gt-deacon"
|
return beads.DeaconBeadID()
|
||||||
case RoleWitness:
|
case RoleWitness:
|
||||||
if len(parts) >= 1 {
|
if len(parts) >= 1 {
|
||||||
return "gt-witness-" + parts[0]
|
return beads.WitnessBeadID(parts[0])
|
||||||
}
|
}
|
||||||
return "gt-witness"
|
return ""
|
||||||
case RoleRefinery:
|
case RoleRefinery:
|
||||||
if len(parts) >= 1 {
|
if len(parts) >= 1 {
|
||||||
return "gt-refinery-" + parts[0]
|
return beads.RefineryBeadID(parts[0])
|
||||||
}
|
}
|
||||||
return "gt-refinery"
|
return ""
|
||||||
case RolePolecat:
|
case RolePolecat:
|
||||||
if len(parts) >= 2 {
|
if len(parts) >= 2 {
|
||||||
return "gt-polecat-" + parts[0] + "-" + parts[1]
|
return beads.PolecatBeadID(parts[0], parts[1])
|
||||||
} else if len(parts) == 1 {
|
|
||||||
return "gt-polecat-" + parts[0]
|
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case RoleCrew:
|
case RoleCrew:
|
||||||
// Crew members may not have agent beads
|
if len(parts) >= 3 && parts[1] == "crew" {
|
||||||
|
return beads.CrewBeadID(parts[0], parts[2])
|
||||||
|
}
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/git"
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/polecat"
|
"github.com/steveyegge/gastown/internal/polecat"
|
||||||
"github.com/steveyegge/gastown/internal/rig"
|
"github.com/steveyegge/gastown/internal/rig"
|
||||||
@@ -1208,7 +1209,7 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Printf(" - Kill session: gt-%s-%s\n", p.rigName, p.polecatName)
|
fmt.Printf(" - Kill session: gt-%s-%s\n", p.rigName, p.polecatName)
|
||||||
fmt.Printf(" - Delete worktree: %s/polecats/%s\n", p.r.Path, p.polecatName)
|
fmt.Printf(" - Delete worktree: %s/polecats/%s\n", p.r.Path, p.polecatName)
|
||||||
fmt.Printf(" - Delete branch (if exists)\n")
|
fmt.Printf(" - Delete branch (if exists)\n")
|
||||||
fmt.Printf(" - Close agent bead: gt-polecat-%s-%s\n", p.rigName, p.polecatName)
|
fmt.Printf(" - Close agent bead: %s\n", beads.PolecatBeadID(p.rigName, p.polecatName))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1257,7 +1258,7 @@ func runPolecatNuke(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 5: Close agent bead (if exists)
|
// Step 5: Close agent bead (if exists)
|
||||||
agentBeadID := fmt.Sprintf("gt-polecat-%s-%s", p.rigName, p.polecatName)
|
agentBeadID := beads.PolecatBeadID(p.rigName, p.polecatName)
|
||||||
closeCmd := exec.Command("bd", "close", agentBeadID, "--reason=nuked")
|
closeCmd := exec.Command("bd", "close", agentBeadID, "--reason=nuked")
|
||||||
closeCmd.Dir = filepath.Join(p.r.Path, "mayor", "rig")
|
closeCmd.Dir = filepath.Join(p.r.Path, "mayor", "rig")
|
||||||
if err := closeCmd.Run(); err != nil {
|
if err := closeCmd.Run(); err != nil {
|
||||||
|
|||||||
@@ -1137,31 +1137,32 @@ func getAgentFields(ctx RoleContext, state string) *beads.AgentFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getAgentBeadID returns the agent bead ID for the current role.
|
// getAgentBeadID returns the agent bead ID for the current role.
|
||||||
|
// Uses canonical naming: prefix-rig-role-name
|
||||||
// Returns empty string for unknown roles.
|
// Returns empty string for unknown roles.
|
||||||
func getAgentBeadID(ctx RoleContext) string {
|
func getAgentBeadID(ctx RoleContext) string {
|
||||||
switch ctx.Role {
|
switch ctx.Role {
|
||||||
case RoleMayor:
|
case RoleMayor:
|
||||||
return "gt-mayor"
|
return beads.MayorBeadID()
|
||||||
case RoleDeacon:
|
case RoleDeacon:
|
||||||
return "gt-deacon"
|
return beads.DeaconBeadID()
|
||||||
case RoleWitness:
|
case RoleWitness:
|
||||||
if ctx.Rig != "" {
|
if ctx.Rig != "" {
|
||||||
return fmt.Sprintf("gt-witness-%s", ctx.Rig)
|
return beads.WitnessBeadID(ctx.Rig)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case RoleRefinery:
|
case RoleRefinery:
|
||||||
if ctx.Rig != "" {
|
if ctx.Rig != "" {
|
||||||
return fmt.Sprintf("gt-refinery-%s", ctx.Rig)
|
return beads.RefineryBeadID(ctx.Rig)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case RolePolecat:
|
case RolePolecat:
|
||||||
if ctx.Rig != "" && ctx.Polecat != "" {
|
if ctx.Rig != "" && ctx.Polecat != "" {
|
||||||
return fmt.Sprintf("gt-polecat-%s-%s", ctx.Rig, ctx.Polecat)
|
return beads.PolecatBeadID(ctx.Rig, ctx.Polecat)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
case RoleCrew:
|
case RoleCrew:
|
||||||
if ctx.Rig != "" && ctx.Polecat != "" {
|
if ctx.Rig != "" && ctx.Polecat != "" {
|
||||||
return fmt.Sprintf("gt-crew-%s-%s", ctx.Rig, ctx.Polecat)
|
return beads.CrewBeadID(ctx.Rig, ctx.Polecat)
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
|
|||||||
+11
-10
@@ -632,11 +632,11 @@ func runSlingFormula(args []string) error {
|
|||||||
// This enables the witness to see what each agent is working on.
|
// This enables the witness to see what each agent is working on.
|
||||||
func updateAgentHookBead(agentID, beadID string) {
|
func updateAgentHookBead(agentID, beadID string) {
|
||||||
// Convert agent ID to agent bead ID
|
// Convert agent ID to agent bead ID
|
||||||
// Format examples:
|
// Format examples (canonical: prefix-rig-role-name):
|
||||||
// gastown/crew/max -> gt-crew-gastown-max
|
// gastown/crew/max -> gt-gastown-crew-max
|
||||||
// gastown/polecats/Toast -> gt-polecat-gastown-Toast
|
// gastown/polecats/Toast -> gt-gastown-polecat-Toast
|
||||||
// mayor -> gt-mayor
|
// mayor -> gt-mayor
|
||||||
// gastown/witness -> gt-witness-gastown
|
// gastown/witness -> gt-gastown-witness
|
||||||
agentBeadID := agentIDToBeadID(agentID)
|
agentBeadID := agentIDToBeadID(agentID)
|
||||||
if agentBeadID == "" {
|
if agentBeadID == "" {
|
||||||
return
|
return
|
||||||
@@ -673,13 +673,14 @@ func wakeRigAgents(rigName string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentIDToBeadID converts an agent ID to its corresponding agent bead ID.
|
// agentIDToBeadID converts an agent ID to its corresponding agent bead ID.
|
||||||
|
// Uses canonical naming: prefix-rig-role-name
|
||||||
func agentIDToBeadID(agentID string) string {
|
func agentIDToBeadID(agentID string) string {
|
||||||
// Handle simple cases
|
// Handle simple cases
|
||||||
if agentID == "mayor" {
|
if agentID == "mayor" {
|
||||||
return "gt-mayor"
|
return beads.MayorBeadID()
|
||||||
}
|
}
|
||||||
if agentID == "deacon" {
|
if agentID == "deacon" {
|
||||||
return "gt-deacon"
|
return beads.DeaconBeadID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse path-style agent IDs
|
// Parse path-style agent IDs
|
||||||
@@ -692,13 +693,13 @@ func agentIDToBeadID(agentID string) string {
|
|||||||
|
|
||||||
switch {
|
switch {
|
||||||
case len(parts) == 2 && parts[1] == "witness":
|
case len(parts) == 2 && parts[1] == "witness":
|
||||||
return fmt.Sprintf("gt-witness-%s", rig)
|
return beads.WitnessBeadID(rig)
|
||||||
case len(parts) == 2 && parts[1] == "refinery":
|
case len(parts) == 2 && parts[1] == "refinery":
|
||||||
return fmt.Sprintf("gt-refinery-%s", rig)
|
return beads.RefineryBeadID(rig)
|
||||||
case len(parts) == 3 && parts[1] == "crew":
|
case len(parts) == 3 && parts[1] == "crew":
|
||||||
return fmt.Sprintf("gt-crew-%s-%s", rig, parts[2])
|
return beads.CrewBeadID(rig, parts[2])
|
||||||
case len(parts) == 3 && parts[1] == "polecats":
|
case len(parts) == 3 && parts[1] == "polecats":
|
||||||
return fmt.Sprintf("gt-polecat-%s-%s", rig, parts[2])
|
return beads.PolecatBeadID(rig, parts[2])
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-11
@@ -313,23 +313,26 @@ func renderAgentDetails(agent AgentRuntime, indent string, hooks []AgentHookInfo
|
|||||||
stateInfo = style.Dim.Render(fmt.Sprintf(" [%s]", agent.State))
|
stateInfo = style.Dim.Render(fmt.Sprintf(" [%s]", agent.State))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build agent bead ID
|
// Build agent bead ID using canonical naming: prefix-rig-role-name
|
||||||
agentBeadID := "gt-" + agent.Name
|
agentBeadID := "gt-" + agent.Name
|
||||||
if agent.Address != "" && agent.Address != agent.Name {
|
if agent.Address != "" && agent.Address != agent.Name {
|
||||||
// Use address for full path agents like gastown/crew/joe → gt-crew-gastown-joe
|
// Use address for full path agents like gastown/crew/joe → gt-gastown-crew-joe
|
||||||
addr := strings.TrimSuffix(agent.Address, "/") // Remove trailing slash for global agents
|
addr := strings.TrimSuffix(agent.Address, "/") // Remove trailing slash for global agents
|
||||||
parts := strings.Split(addr, "/")
|
parts := strings.Split(addr, "/")
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
// Global agent: mayor/, deacon/ → gt-mayor, gt-deacon
|
// Global agent: mayor/, deacon/ → gt-mayor, gt-deacon
|
||||||
agentBeadID = "gt-" + parts[0]
|
agentBeadID = beads.AgentBeadID("", parts[0], "")
|
||||||
} else if len(parts) >= 2 {
|
} else if len(parts) >= 2 {
|
||||||
|
rig := parts[0]
|
||||||
if parts[1] == "crew" && len(parts) >= 3 {
|
if parts[1] == "crew" && len(parts) >= 3 {
|
||||||
agentBeadID = fmt.Sprintf("gt-crew-%s-%s", parts[0], parts[2])
|
agentBeadID = beads.CrewBeadID(rig, parts[2])
|
||||||
} else if parts[1] == "witness" || parts[1] == "refinery" {
|
} else if parts[1] == "witness" {
|
||||||
agentBeadID = fmt.Sprintf("gt-%s-%s", parts[1], parts[0])
|
agentBeadID = beads.WitnessBeadID(rig)
|
||||||
|
} else if parts[1] == "refinery" {
|
||||||
|
agentBeadID = beads.RefineryBeadID(rig)
|
||||||
} else if len(parts) == 2 {
|
} else if len(parts) == 2 {
|
||||||
// polecat: rig/name
|
// polecat: rig/name
|
||||||
agentBeadID = fmt.Sprintf("gt-polecat-%s-%s", parts[0], parts[1])
|
agentBeadID = beads.PolecatBeadID(rig, parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -531,7 +534,7 @@ func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *bea
|
|||||||
Running: running,
|
Running: running,
|
||||||
}
|
}
|
||||||
// Look up agent bead
|
// Look up agent bead
|
||||||
agentID := fmt.Sprintf("gt-witness-%s", r.Name)
|
agentID := beads.WitnessBeadID(r.Name)
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
||||||
witness.HookBead = fields.HookBead
|
witness.HookBead = fields.HookBead
|
||||||
witness.State = fields.AgentState
|
witness.State = fields.AgentState
|
||||||
@@ -558,7 +561,7 @@ func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *bea
|
|||||||
Running: running,
|
Running: running,
|
||||||
}
|
}
|
||||||
// Look up agent bead
|
// Look up agent bead
|
||||||
agentID := fmt.Sprintf("gt-refinery-%s", r.Name)
|
agentID := beads.RefineryBeadID(r.Name)
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
||||||
refinery.HookBead = fields.HookBead
|
refinery.HookBead = fields.HookBead
|
||||||
refinery.State = fields.AgentState
|
refinery.State = fields.AgentState
|
||||||
@@ -585,7 +588,7 @@ func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *bea
|
|||||||
Running: running,
|
Running: running,
|
||||||
}
|
}
|
||||||
// Look up agent bead
|
// Look up agent bead
|
||||||
agentID := fmt.Sprintf("gt-polecat-%s-%s", r.Name, name)
|
agentID := beads.PolecatBeadID(r.Name, name)
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
||||||
polecat.HookBead = fields.HookBead
|
polecat.HookBead = fields.HookBead
|
||||||
polecat.State = fields.AgentState
|
polecat.State = fields.AgentState
|
||||||
@@ -612,7 +615,7 @@ func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *bea
|
|||||||
Running: running,
|
Running: running,
|
||||||
}
|
}
|
||||||
// Look up agent bead
|
// Look up agent bead
|
||||||
agentID := fmt.Sprintf("gt-crew-%s-%s", r.Name, name)
|
agentID := beads.CrewBeadID(r.Name, name)
|
||||||
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
|
||||||
crewAgent.HookBead = fields.HookBead
|
crewAgent.HookBead = fields.HookBead
|
||||||
crewAgent.State = fields.AgentState
|
crewAgent.State = fields.AgentState
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ func runStatusLine(cmd *cobra.Command, args []string) error {
|
|||||||
return runDeaconStatusLine(t)
|
return runDeaconStatusLine(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Witness status line (session naming: gt-witness-<rig>)
|
// Witness status line (session naming: gt-<rig>-witness)
|
||||||
if role == "witness" || strings.HasPrefix(statusLineSession, "gt-witness-") {
|
if role == "witness" || strings.HasSuffix(statusLineSession, "-witness") {
|
||||||
return runWitnessStatusLine(t, rigName)
|
return runWitnessStatusLine(t, rigName)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,9 +221,9 @@ func runDeaconStatusLine(t *tmux.Tmux) error {
|
|||||||
// Shows: polecat count, crew count, mail preview
|
// Shows: polecat count, crew count, mail preview
|
||||||
func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
|
func runWitnessStatusLine(t *tmux.Tmux, rigName string) error {
|
||||||
if rigName == "" {
|
if rigName == "" {
|
||||||
// Try to extract from session name: gt-witness-<rig>
|
// Try to extract from session name: gt-<rig>-witness
|
||||||
if strings.HasPrefix(statusLineSession, "gt-witness-") {
|
if strings.HasSuffix(statusLineSession, "-witness") && strings.HasPrefix(statusLineSession, "gt-") {
|
||||||
rigName = strings.TrimPrefix(statusLineSession, "gt-witness-")
|
rigName = strings.TrimPrefix(strings.TrimSuffix(statusLineSession, "-witness"), "gt-")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ func TestCategorizeSessionRig(t *testing.T) {
|
|||||||
{"gt-gastown-crew-max", "gastown"},
|
{"gt-gastown-crew-max", "gastown"},
|
||||||
{"gt-myrig-crew-user", "myrig"},
|
{"gt-myrig-crew-user", "myrig"},
|
||||||
|
|
||||||
// Witness sessions (actual format: gt-witness-<rig>)
|
// Witness sessions (canonical format: gt-<rig>-witness)
|
||||||
{"gt-witness-gastown", "gastown"},
|
|
||||||
{"gt-witness-myrig", "myrig"},
|
|
||||||
// Legacy format still works as fallback
|
|
||||||
{"gt-gastown-witness", "gastown"},
|
{"gt-gastown-witness", "gastown"},
|
||||||
{"gt-myrig-witness", "myrig"},
|
{"gt-myrig-witness", "myrig"},
|
||||||
|
// Legacy format still works as fallback
|
||||||
|
{"gt-witness-gastown", "gastown"},
|
||||||
|
{"gt-witness-myrig", "myrig"},
|
||||||
|
|
||||||
// Refinery sessions
|
// Refinery sessions
|
||||||
{"gt-gastown-refinery", "gastown"},
|
{"gt-gastown-refinery", "gastown"},
|
||||||
@@ -61,8 +61,8 @@ func TestCategorizeSessionType(t *testing.T) {
|
|||||||
{"gt-a-b", AgentPolecat},
|
{"gt-a-b", AgentPolecat},
|
||||||
|
|
||||||
// Non-polecat sessions
|
// Non-polecat sessions
|
||||||
{"gt-witness-gastown", AgentWitness}, // actual format
|
{"gt-gastown-witness", AgentWitness}, // canonical format
|
||||||
{"gt-gastown-witness", AgentWitness}, // legacy fallback
|
{"gt-witness-gastown", AgentWitness}, // legacy fallback
|
||||||
{"gt-gastown-refinery", AgentRefinery},
|
{"gt-gastown-refinery", AgentRefinery},
|
||||||
{"gt-gastown-crew-max", AgentCrew},
|
{"gt-gastown-crew-max", AgentCrew},
|
||||||
{"gt-myrig-crew-user", AgentCrew},
|
{"gt-myrig-crew-user", AgentCrew},
|
||||||
|
|||||||
@@ -135,9 +135,9 @@ func runThemeApply(cmd *cobra.Command, args []string) error {
|
|||||||
theme = tmux.DeaconTheme()
|
theme = tmux.DeaconTheme()
|
||||||
worker = "Deacon"
|
worker = "Deacon"
|
||||||
role = "health-check"
|
role = "health-check"
|
||||||
} else if strings.HasPrefix(session, "gt-witness-") {
|
} else if strings.HasSuffix(session, "-witness") && strings.HasPrefix(session, "gt-") {
|
||||||
// Witness sessions: gt-witness-<rig>
|
// Witness sessions: gt-<rig>-witness
|
||||||
rig = strings.TrimPrefix(session, "gt-witness-")
|
rig = strings.TrimPrefix(strings.TrimSuffix(session, "-witness"), "gt-")
|
||||||
theme = getThemeForRole(rig, "witness")
|
theme = getThemeForRole(rig, "witness")
|
||||||
worker = "witness"
|
worker = "witness"
|
||||||
role = "witness"
|
role = "witness"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
"github.com/steveyegge/gastown/internal/constants"
|
"github.com/steveyegge/gastown/internal/constants"
|
||||||
"github.com/steveyegge/gastown/internal/keepalive"
|
"github.com/steveyegge/gastown/internal/keepalive"
|
||||||
"github.com/steveyegge/gastown/internal/polecat"
|
"github.com/steveyegge/gastown/internal/polecat"
|
||||||
@@ -327,7 +328,7 @@ func (d *Daemon) ensureWitnessesRunning() {
|
|||||||
|
|
||||||
// ensureWitnessRunning ensures the witness for a specific rig is running.
|
// ensureWitnessRunning ensures the witness for a specific rig is running.
|
||||||
func (d *Daemon) ensureWitnessRunning(rigName string) {
|
func (d *Daemon) ensureWitnessRunning(rigName string) {
|
||||||
agentID := "gt-witness-" + rigName
|
agentID := beads.WitnessBeadID(rigName)
|
||||||
sessionName := "gt-" + rigName + "-witness"
|
sessionName := "gt-" + rigName + "-witness"
|
||||||
|
|
||||||
// Check agent bead state (ZFC: trust what agent reports)
|
// Check agent bead state (ZFC: trust what agent reports)
|
||||||
@@ -374,7 +375,7 @@ func (d *Daemon) pokeWitnesses() {
|
|||||||
|
|
||||||
// pokeWitness sends a heartbeat to a specific rig's witness.
|
// pokeWitness sends a heartbeat to a specific rig's witness.
|
||||||
func (d *Daemon) pokeWitness(rigName string) {
|
func (d *Daemon) pokeWitness(rigName string) {
|
||||||
agentID := "gt-witness-" + rigName
|
agentID := beads.WitnessBeadID(rigName)
|
||||||
sessionName := "gt-" + rigName + "-witness"
|
sessionName := "gt-" + rigName + "-witness"
|
||||||
|
|
||||||
// Check agent bead state (ZFC: trust what agent reports)
|
// Check agent bead state (ZFC: trust what agent reports)
|
||||||
|
|||||||
@@ -544,33 +544,34 @@ func (d *Daemon) getAgentBeadInfo(agentBeadID string) (*AgentBeadInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// identityToAgentBeadID maps a daemon identity to an agent bead ID.
|
// identityToAgentBeadID maps a daemon identity to an agent bead ID.
|
||||||
|
// Uses the canonical naming convention: prefix-rig-role-name
|
||||||
// Examples:
|
// Examples:
|
||||||
// - "deacon" → "gt-deacon"
|
// - "deacon" → "gt-deacon"
|
||||||
// - "mayor" → "gt-mayor"
|
// - "mayor" → "gt-mayor"
|
||||||
// - "gastown-witness" → "gt-witness-gastown"
|
// - "gastown-witness" → "gt-gastown-witness"
|
||||||
// - "gastown-refinery" → "gt-refinery-gastown"
|
// - "gastown-refinery" → "gt-gastown-refinery"
|
||||||
func (d *Daemon) identityToAgentBeadID(identity string) string {
|
func (d *Daemon) identityToAgentBeadID(identity string) string {
|
||||||
switch identity {
|
switch identity {
|
||||||
case "deacon":
|
case "deacon":
|
||||||
return "gt-deacon"
|
return beads.DeaconBeadID()
|
||||||
case "mayor":
|
case "mayor":
|
||||||
return "gt-mayor"
|
return beads.MayorBeadID()
|
||||||
default:
|
default:
|
||||||
// Pattern: <rig>-witness → gt-witness-<rig>
|
// Pattern: <rig>-witness → gt-<rig>-witness
|
||||||
if strings.HasSuffix(identity, "-witness") {
|
if strings.HasSuffix(identity, "-witness") {
|
||||||
rigName := strings.TrimSuffix(identity, "-witness")
|
rigName := strings.TrimSuffix(identity, "-witness")
|
||||||
return "gt-witness-" + rigName
|
return beads.WitnessBeadID(rigName)
|
||||||
}
|
}
|
||||||
// Pattern: <rig>-refinery → gt-refinery-<rig>
|
// Pattern: <rig>-refinery → gt-<rig>-refinery
|
||||||
if strings.HasSuffix(identity, "-refinery") {
|
if strings.HasSuffix(identity, "-refinery") {
|
||||||
rigName := strings.TrimSuffix(identity, "-refinery")
|
rigName := strings.TrimSuffix(identity, "-refinery")
|
||||||
return "gt-refinery-" + rigName
|
return beads.RefineryBeadID(rigName)
|
||||||
}
|
}
|
||||||
// Pattern: <rig>-crew-<name> → gt-crew-<rig>-<name>
|
// Pattern: <rig>-crew-<name> → gt-<rig>-crew-<name>
|
||||||
if strings.Contains(identity, "-crew-") {
|
if strings.Contains(identity, "-crew-") {
|
||||||
parts := strings.SplitN(identity, "-crew-", 2)
|
parts := strings.SplitN(identity, "-crew-", 2)
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
return "gt-crew-" + parts[0] + "-" + parts[1]
|
return beads.CrewBeadID(parts[0], parts[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Unknown format
|
// Unknown format
|
||||||
@@ -588,16 +589,16 @@ const DeadAgentTimeout = 15 * time.Minute
|
|||||||
func (d *Daemon) checkStaleAgents() {
|
func (d *Daemon) checkStaleAgents() {
|
||||||
// Known agent bead IDs to check
|
// Known agent bead IDs to check
|
||||||
agentBeadIDs := []string{
|
agentBeadIDs := []string{
|
||||||
"gt-deacon",
|
beads.DeaconBeadID(),
|
||||||
"gt-mayor",
|
beads.MayorBeadID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add rig-specific agents (witness, refinery) for known rigs
|
// Add rig-specific agents (witness, refinery) for known rigs
|
||||||
// For now, we check gastown - could be expanded to discover rigs dynamically
|
// For now, we check gastown - could be expanded to discover rigs dynamically
|
||||||
rigs := []string{"gastown", "beads"}
|
rigs := []string{"gastown", "beads"}
|
||||||
for _, rig := range rigs {
|
for _, rig := range rigs {
|
||||||
agentBeadIDs = append(agentBeadIDs, "gt-witness-"+rig)
|
agentBeadIDs = append(agentBeadIDs, beads.WitnessBeadID(rig))
|
||||||
agentBeadIDs = append(agentBeadIDs, "gt-refinery-"+rig)
|
agentBeadIDs = append(agentBeadIDs, beads.RefineryBeadID(rig))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, agentBeadID := range agentBeadIDs {
|
for _, agentBeadID := range agentBeadIDs {
|
||||||
|
|||||||
@@ -76,14 +76,12 @@ 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
|
||||||
var firstPrefix string
|
|
||||||
for prefix, rigName := range prefixToRig {
|
for prefix, rigName := 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 == "" || rigName < firstRigName {
|
||||||
firstRigName = rigName
|
firstRigName = rigName
|
||||||
firstPrefix = prefix
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,9 +99,9 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
|
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
|
||||||
bd := beads.New(rigBeadsPath)
|
bd := beads.New(rigBeadsPath)
|
||||||
|
|
||||||
// Check rig-specific agents
|
// Check rig-specific agents (using canonical naming: prefix-rig-role-name)
|
||||||
witnessID := fmt.Sprintf("%s-witness-%s", prefix, rigName)
|
witnessID := beads.WitnessBeadID(rigName)
|
||||||
refineryID := fmt.Sprintf("%s-refinery-%s", prefix, rigName)
|
refineryID := beads.RefineryBeadID(rigName)
|
||||||
|
|
||||||
if _, err := bd.Show(witnessID); err != nil {
|
if _, err := bd.Show(witnessID); err != nil {
|
||||||
missing = append(missing, witnessID)
|
missing = append(missing, witnessID)
|
||||||
@@ -118,7 +116,7 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
// Check crew worker agents
|
// Check crew worker agents
|
||||||
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
||||||
for _, workerName := range crewWorkers {
|
for _, workerName := range crewWorkers {
|
||||||
crewID := fmt.Sprintf("%s-crew-%s-%s", prefix, rigName, workerName)
|
crewID := beads.CrewBeadID(rigName, workerName)
|
||||||
if _, err := bd.Show(crewID); err != nil {
|
if _, err := bd.Show(crewID); err != nil {
|
||||||
missing = append(missing, crewID)
|
missing = append(missing, crewID)
|
||||||
}
|
}
|
||||||
@@ -127,8 +125,8 @@ func (c *AgentBeadsCheck) Run(ctx *CheckContext) *CheckResult {
|
|||||||
|
|
||||||
// Check global agents in first rig
|
// Check global agents in first rig
|
||||||
if rigName == firstRigName {
|
if rigName == firstRigName {
|
||||||
deaconID := firstPrefix + "-deacon"
|
deaconID := beads.DeaconBeadID()
|
||||||
mayorID := firstPrefix + "-mayor"
|
mayorID := beads.MayorBeadID()
|
||||||
|
|
||||||
if _, err := bd.Show(deaconID); err != nil {
|
if _, err := bd.Show(deaconID); err != nil {
|
||||||
missing = append(missing, deaconID)
|
missing = append(missing, deaconID)
|
||||||
@@ -198,14 +196,12 @@ 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
|
||||||
var firstPrefix string
|
|
||||||
for prefix, rigName := range prefixToRig {
|
for prefix, rigName := range prefixToRig {
|
||||||
if prefix != "gt" {
|
if prefix != "gt" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if firstRigName == "" || rigName < firstRigName {
|
if firstRigName == "" || rigName < firstRigName {
|
||||||
firstRigName = rigName
|
firstRigName = rigName
|
||||||
firstPrefix = prefix
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +215,8 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
|
rigBeadsPath := filepath.Join(ctx.TownRoot, rigName, "mayor", "rig")
|
||||||
bd := beads.New(rigBeadsPath)
|
bd := beads.New(rigBeadsPath)
|
||||||
|
|
||||||
// Create rig-specific agents if missing
|
// Create rig-specific agents if missing (using canonical naming: prefix-rig-role-name)
|
||||||
witnessID := fmt.Sprintf("%s-witness-%s", prefix, rigName)
|
witnessID := beads.WitnessBeadID(rigName)
|
||||||
if _, err := bd.Show(witnessID); err != nil {
|
if _, err := bd.Show(witnessID); err != nil {
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
RoleType: "witness",
|
RoleType: "witness",
|
||||||
@@ -234,7 +230,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refineryID := fmt.Sprintf("%s-refinery-%s", prefix, rigName)
|
refineryID := beads.RefineryBeadID(rigName)
|
||||||
if _, err := bd.Show(refineryID); err != nil {
|
if _, err := bd.Show(refineryID); err != nil {
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
RoleType: "refinery",
|
RoleType: "refinery",
|
||||||
@@ -251,7 +247,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
// Create crew worker agents if missing
|
// Create crew worker agents if missing
|
||||||
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
crewWorkers := listCrewWorkers(ctx.TownRoot, rigName)
|
||||||
for _, workerName := range crewWorkers {
|
for _, workerName := range crewWorkers {
|
||||||
crewID := fmt.Sprintf("%s-crew-%s-%s", prefix, rigName, workerName)
|
crewID := beads.CrewBeadID(rigName, workerName)
|
||||||
if _, err := bd.Show(crewID); err != nil {
|
if _, err := bd.Show(crewID); err != nil {
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
RoleType: "crew",
|
RoleType: "crew",
|
||||||
@@ -268,7 +264,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
|
|
||||||
// Create global agents in first rig if missing
|
// Create global agents in first rig if missing
|
||||||
if rigName == firstRigName {
|
if rigName == firstRigName {
|
||||||
deaconID := firstPrefix + "-deacon"
|
deaconID := beads.DeaconBeadID()
|
||||||
if _, err := bd.Show(deaconID); err != nil {
|
if _, err := bd.Show(deaconID); err != nil {
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
RoleType: "deacon",
|
RoleType: "deacon",
|
||||||
@@ -282,7 +278,7 @@ func (c *AgentBeadsCheck) Fix(ctx *CheckContext) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mayorID := firstPrefix + "-mayor"
|
mayorID := beads.MayorBeadID()
|
||||||
if _, err := bd.Show(mayorID); err != nil {
|
if _, err := bd.Show(mayorID); err != nil {
|
||||||
fields := &beads.AgentFields{
|
fields := &beads.AgentFields{
|
||||||
RoleType: "mayor",
|
RoleType: "mayor",
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ func (m *Manager) assigneeID(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// agentBeadID returns the agent bead ID for a polecat.
|
// agentBeadID returns the agent bead ID for a polecat.
|
||||||
// Format: "gt-polecat-<rig>-<name>" (e.g., "gt-polecat-gastown-Toast")
|
// Format: "gt-<rig>-polecat-<name>" (e.g., "gt-gastown-polecat-Toast")
|
||||||
func (m *Manager) agentBeadID(name string) string {
|
func (m *Manager) agentBeadID(name string) string {
|
||||||
return fmt.Sprintf("gt-polecat-%s-%s", m.rig.Name, name)
|
return beads.PolecatBeadID(m.rig.Name, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCleanupStatusFromBead reads the cleanup_status from the polecat's agent bead.
|
// getCleanupStatusFromBead reads the cleanup_status from the polecat's agent bead.
|
||||||
|
|||||||
@@ -414,16 +414,16 @@ func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig boo
|
|||||||
|
|
||||||
var agents []agentDef
|
var agents []agentDef
|
||||||
|
|
||||||
// Always create rig-specific agents
|
// Always create rig-specific agents (using canonical naming: prefix-rig-role-name)
|
||||||
agents = append(agents,
|
agents = append(agents,
|
||||||
agentDef{
|
agentDef{
|
||||||
id: fmt.Sprintf("%s-witness-%s", prefix, rigName),
|
id: beads.WitnessBeadID(rigName),
|
||||||
roleType: "witness",
|
roleType: "witness",
|
||||||
rig: rigName,
|
rig: rigName,
|
||||||
desc: fmt.Sprintf("Witness for %s - monitors polecat health and progress.", rigName),
|
desc: fmt.Sprintf("Witness for %s - monitors polecat health and progress.", rigName),
|
||||||
},
|
},
|
||||||
agentDef{
|
agentDef{
|
||||||
id: fmt.Sprintf("%s-refinery-%s", prefix, rigName),
|
id: beads.RefineryBeadID(rigName),
|
||||||
roleType: "refinery",
|
roleType: "refinery",
|
||||||
rig: rigName,
|
rig: rigName,
|
||||||
desc: fmt.Sprintf("Refinery for %s - processes merge queue.", rigName),
|
desc: fmt.Sprintf("Refinery for %s - processes merge queue.", rigName),
|
||||||
@@ -434,13 +434,13 @@ func (m *Manager) initAgentBeads(rigPath, rigName, prefix string, isFirstRig boo
|
|||||||
if isFirstRig {
|
if isFirstRig {
|
||||||
agents = append(agents,
|
agents = append(agents,
|
||||||
agentDef{
|
agentDef{
|
||||||
id: prefix + "-deacon",
|
id: beads.DeaconBeadID(),
|
||||||
roleType: "deacon",
|
roleType: "deacon",
|
||||||
rig: "",
|
rig: "",
|
||||||
desc: "Deacon (daemon beacon) - receives mechanical heartbeats, runs town plugins and monitoring.",
|
desc: "Deacon (daemon beacon) - receives mechanical heartbeats, runs town plugins and monitoring.",
|
||||||
},
|
},
|
||||||
agentDef{
|
agentDef{
|
||||||
id: prefix + "-mayor",
|
id: beads.MayorBeadID(),
|
||||||
roleType: "mayor",
|
roleType: "mayor",
|
||||||
rig: "",
|
rig: "",
|
||||||
desc: "Mayor - global coordinator, handles cross-rig communication and escalations.",
|
desc: "Mayor - global coordinator, handles cross-rig communication and escalations.",
|
||||||
|
|||||||
+29
-33
@@ -10,6 +10,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/steveyegge/gastown/internal/beads"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EventSource represents a source of events
|
// EventSource represents a source of events
|
||||||
@@ -170,45 +172,39 @@ func parseSimpleLine(line string) *Event {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseBeadContext extracts actor/rig/role from a bead ID
|
// parseBeadContext extracts actor/rig/role from a bead ID
|
||||||
|
// Uses canonical naming: prefix-rig-role-name
|
||||||
|
// Examples: gt-gastown-crew-joe, gt-gastown-witness, gt-mayor
|
||||||
func parseBeadContext(beadID string) (actor, rig, role string) {
|
func parseBeadContext(beadID string) (actor, rig, role string) {
|
||||||
if beadID == "" {
|
if beadID == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent beads: gt-crew-gastown-joe, gt-witness-gastown, gt-mayor
|
// Use the canonical parser
|
||||||
if strings.HasPrefix(beadID, "gt-crew-") {
|
parsedRig, parsedRole, name, ok := beads.ParseAgentBeadID(beadID)
|
||||||
parts := strings.Split(beadID, "-")
|
if !ok {
|
||||||
if len(parts) >= 4 {
|
return
|
||||||
rig = parts[2]
|
}
|
||||||
actor = strings.Join(parts[2:], "/")
|
|
||||||
role = "crew"
|
rig = parsedRig
|
||||||
|
role = parsedRole
|
||||||
|
|
||||||
|
// Build actor identifier
|
||||||
|
switch parsedRole {
|
||||||
|
case "mayor", "deacon":
|
||||||
|
actor = parsedRole
|
||||||
|
case "witness", "refinery":
|
||||||
|
actor = parsedRole
|
||||||
|
case "crew":
|
||||||
|
if name != "" {
|
||||||
|
actor = parsedRig + "/crew/" + name
|
||||||
|
} else {
|
||||||
|
actor = parsedRole
|
||||||
}
|
}
|
||||||
} else if strings.HasPrefix(beadID, "gt-witness-") {
|
case "polecat":
|
||||||
parts := strings.Split(beadID, "-")
|
if name != "" {
|
||||||
if len(parts) >= 3 {
|
actor = parsedRig + "/" + name
|
||||||
rig = parts[2]
|
} else {
|
||||||
actor = "witness"
|
actor = parsedRole
|
||||||
role = "witness"
|
|
||||||
}
|
|
||||||
} else if strings.HasPrefix(beadID, "gt-refinery-") {
|
|
||||||
parts := strings.Split(beadID, "-")
|
|
||||||
if len(parts) >= 3 {
|
|
||||||
rig = parts[2]
|
|
||||||
actor = "refinery"
|
|
||||||
role = "refinery"
|
|
||||||
}
|
|
||||||
} else if beadID == "gt-mayor" {
|
|
||||||
actor = "mayor"
|
|
||||||
role = "mayor"
|
|
||||||
} else if beadID == "gt-deacon" {
|
|
||||||
actor = "deacon"
|
|
||||||
role = "deacon"
|
|
||||||
} else if strings.HasPrefix(beadID, "gt-polecat-") {
|
|
||||||
parts := strings.Split(beadID, "-")
|
|
||||||
if len(parts) >= 3 {
|
|
||||||
rig = parts[2]
|
|
||||||
actor = strings.Join(parts[2:], "-")
|
|
||||||
role = "polecat"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user