diff --git a/internal/cmd/deacon.go b/internal/cmd/deacon.go index cb839b98..45dfdb7f 100644 --- a/internal/cmd/deacon.go +++ b/internal/cmd/deacon.go @@ -350,6 +350,12 @@ func startDeaconSession(t *tmux.Tmux) error { 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(DeaconSessionName, session.PropulsionNudgeForRole("deacon")) // Non-fatal + return nil } diff --git a/internal/cmd/mayor.go b/internal/cmd/mayor.go index 7d89702e..04f6705e 100644 --- a/internal/cmd/mayor.go +++ b/internal/cmd/mayor.go @@ -153,6 +153,12 @@ func startMayorSession(t *tmux.Tmux) error { Topic: "cold-start", }) // Non-fatal + // GUPP: Gas Town Universal Propulsion Principle + // Send the propulsion nudge to trigger autonomous coordination. + // Wait for beacon to be fully processed (needs to be separate prompt) + time.Sleep(2 * time.Second) + _ = t.NudgeSession(MayorSessionName, session.PropulsionNudgeForRole("mayor")) // Non-fatal + return nil } diff --git a/internal/cmd/start.go b/internal/cmd/start.go index 989bb3c9..bf659249 100644 --- a/internal/cmd/start.go +++ b/internal/cmd/start.go @@ -339,14 +339,32 @@ func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) { theme := tmux.AssignTheme(rigName) _ = t.ConfigureGasTownSession(sessionName, theme, rigName, "refinery", "refinery") - // Launch Claude in a respawn loop + // 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 - runtimeCmd := config.GetRuntimeCommand(r.Path) - loopCmd := `export GT_ROLE=refinery BD_ACTOR=` + bdActor + ` GIT_AUTHOR_NAME=` + bdActor + ` && while true; do echo "🛢️ Starting Refinery for ` + rigName + `..."; ` + runtimeCmd + `; echo ""; echo "Refinery exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done` - if err := t.SendKeysDelayed(sessionName, loopCmd, 200); err != nil { + 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) + + // 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")) // Non-fatal + return true, nil } diff --git a/internal/cmd/witness.go b/internal/cmd/witness.go index 543ea92c..a658fc20 100644 --- a/internal/cmd/witness.go +++ b/internal/cmd/witness.go @@ -352,6 +352,12 @@ func ensureWitnessSession(rigName string, r *rig.Rig) (bool, error) { 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")) // Non-fatal + return true, nil } diff --git a/internal/session/names.go b/internal/session/names.go index 5c8edeba..a3ce5cf2 100644 --- a/internal/session/names.go +++ b/internal/session/names.go @@ -43,3 +43,26 @@ func PolecatSessionName(rig, name string) string { func PropulsionNudge() string { return "Run `gt hook` to check your hook and begin work." } + +// PropulsionNudgeForRole generates a role-specific GUPP nudge. +// Different roles have different startup flows: +// - polecat/crew: Check hook for slung work +// - witness/refinery: Start patrol cycle +// - deacon: Start heartbeat patrol +// - mayor: Check mail for coordination work +func PropulsionNudgeForRole(role string) string { + switch role { + case "polecat", "crew": + return PropulsionNudge() + case "witness": + return "Run `gt prime` to check patrol status and begin work." + case "refinery": + return "Run `gt prime` to check MQ status and begin patrol." + case "deacon": + return "Run `gt prime` to check patrol status and begin heartbeat cycle." + case "mayor": + return "Run `gt prime` to check mail and begin coordination." + default: + return PropulsionNudge() + } +}