fix: restore agent override support lost in manager refactors
The manager refactors (ea8bef2,72544cc0) conflicted with the agent override feature, causing regressions: Deacon (ea8bef2): - Lost agentOverride parameter - Re-added respawn loop (removed in5f2e16f) - Lost GUPP (startup + propulsion nudges) Crew (72544cc0): - Lost agentOverride wiring to StartOptions - --agent flag had no effect on crew refresh/restart This fix restores agent override support and GUPP while keeping improvements from the manager refactors (zombie detection, etc). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Steve Yegge
parent
00a59dec44
commit
c91ab85457
@@ -236,9 +236,10 @@ func runCrewRefresh(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Use manager's Start() with refresh options
|
||||
err = crewMgr.Start(name, crew.StartOptions{
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "refresh", // Startup nudge topic
|
||||
Interactive: true, // No --dangerously-skip-permissions
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "refresh", // Startup nudge topic
|
||||
Interactive: true, // No --dangerously-skip-permissions
|
||||
AgentOverride: crewAgentOverride,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("starting crew session: %w", err)
|
||||
@@ -346,8 +347,9 @@ func runCrewRestart(cmd *cobra.Command, args []string) error {
|
||||
// Use manager's Start() with restart options
|
||||
// Start() will create workspace if needed (idempotent)
|
||||
err = crewMgr.Start(name, crew.StartOptions{
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "restart", // Startup nudge topic
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "restart", // Startup nudge topic
|
||||
AgentOverride: crewAgentOverride,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error restarting %s: %v\n", arg, err)
|
||||
@@ -425,8 +427,9 @@ func runCrewRestartAll() error {
|
||||
|
||||
// Use manager's Start() with restart options
|
||||
err = crewMgr.Start(agent.AgentName, crew.StartOptions{
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "restart", // Startup nudge topic
|
||||
KillExisting: true, // Kill old session if running
|
||||
Topic: "restart", // Startup nudge topic
|
||||
AgentOverride: crewAgentOverride,
|
||||
})
|
||||
if err != nil {
|
||||
failed++
|
||||
|
||||
@@ -203,7 +203,7 @@ func startCoreAgents(townRoot string, agentOverride string) error {
|
||||
|
||||
// Start Deacon (health monitor)
|
||||
deaconMgr := deacon.NewManager(townRoot)
|
||||
if err := deaconMgr.Start(); err != nil {
|
||||
if err := deaconMgr.Start(agentOverride); err != nil {
|
||||
if err == deacon.ErrAlreadyRunning {
|
||||
fmt.Printf(" %s Deacon already running\n", style.Dim.Render("○"))
|
||||
} else {
|
||||
@@ -694,6 +694,7 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
|
||||
err = crewMgr.Start(name, crew.StartOptions{
|
||||
Account: startCrewAccount,
|
||||
ClaudeConfigDir: claudeConfigDir,
|
||||
AgentOverride: startCrewAgentOverride,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, crew.ErrSessionRunning) {
|
||||
|
||||
@@ -83,7 +83,7 @@ func runUp(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// 2. Deacon (Claude agent)
|
||||
deaconMgr := deacon.NewManager(townRoot)
|
||||
if err := deaconMgr.Start(); err != nil {
|
||||
if err := deaconMgr.Start(""); err != nil {
|
||||
if err == deacon.ErrAlreadyRunning {
|
||||
printStatus("Deacon", true, deaconMgr.SessionName())
|
||||
} else {
|
||||
|
||||
@@ -50,6 +50,9 @@ type StartOptions struct {
|
||||
|
||||
// Interactive removes --dangerously-skip-permissions for interactive/refresh mode.
|
||||
Interactive bool
|
||||
|
||||
// AgentOverride specifies an alternate agent alias (e.g., for testing).
|
||||
AgentOverride string
|
||||
}
|
||||
|
||||
// validateCrewName checks that a crew name is safe and valid.
|
||||
@@ -514,7 +517,11 @@ func (m *Manager) Start(name string, opts StartOptions) error {
|
||||
|
||||
// Start claude with environment exports and beacon as initial prompt
|
||||
// SessionStart hook handles context loading (gt prime --hook)
|
||||
claudeCmd := config.BuildCrewStartupCommand(m.rig.Name, name, m.rig.Path, beacon)
|
||||
claudeCmd, err := config.BuildCrewStartupCommandWithAgentOverride(m.rig.Name, name, m.rig.Path, beacon, opts.AgentOverride)
|
||||
if err != nil {
|
||||
_ = t.KillSession(sessionID)
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
|
||||
// For interactive/refresh mode, remove --dangerously-skip-permissions
|
||||
if opts.Interactive {
|
||||
|
||||
@@ -49,8 +49,9 @@ func (m *Manager) deaconDir() string {
|
||||
}
|
||||
|
||||
// Start starts the deacon session.
|
||||
// The deacon runs in a respawn loop for automatic recovery.
|
||||
func (m *Manager) Start() error {
|
||||
// agentOverride allows specifying an alternate agent alias (e.g., for testing).
|
||||
// Restarts are handled by daemon via ensureDeaconRunning on each heartbeat.
|
||||
func (m *Manager) Start(agentOverride string) error {
|
||||
t := tmux.NewTmux()
|
||||
sessionID := m.SessionName()
|
||||
|
||||
@@ -91,26 +92,25 @@ func (m *Manager) Start() error {
|
||||
theme := tmux.DeaconTheme()
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, "", "Deacon", "health-check")
|
||||
|
||||
// Launch Claude in a respawn loop for automatic recovery
|
||||
// The respawn loop ensures the deacon restarts if Claude crashes
|
||||
runtimeCmd := config.GetRuntimeCommand("")
|
||||
respawnCmd := fmt.Sprintf(
|
||||
`export GT_ROLE=deacon BD_ACTOR=deacon GIT_AUTHOR_NAME=deacon && while true; do echo "⛪ Starting Deacon session..."; %s; echo ""; echo "Deacon exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`,
|
||||
runtimeCmd,
|
||||
)
|
||||
// Launch Claude directly (no shell respawn loop)
|
||||
// Restarts are handled by daemon via ensureDeaconRunning on each heartbeat
|
||||
startupCmd, err := config.BuildAgentStartupCommandWithAgentOverride("deacon", "deacon", "", "", agentOverride)
|
||||
if err != nil {
|
||||
_ = t.KillSession(sessionID)
|
||||
return fmt.Errorf("building startup command: %w", err)
|
||||
}
|
||||
|
||||
// Wait for shell to be ready before sending keys (prevents "can't find pane" under load)
|
||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||
_ = t.KillSession(sessionID)
|
||||
return fmt.Errorf("waiting for shell: %w", err)
|
||||
}
|
||||
if err := t.SendKeysDelayed(sessionID, respawnCmd, 200); err != nil {
|
||||
if err := t.SendKeysDelayed(sessionID, startupCmd, 200); err != nil {
|
||||
_ = t.KillSession(sessionID) // best-effort cleanup
|
||||
return fmt.Errorf("starting Claude agent: %w", err)
|
||||
}
|
||||
|
||||
// Wait for Claude to start (non-fatal)
|
||||
// Note: Deacon respawn loop makes this tricky - Claude restarts multiple times
|
||||
if err := t.WaitForCommand(sessionID, constants.SupportedShells, constants.ClaudeStartTimeout); err != nil {
|
||||
// Non-fatal - try to continue anyway
|
||||
}
|
||||
@@ -120,8 +120,18 @@ func (m *Manager) Start() error {
|
||||
|
||||
time.Sleep(constants.ShutdownNotifyDelay)
|
||||
|
||||
// Note: Deacon doesn't get startup nudge due to respawn loop complexity
|
||||
// The deacon uses its own patrol pattern defined in its CLAUDE.md/prime
|
||||
// Inject startup nudge for predecessor discovery via /resume
|
||||
_ = session.StartupNudge(t, sessionID, session.StartupNudgeConfig{
|
||||
Recipient: "deacon",
|
||||
Sender: "daemon",
|
||||
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(sessionID, session.PropulsionNudgeForRole("deacon", deaconDir)) // Non-fatal
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user