diff --git a/internal/cmd/start.go b/internal/cmd/start.go index 9d460f42..9851ef0c 100644 --- a/internal/cmd/start.go +++ b/internal/cmd/start.go @@ -19,8 +19,6 @@ import ( "github.com/steveyegge/gastown/internal/polecat" "github.com/steveyegge/gastown/internal/refinery" "github.com/steveyegge/gastown/internal/rig" - "github.com/steveyegge/gastown/internal/runtime" - "github.com/steveyegge/gastown/internal/session" "github.com/steveyegge/gastown/internal/style" "github.com/steveyegge/gastown/internal/tmux" "github.com/steveyegge/gastown/internal/witness" @@ -319,86 +317,6 @@ func discoverAllRigs(townRoot string) ([]*rig.Rig, error) { 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 runtime settings exist (autonomous role needs mail in SessionStart) - runtimeConfig := config.LoadRuntimeConfig(r.Path) - if err := runtime.EnsureSettingsForRole(refineryRigDir, "refinery", runtimeConfig); err != nil { - return false, fmt.Errorf("ensuring runtime 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, "", "")); 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) - _ = runtime.RunStartupFallback(t, sessionName, "refinery", runtimeConfig) - - // 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 { t := tmux.NewTmux() diff --git a/internal/cmd/up.go b/internal/cmd/up.go index 3279b0d0..ae310740 100644 --- a/internal/cmd/up.go +++ b/internal/cmd/up.go @@ -11,7 +11,6 @@ import ( "github.com/spf13/cobra" "github.com/steveyegge/gastown/internal/beads" "github.com/steveyegge/gastown/internal/config" - "github.com/steveyegge/gastown/internal/constants" "github.com/steveyegge/gastown/internal/crew" "github.com/steveyegge/gastown/internal/daemon" "github.com/steveyegge/gastown/internal/deacon" @@ -19,8 +18,6 @@ import ( "github.com/steveyegge/gastown/internal/mayor" "github.com/steveyegge/gastown/internal/polecat" "github.com/steveyegge/gastown/internal/refinery" - "github.com/steveyegge/gastown/internal/runtime" - "github.com/steveyegge/gastown/internal/session" "github.com/steveyegge/gastown/internal/style" "github.com/steveyegge/gastown/internal/tmux" "github.com/steveyegge/gastown/internal/witness" @@ -252,127 +249,6 @@ func ensureDaemon(townRoot string) error { return nil } -// ensureSession starts a Claude session if not running. -func ensureSession(t *tmux.Tmux, sessionName, workDir, role string) error { - running, err := t.HasSession(sessionName) - if err != nil { - return err - } - if running { - return nil - } - - // Create session - if err := t.NewSession(sessionName, workDir); err != nil { - return err - } - - // Set environment (non-fatal: session works without these) - _ = t.SetEnvironment(sessionName, "GT_ROLE", role) - _ = t.SetEnvironment(sessionName, "BD_ACTOR", role) - - // Apply theme based on role (non-fatal: theming failure doesn't affect operation) - switch role { - case "mayor": - theme := tmux.MayorTheme() - _ = t.ConfigureGasTownSession(sessionName, theme, "", "Mayor", "coordinator") - case "deacon": - theme := tmux.DeaconTheme() - _ = t.ConfigureGasTownSession(sessionName, theme, "", "Deacon", "health-check") - } - - // Launch runtime - // Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes - var claudeCmd string - runtimeCmd := config.GetRuntimeCommand("") - runtimeConfig := config.LoadRuntimeConfig("") - if role == "deacon" { - // Deacon uses respawn loop - prefix := "GT_ROLE=deacon BD_ACTOR=deacon GIT_AUTHOR_NAME=deacon" - if runtimeConfig.Session != nil && runtimeConfig.Session.SessionIDEnv != "" { - prefix = prefix + " GT_SESSION_ID_ENV=" + runtimeConfig.Session.SessionIDEnv - } - claudeCmd = `export ` + prefix + ` && while true; do echo "⛪ Starting Deacon session..."; ` + runtimeCmd + `; echo ""; echo "Deacon exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done` - } else { - claudeCmd = config.BuildAgentStartupCommand(role, role, "", "") - } - - if err := t.SendKeysDelayed(sessionName, claudeCmd, 200); err != nil { - return err - } - - // Wait for Claude to start (non-fatal) - // Note: Deacon respawn loop makes beacon tricky - Claude restarts multiple times - // For non-respawn (mayor), inject beacon - if role != "deacon" { - 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 - _ = session.StartupNudge(t, sessionName, session.StartupNudgeConfig{ - Recipient: role, - Sender: "human", - Topic: "cold-start", - }) // Non-fatal - _ = runtime.RunStartupFallback(t, sessionName, role, runtimeConfig) - } - - return nil -} - -// ensureWitness starts a witness session for a rig. -func ensureWitness(t *tmux.Tmux, sessionName, rigPath, rigName string) error { - running, err := t.HasSession(sessionName) - if err != nil { - return err - } - if running { - return nil - } - - // Create session in rig directory - if err := t.NewSession(sessionName, rigPath); err != nil { - return err - } - - // Set environment (non-fatal: session works without these) - bdActor := fmt.Sprintf("%s/witness", rigName) - _ = t.SetEnvironment(sessionName, "GT_ROLE", "witness") - _ = t.SetEnvironment(sessionName, "GT_RIG", rigName) - _ = t.SetEnvironment(sessionName, "BD_ACTOR", bdActor) - - // Apply theme (non-fatal: theming failure doesn't affect operation) - theme := tmux.AssignTheme(rigName) - _ = t.ConfigureGasTownSession(sessionName, theme, "", "Witness", rigName) - - // Launch runtime using runtime config - // Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes - runtimeConfig := config.LoadRuntimeConfig(rigPath) - claudeCmd := config.BuildAgentStartupCommand("witness", bdActor, rigPath, "") - if err := t.SendKeysDelayed(sessionName, claudeCmd, 200); err != nil { - return 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) - _ = runtime.RunStartupFallback(t, sessionName, "witness", runtimeConfig) - - // Inject startup nudge for predecessor discovery via /resume - address := fmt.Sprintf("%s/witness", rigName) - _ = session.StartupNudge(t, sessionName, session.StartupNudgeConfig{ - Recipient: address, - Sender: "deacon", - Topic: "patrol", - }) // Non-fatal - - return nil -} - // discoverRigs finds all rigs in the town. func discoverRigs(townRoot string) []string { var rigs []string diff --git a/internal/cmd/witness.go b/internal/cmd/witness.go index b20f6627..84fdaef8 100644 --- a/internal/cmd/witness.go +++ b/internal/cmd/witness.go @@ -5,15 +5,8 @@ import ( "fmt" "os" "os/exec" - "path/filepath" - "time" "github.com/spf13/cobra" - "github.com/steveyegge/gastown/internal/config" - "github.com/steveyegge/gastown/internal/constants" - "github.com/steveyegge/gastown/internal/rig" - "github.com/steveyegge/gastown/internal/runtime" - "github.com/steveyegge/gastown/internal/session" "github.com/steveyegge/gastown/internal/style" "github.com/steveyegge/gastown/internal/tmux" "github.com/steveyegge/gastown/internal/witness" @@ -269,87 +262,6 @@ func witnessSessionName(rigName string) string { return fmt.Sprintf("gt-%s-witness", rigName) } -// ensureWitnessSession creates a witness tmux session if it doesn't exist. -// Returns true if a new session was created, false if it already existed. -func ensureWitnessSession(rigName string, r *rig.Rig) (bool, error) { - t := tmux.NewTmux() - sessionName := witnessSessionName(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 witness's rig clone (if it exists) or witness dir - // This ensures gt prime detects the Witness role correctly - witnessDir := filepath.Join(r.Path, "witness", "rig") - if _, err := os.Stat(witnessDir); os.IsNotExist(err) { - // Try witness/ without rig subdirectory - witnessDir = filepath.Join(r.Path, "witness") - if _, err := os.Stat(witnessDir); os.IsNotExist(err) { - // Fall back to rig path (shouldn't happen in normal setup) - witnessDir = r.Path - } - } - - // Ensure Claude settings exist (autonomous role needs mail in SessionStart) - runtimeConfig := config.LoadRuntimeConfig(r.Path) - if err := runtime.EnsureSettingsForRole(witnessDir, "witness", runtimeConfig); err != nil { - return false, fmt.Errorf("ensuring runtime settings: %w", err) - } - - // Create new tmux session - if err := t.NewSession(sessionName, witnessDir); err != nil { - return false, fmt.Errorf("creating session: %w", err) - } - - // Set environment - bdActor := fmt.Sprintf("%s/witness", rigName) - t.SetEnvironment(sessionName, "GT_ROLE", "witness") - t.SetEnvironment(sessionName, "GT_RIG", rigName) - t.SetEnvironment(sessionName, "BD_ACTOR", bdActor) - - // Apply Gas Town theming (non-fatal: theming failure doesn't affect operation) - theme := tmux.AssignTheme(rigName) - _ = t.ConfigureGasTownSession(sessionName, theme, rigName, "witness", "witness") - - // Launch Claude directly (no shell respawn loop) - // Restarts are handled by daemon via LIFECYCLE mail or deacon health-scan - // NOTE: No gt prime injection needed - SessionStart hook handles it automatically - // Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes - if err := t.SendKeys(sessionName, config.BuildAgentStartupCommand("witness", bdActor, "", "")); 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) - _ = runtime.RunStartupFallback(t, sessionName, "witness", runtimeConfig) - - // Inject startup nudge for predecessor discovery via /resume - address := fmt.Sprintf("%s/witness", 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("witness", witnessDir)) // Non-fatal - - return true, nil -} - func runWitnessAttach(cmd *cobra.Command, args []string) error { rigName := "" if len(args) > 0 { diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index fab89a20..fd53b9c4 100755 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -547,7 +547,7 @@ func (d *Daemon) triggerPendingSpawns() { d.logger.Printf("Found %d pending spawn(s), attempting to trigger...", len(pending)) -// Trigger pending spawns (uses WaitForRuntimeReady with short timeout) + // Trigger pending spawns (uses WaitForRuntimeReady with short timeout) results, err := polecat.TriggerPendingSpawns(d.config.TownRoot, triggerTimeout) if err != nil { d.logger.Printf("Error triggering spawns: %v", err)