refactor: replace ensureRefinerySession with refinery.Manager.Start()

Replaces inline ensureRefinerySession function with refinery.NewManager(r).Start(false) in gt start --all. Gains zombie detection, proper state tracking, and WaitForShellReady fix.

CI failures (lint in beads.go, integration tests) are pre-existing issues unrelated to this PR's changes.

Co-Authored-By: julianknutsen <julianknutsen@users.noreply.github.com>
This commit is contained in:
Julian Knutsen
2026-01-07 21:03:52 -08:00
committed by GitHub
parent f30178265c
commit 2de2d6b7e4

View File

@@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/claude"
"github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/config"
"github.com/steveyegge/gastown/internal/constants" "github.com/steveyegge/gastown/internal/constants"
"github.com/steveyegge/gastown/internal/crew" "github.com/steveyegge/gastown/internal/crew"
@@ -18,8 +17,8 @@ import (
"github.com/steveyegge/gastown/internal/git" "github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/mayor" "github.com/steveyegge/gastown/internal/mayor"
"github.com/steveyegge/gastown/internal/polecat" "github.com/steveyegge/gastown/internal/polecat"
"github.com/steveyegge/gastown/internal/refinery"
"github.com/steveyegge/gastown/internal/rig" "github.com/steveyegge/gastown/internal/rig"
"github.com/steveyegge/gastown/internal/session"
"github.com/steveyegge/gastown/internal/style" "github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tmux" "github.com/steveyegge/gastown/internal/tmux"
"github.com/steveyegge/gastown/internal/witness" "github.com/steveyegge/gastown/internal/witness"
@@ -246,17 +245,15 @@ func startRigAgents(t *tmux.Tmux, townRoot string) {
} }
// Start Refinery // Start Refinery
refinerySession := fmt.Sprintf("gt-%s-refinery", r.Name) refineryMgr := refinery.NewManager(r)
refineryRunning, _ := t.HasSession(refinerySession) if err := refineryMgr.Start(false); err != nil {
if refineryRunning { if errors.Is(err, refinery.ErrAlreadyRunning) {
fmt.Printf(" %s %s refinery already running\n", style.Dim.Render("○"), r.Name) fmt.Printf(" %s %s refinery already running\n", style.Dim.Render("○"), r.Name)
} else { } else {
created, err := ensureRefinerySession(r.Name, r)
if err != nil {
fmt.Printf(" %s %s refinery failed: %v\n", style.Dim.Render("○"), r.Name, err) fmt.Printf(" %s %s refinery failed: %v\n", style.Dim.Render("○"), r.Name, err)
} else if created {
fmt.Printf(" %s %s refinery started\n", style.Bold.Render("✓"), r.Name)
} }
} else {
fmt.Printf(" %s %s refinery started\n", style.Bold.Render("✓"), r.Name)
} }
} }
} }
@@ -320,86 +317,6 @@ func discoverAllRigs(townRoot string) ([]*rig.Rig, error) {
return rigMgr.DiscoverRigs() return rigMgr.DiscoverRigs()
} }
// ensureRefinerySession creates a refinery tmux session if it doesn't exist.
// Returns true if a new session was created, false if it already existed.
func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) {
t := tmux.NewTmux()
sessionName := fmt.Sprintf("gt-%s-refinery", rigName)
// Check if session already exists
running, err := t.HasSession(sessionName)
if err != nil {
return false, fmt.Errorf("checking session: %w", err)
}
if running {
return false, nil
}
// Working directory is the refinery's rig clone
refineryRigDir := filepath.Join(r.Path, "refinery", "rig")
if _, err := os.Stat(refineryRigDir); os.IsNotExist(err) {
// Fall back to rig path if refinery/rig doesn't exist
refineryRigDir = r.Path
}
// Ensure Claude settings exist in refinery/ (not refinery/rig/) so we don't
// write into the source repo. Claude walks up the tree to find settings.
refineryParentDir := filepath.Join(r.Path, "refinery")
if err := claude.EnsureSettingsForRole(refineryParentDir, "refinery"); err != nil {
return false, fmt.Errorf("ensuring Claude settings: %w", err)
}
// Create new tmux session
if err := t.NewSession(sessionName, refineryRigDir); err != nil {
return false, fmt.Errorf("creating session: %w", err)
}
// Set environment
bdActor := fmt.Sprintf("%s/refinery", rigName)
_ = t.SetEnvironment(sessionName, "GT_ROLE", "refinery")
_ = t.SetEnvironment(sessionName, "GT_RIG", rigName)
_ = t.SetEnvironment(sessionName, "BD_ACTOR", bdActor)
// Set beads environment
beadsDir := filepath.Join(r.Path, "mayor", "rig", ".beads")
_ = t.SetEnvironment(sessionName, "BEADS_DIR", beadsDir)
_ = t.SetEnvironment(sessionName, "BEADS_NO_DAEMON", "1")
_ = t.SetEnvironment(sessionName, "BEADS_AGENT_NAME", fmt.Sprintf("%s/refinery", rigName))
// Apply Gas Town theming (non-fatal: theming failure doesn't affect operation)
theme := tmux.AssignTheme(rigName)
_ = t.ConfigureGasTownSession(sessionName, theme, rigName, "refinery", "refinery")
// Launch Claude directly (no respawn loop - daemon handles restart)
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
if err := t.SendKeys(sessionName, config.BuildAgentStartupCommand("refinery", bdActor, r.Path, "")); err != nil {
return false, fmt.Errorf("sending command: %w", err)
}
// Wait for Claude to start (non-fatal)
if err := t.WaitForCommand(sessionName, constants.SupportedShells, constants.ClaudeStartTimeout); err != nil {
// Non-fatal
}
time.Sleep(constants.ShutdownNotifyDelay)
// Inject startup nudge for predecessor discovery via /resume
address := fmt.Sprintf("%s/refinery", rigName)
_ = session.StartupNudge(t, sessionName, session.StartupNudgeConfig{
Recipient: address,
Sender: "deacon",
Topic: "patrol",
}) // Non-fatal
// GUPP: Gas Town Universal Propulsion Principle
// Send the propulsion nudge to trigger autonomous patrol execution.
// Wait for beacon to be fully processed (needs to be separate prompt)
time.Sleep(2 * time.Second)
_ = t.NudgeSession(sessionName, session.PropulsionNudgeForRole("refinery", refineryRigDir)) // Non-fatal
return true, nil
}
func runShutdown(cmd *cobra.Command, args []string) error { func runShutdown(cmd *cobra.Command, args []string) error {
t := tmux.NewTmux() t := tmux.NewTmux()