perf: Optimize gt status from 13s to <1s (gt-zdtpw)

Key optimizations:
- Use --no-daemon for bd commands to avoid daemon IPC overhead
- Pre-fetch all agent beads in single query (ListAgentBeads)
- Pre-fetch all hook beads in single query (ShowMultiple)
- Pre-fetch all tmux sessions for O(1) lookup
- Parallel rig processing with goroutines
- Add --fast flag to skip mail lookups

The main bottleneck was bd daemon communication timing out on
stale daemon processes, causing 5+ second delays per rig.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gastown/crew/max
2025-12-31 12:14:09 -08:00
committed by Steve Yegge
parent 6d1d1469b2
commit 22557e6917
2 changed files with 301 additions and 181 deletions

View File

@@ -218,7 +218,10 @@ func New(workDir string) *Beads {
// run executes a bd command and returns stdout.
func (b *Beads) run(args ...string) ([]byte, error) {
cmd := exec.Command("bd", args...)
// Use --no-daemon for faster read operations (avoids daemon IPC overhead)
// The daemon is primarily useful for write coalescing, not reads
fullArgs := append([]string{"--no-daemon"}, args...)
cmd := exec.Command("bd", fullArgs...)
cmd.Dir = b.workDir
var stdout, stderr bytes.Buffer
@@ -389,6 +392,55 @@ func (b *Beads) Show(id string) (*Issue, error) {
return issues[0], nil
}
// ShowMultiple fetches multiple issues by ID in a single bd call.
// Returns a map of ID to Issue. Missing IDs are not included in the map.
func (b *Beads) ShowMultiple(ids []string) (map[string]*Issue, error) {
if len(ids) == 0 {
return make(map[string]*Issue), nil
}
// bd show supports multiple IDs
args := append([]string{"show", "--json"}, ids...)
out, err := b.run(args...)
if err != nil {
// If bd fails, return empty map (some IDs might not exist)
return make(map[string]*Issue), nil
}
var issues []*Issue
if err := json.Unmarshal(out, &issues); err != nil {
return nil, fmt.Errorf("parsing bd show output: %w", err)
}
result := make(map[string]*Issue, len(issues))
for _, issue := range issues {
result[issue.ID] = issue
}
return result, nil
}
// ListAgentBeads returns all agent beads in a single query.
// Returns a map of agent bead ID to Issue.
func (b *Beads) ListAgentBeads() (map[string]*Issue, error) {
out, err := b.run("list", "--type=agent", "--json")
if err != nil {
return nil, err
}
var issues []*Issue
if err := json.Unmarshal(out, &issues); err != nil {
return nil, fmt.Errorf("parsing bd list output: %w", err)
}
result := make(map[string]*Issue, len(issues))
for _, issue := range issues {
result[issue.ID] = issue
}
return result, nil
}
// Blocked returns issues that are blocked by dependencies.
func (b *Beads) Blocked() ([]*Issue, error) {
out, err := b.run("blocked", "--json")

View File

@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/beads"
@@ -21,6 +22,7 @@ import (
)
var statusJSON bool
var statusFast bool
var statusCmd = &cobra.Command{
Use: "status",
@@ -29,12 +31,15 @@ var statusCmd = &cobra.Command{
Short: "Show overall town status",
Long: `Display the current status of the Gas Town workspace.
Shows town name, registered rigs, active polecats, and witness status.`,
Shows town name, registered rigs, active polecats, and witness status.
Use --fast to skip mail lookups for faster execution.`,
RunE: runStatus,
}
func init() {
statusCmd.Flags().BoolVar(&statusJSON, "json", false, "Output as JSON")
statusCmd.Flags().BoolVar(&statusFast, "fast", false, "Skip mail lookups for faster execution")
rootCmd.AddCommand(statusCmd)
}
@@ -144,6 +149,14 @@ func runStatus(cmd *cobra.Command, args []string) error {
// Create tmux instance for runtime checks
t := tmux.NewTmux()
// Pre-fetch all tmux sessions for O(1) lookup
allSessions := make(map[string]bool)
if sessions, err := t.ListSessions(); err == nil {
for _, s := range sessions {
allSessions[s] = true
}
}
// Discover rigs
rigs, err := mgr.DiscoverRigs()
if err != nil {
@@ -154,6 +167,25 @@ func runStatus(cmd *cobra.Command, args []string) error {
gastownBeadsPath := filepath.Join(townRoot, "gastown", "mayor", "rig")
agentBeads := beads.New(gastownBeadsPath)
// Pre-fetch all agent beads in a single query for performance
allAgentBeads, _ := agentBeads.ListAgentBeads()
if allAgentBeads == nil {
allAgentBeads = make(map[string]*beads.Issue)
}
// Pre-fetch all hook beads (referenced in agent beads) in a single query
var allHookIDs []string
for _, issue := range allAgentBeads {
fields := beads.ParseAgentFields(issue.Description)
if fields != nil && fields.HookBead != "" {
allHookIDs = append(allHookIDs, fields.HookBead)
}
}
allHookBeads, _ := agentBeads.ShowMultiple(allHookIDs)
if allHookBeads == nil {
allHookBeads = make(map[string]*beads.Issue)
}
// Create mail router for inbox lookups
mailRouter := mail.NewRouter(townRoot)
@@ -173,57 +205,79 @@ func runStatus(cmd *cobra.Command, args []string) error {
}
}
// Build status
// Build status - parallel fetch global agents and rigs
status := TownStatus{
Name: townConfig.Name,
Location: townRoot,
Overseer: overseerInfo,
Agents: discoverGlobalAgents(t, agentBeads, mailRouter),
Rigs: make([]RigStatus, 0, len(rigs)),
Rigs: make([]RigStatus, len(rigs)),
}
for _, r := range rigs {
rs := RigStatus{
Name: r.Name,
Polecats: r.Polecats,
PolecatCount: len(r.Polecats),
HasWitness: r.HasWitness,
HasRefinery: r.HasRefinery,
}
var wg sync.WaitGroup
// Count crew workers
crewGit := git.NewGit(r.Path)
crewMgr := crew.NewManager(r, crewGit)
if workers, err := crewMgr.List(); err == nil {
for _, w := range workers {
rs.Crews = append(rs.Crews, w.Name)
// Fetch global agents in parallel with rig discovery
wg.Add(1)
go func() {
defer wg.Done()
status.Agents = discoverGlobalAgents(allSessions, allAgentBeads, allHookBeads, mailRouter, statusFast)
}()
// Process all rigs in parallel
rigActiveHooks := make([]int, len(rigs)) // Track hooks per rig for thread safety
for i, r := range rigs {
wg.Add(1)
go func(idx int, r *rig.Rig) {
defer wg.Done()
rs := RigStatus{
Name: r.Name,
Polecats: r.Polecats,
PolecatCount: len(r.Polecats),
HasWitness: r.HasWitness,
HasRefinery: r.HasRefinery,
}
rs.CrewCount = len(workers)
}
// Discover hooks for all agents in this rig
rs.Hooks = discoverRigHooks(r, rs.Crews)
for _, hook := range rs.Hooks {
if hook.HasWork {
status.Summary.ActiveHooks++
// Count crew workers
crewGit := git.NewGit(r.Path)
crewMgr := crew.NewManager(r, crewGit)
if workers, err := crewMgr.List(); err == nil {
for _, w := range workers {
rs.Crews = append(rs.Crews, w.Name)
}
rs.CrewCount = len(workers)
}
}
// Discover runtime state for all agents in this rig
rs.Agents = discoverRigAgents(t, r, rs.Crews, agentBeads, mailRouter)
// Discover hooks for all agents in this rig
rs.Hooks = discoverRigHooks(r, rs.Crews)
activeHooks := 0
for _, hook := range rs.Hooks {
if hook.HasWork {
activeHooks++
}
}
rigActiveHooks[idx] = activeHooks
// Get MQ summary if rig has a refinery
rs.MQ = getMQSummary(r)
// Discover runtime state for all agents in this rig
rs.Agents = discoverRigAgents(allSessions, r, rs.Crews, allAgentBeads, allHookBeads, mailRouter, statusFast)
status.Rigs = append(status.Rigs, rs)
// Get MQ summary if rig has a refinery
rs.MQ = getMQSummary(r)
// Update summary
status.Summary.PolecatCount += len(r.Polecats)
status.Rigs[idx] = rs
}(i, r)
}
wg.Wait()
// Aggregate summary (after parallel work completes)
for i, rs := range status.Rigs {
status.Summary.PolecatCount += rs.PolecatCount
status.Summary.CrewCount += rs.CrewCount
if r.HasWitness {
status.Summary.ActiveHooks += rigActiveHooks[i]
if rs.HasWitness {
status.Summary.WitnessCount++
}
if r.HasRefinery {
if rs.HasRefinery {
status.Summary.RefineryCount++
}
}
@@ -529,58 +583,73 @@ func discoverRigHooks(r *rig.Rig, crews []string) []AgentHookInfo {
}
// discoverGlobalAgents checks runtime state for town-level agents (Mayor, Deacon).
func discoverGlobalAgents(t *tmux.Tmux, agentBeads *beads.Beads, mailRouter *mail.Router) []AgentRuntime {
var agents []AgentRuntime
// Check Mayor
mayorRunning, _ := t.HasSession(MayorSessionName)
mayor := AgentRuntime{
Name: "mayor",
Address: "mayor/",
Session: MayorSessionName,
Role: "coordinator",
Running: mayorRunning,
// Uses parallel fetching for performance. If skipMail is true, mail lookups are skipped.
// allSessions is a preloaded map of tmux sessions for O(1) lookup.
// allAgentBeads is a preloaded map of agent beads for O(1) lookup.
// allHookBeads is a preloaded map of hook beads for O(1) lookup.
func discoverGlobalAgents(allSessions map[string]bool, allAgentBeads map[string]*beads.Issue, allHookBeads map[string]*beads.Issue, mailRouter *mail.Router, skipMail bool) []AgentRuntime {
// Define agents to discover
agentDefs := []struct {
name string
address string
session string
role string
beadID string
}{
{"mayor", "mayor/", MayorSessionName, "coordinator", "gt-mayor"},
{"deacon", "deacon/", DeaconSessionName, "health-check", "gt-deacon"},
}
// Look up agent bead for hook/state
if issue, fields, err := agentBeads.GetAgentBead("gt-mayor"); err == nil && issue != nil {
mayor.HookBead = fields.HookBead
mayor.State = fields.AgentState
if fields.HookBead != "" {
mayor.HasWork = true
// Try to get the title of the pinned bead
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
mayor.WorkTitle = pinnedIssue.Title
agents := make([]AgentRuntime, len(agentDefs))
var wg sync.WaitGroup
for i, def := range agentDefs {
wg.Add(1)
go func(idx int, d struct {
name string
address string
session string
role string
beadID string
}) {
defer wg.Done()
agent := AgentRuntime{
Name: d.name,
Address: d.address,
Session: d.session,
Role: d.role,
}
}
}
// Get mail info
populateMailInfo(&mayor, mailRouter)
agents = append(agents, mayor)
// Check Deacon
deaconRunning, _ := t.HasSession(DeaconSessionName)
deacon := AgentRuntime{
Name: "deacon",
Address: "deacon/",
Session: DeaconSessionName,
Role: "health-check",
Running: deaconRunning,
}
// Look up agent bead for hook/state
if issue, fields, err := agentBeads.GetAgentBead("gt-deacon"); err == nil && issue != nil {
deacon.HookBead = fields.HookBead
deacon.State = fields.AgentState
if fields.HookBead != "" {
deacon.HasWork = true
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
deacon.WorkTitle = pinnedIssue.Title
// Check tmux session from preloaded map (O(1))
agent.Running = allSessions[d.session]
// Look up agent bead from preloaded map (O(1))
if issue, ok := allAgentBeads[d.beadID]; ok {
fields := beads.ParseAgentFields(issue.Description)
if fields != nil {
agent.HookBead = fields.HookBead
agent.State = fields.AgentState
if fields.HookBead != "" {
agent.HasWork = true
// Get hook title from preloaded map
if pinnedIssue, ok := allHookBeads[fields.HookBead]; ok {
agent.WorkTitle = pinnedIssue.Title
}
}
}
}
}
}
// Get mail info
populateMailInfo(&deacon, mailRouter)
agents = append(agents, deacon)
// Get mail info (skip if --fast)
if !skipMail {
populateMailInfo(&agent, mailRouter)
}
agents[idx] = agent
}(i, def)
}
wg.Wait()
return agents
}
@@ -602,118 +671,117 @@ func populateMailInfo(agent *AgentRuntime, router *mail.Router) {
}
}
// agentDef defines an agent to discover
type agentDef struct {
name string
address string
session string
role string
beadID string
}
// discoverRigAgents checks runtime state for all agents in a rig.
func discoverRigAgents(t *tmux.Tmux, r *rig.Rig, crews []string, agentBeads *beads.Beads, mailRouter *mail.Router) []AgentRuntime {
var agents []AgentRuntime
// Uses parallel fetching for performance. If skipMail is true, mail lookups are skipped.
// allSessions is a preloaded map of tmux sessions for O(1) lookup.
// allAgentBeads is a preloaded map of agent beads for O(1) lookup.
// allHookBeads is a preloaded map of hook beads for O(1) lookup.
func discoverRigAgents(allSessions map[string]bool, r *rig.Rig, crews []string, allAgentBeads map[string]*beads.Issue, allHookBeads map[string]*beads.Issue, mailRouter *mail.Router, skipMail bool) []AgentRuntime {
// Build list of all agents to discover
var defs []agentDef
// Check Witness
// Witness
if r.HasWitness {
sessionName := witnessSessionName(r.Name)
running, _ := t.HasSession(sessionName)
witness := AgentRuntime{
Name: "witness",
Address: r.Name + "/witness",
Session: sessionName,
Role: "witness",
Running: running,
}
// Look up agent bead
agentID := beads.WitnessBeadID(r.Name)
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
witness.HookBead = fields.HookBead
witness.State = fields.AgentState
if fields.HookBead != "" {
witness.HasWork = true
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
witness.WorkTitle = pinnedIssue.Title
}
}
}
populateMailInfo(&witness, mailRouter)
agents = append(agents, witness)
defs = append(defs, agentDef{
name: "witness",
address: r.Name + "/witness",
session: witnessSessionName(r.Name),
role: "witness",
beadID: beads.WitnessBeadID(r.Name),
})
}
// Check Refinery
// Refinery
if r.HasRefinery {
sessionName := fmt.Sprintf("gt-%s-refinery", r.Name)
running, _ := t.HasSession(sessionName)
refinery := AgentRuntime{
Name: "refinery",
Address: r.Name + "/refinery",
Session: sessionName,
Role: "refinery",
Running: running,
}
// Look up agent bead
agentID := beads.RefineryBeadID(r.Name)
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
refinery.HookBead = fields.HookBead
refinery.State = fields.AgentState
if fields.HookBead != "" {
refinery.HasWork = true
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
refinery.WorkTitle = pinnedIssue.Title
}
}
}
populateMailInfo(&refinery, mailRouter)
agents = append(agents, refinery)
defs = append(defs, agentDef{
name: "refinery",
address: r.Name + "/refinery",
session: fmt.Sprintf("gt-%s-refinery", r.Name),
role: "refinery",
beadID: beads.RefineryBeadID(r.Name),
})
}
// Check Polecats
// Polecats
for _, name := range r.Polecats {
sessionName := fmt.Sprintf("gt-%s-%s", r.Name, name)
running, _ := t.HasSession(sessionName)
polecat := AgentRuntime{
Name: name,
Address: r.Name + "/" + name,
Session: sessionName,
Role: "polecat",
Running: running,
}
// Look up agent bead
agentID := beads.PolecatBeadID(r.Name, name)
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
polecat.HookBead = fields.HookBead
polecat.State = fields.AgentState
if fields.HookBead != "" {
polecat.HasWork = true
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
polecat.WorkTitle = pinnedIssue.Title
}
}
}
populateMailInfo(&polecat, mailRouter)
agents = append(agents, polecat)
defs = append(defs, agentDef{
name: name,
address: r.Name + "/" + name,
session: fmt.Sprintf("gt-%s-%s", r.Name, name),
role: "polecat",
beadID: beads.PolecatBeadID(r.Name, name),
})
}
// Check Crew
// Crew
for _, name := range crews {
sessionName := crewSessionName(r.Name, name)
running, _ := t.HasSession(sessionName)
crewAgent := AgentRuntime{
Name: name,
Address: r.Name + "/crew/" + name,
Session: sessionName,
Role: "crew",
Running: running,
}
// Look up agent bead
agentID := beads.CrewBeadID(r.Name, name)
if issue, fields, err := agentBeads.GetAgentBead(agentID); err == nil && issue != nil {
crewAgent.HookBead = fields.HookBead
crewAgent.State = fields.AgentState
if fields.HookBead != "" {
crewAgent.HasWork = true
if pinnedIssue, err := agentBeads.Show(fields.HookBead); err == nil {
crewAgent.WorkTitle = pinnedIssue.Title
}
}
}
populateMailInfo(&crewAgent, mailRouter)
agents = append(agents, crewAgent)
defs = append(defs, agentDef{
name: name,
address: r.Name + "/crew/" + name,
session: crewSessionName(r.Name, name),
role: "crew",
beadID: beads.CrewBeadID(r.Name, name),
})
}
if len(defs) == 0 {
return nil
}
// Fetch all agents in parallel
agents := make([]AgentRuntime, len(defs))
var wg sync.WaitGroup
for i, def := range defs {
wg.Add(1)
go func(idx int, d agentDef) {
defer wg.Done()
agent := AgentRuntime{
Name: d.name,
Address: d.address,
Session: d.session,
Role: d.role,
}
// Check tmux session from preloaded map (O(1))
agent.Running = allSessions[d.session]
// Look up agent bead from preloaded map (O(1))
if issue, ok := allAgentBeads[d.beadID]; ok {
fields := beads.ParseAgentFields(issue.Description)
if fields != nil {
agent.HookBead = fields.HookBead
agent.State = fields.AgentState
if fields.HookBead != "" {
agent.HasWork = true
// Get hook title from preloaded map
if pinnedIssue, ok := allHookBeads[fields.HookBead]; ok {
agent.WorkTitle = pinnedIssue.Title
}
}
}
}
// Get mail info (skip if --fast)
if !skipMail {
populateMailInfo(&agent, mailRouter)
}
agents[idx] = agent
}(i, def)
}
wg.Wait()
return agents
}