Files
gastown/internal/session/town.go
nux 692d6819f2 feat(crash): improve crash logging and mass death detection
Add comprehensive crash logging improvements to help diagnose mass session death events:

- Add TypeSessionDeath and TypeMassDeath event types for feed visibility
- Log pre-death events before killing sessions (who killed, why)
- Add mass death detection in daemon (3+ deaths in 30s triggers alert)
- Add macOS crash report check in gt doctor
- Support session death events in townlog and feed curator

Closes hq-kt1o6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 14:11:09 -08:00

63 lines
1.8 KiB
Go

// Package session provides polecat session lifecycle management.
package session
import (
"fmt"
"time"
"github.com/steveyegge/gastown/internal/boot"
"github.com/steveyegge/gastown/internal/events"
"github.com/steveyegge/gastown/internal/tmux"
)
// TownSession represents a town-level tmux session.
type TownSession struct {
Name string // Display name (e.g., "Mayor")
SessionID string // Tmux session ID (e.g., "hq-mayor")
}
// TownSessions returns the list of town-level sessions in shutdown order.
// Order matters: Boot (Deacon's watchdog) must be stopped before Deacon,
// otherwise Boot will try to restart Deacon.
func TownSessions() []TownSession {
return []TownSession{
{"Mayor", MayorSessionName()},
{"Boot", boot.SessionName},
{"Deacon", DeaconSessionName()},
}
}
// StopTownSession stops a single town-level tmux session.
// If force is true, skips graceful shutdown (Ctrl-C) and kills immediately.
// Returns true if the session was running and stopped, false if not running.
func StopTownSession(t *tmux.Tmux, ts TownSession, force bool) (bool, error) {
running, err := t.HasSession(ts.SessionID)
if err != nil {
return false, err
}
if !running {
return false, nil
}
// Try graceful shutdown first (unless forced)
if !force {
_ = t.SendKeysRaw(ts.SessionID, "C-c")
time.Sleep(100 * time.Millisecond)
}
// Log pre-death event for crash investigation (before killing)
reason := "user shutdown"
if force {
reason = "forced shutdown"
}
_ = events.LogFeed(events.TypeSessionDeath, ts.SessionID,
events.SessionDeathPayload(ts.SessionID, ts.Name, reason, "gt down"))
// Kill the session
if err := t.KillSession(ts.SessionID); err != nil {
return false, fmt.Errorf("killing %s session: %w", ts.Name, err)
}
return true, nil
}