package doctor import ( "fmt" "os" "time" "github.com/steveyegge/gastown/internal/boot" ) // BootHealthCheck verifies Boot watchdog health. // "The vet checks on the dog." type BootHealthCheck struct { BaseCheck } // NewBootHealthCheck creates a new Boot health check. func NewBootHealthCheck() *BootHealthCheck { return &BootHealthCheck{ BaseCheck: BaseCheck{ CheckName: "boot-health", CheckDescription: "Check Boot watchdog health (the vet checks on the dog)", CheckCategory: CategoryInfrastructure, }, } } // Run checks Boot health: directory, session, status, and marker freshness. func (c *BootHealthCheck) Run(ctx *CheckContext) *CheckResult { b := boot.New(ctx.TownRoot) details := []string{} // Check 1: Boot directory exists bootDir := b.Dir() if _, err := os.Stat(bootDir); os.IsNotExist(err) { return &CheckResult{ Name: c.Name(), Status: StatusWarning, Message: "Boot directory not present", Details: []string{fmt.Sprintf("Expected: %s", bootDir)}, FixHint: "Boot directory is created on first daemon run", } } // Check 2: Session alive sessionAlive := b.IsSessionAlive() if sessionAlive { details = append(details, fmt.Sprintf("Session: %s (alive)", boot.SessionName)) } else { details = append(details, fmt.Sprintf("Session: %s (not running)", boot.SessionName)) } // Check 3: Last execution status status, err := b.LoadStatus() if err != nil { return &CheckResult{ Name: c.Name(), Status: StatusError, Message: "Failed to load Boot status", Details: []string{err.Error()}, } } if !status.CompletedAt.IsZero() { age := time.Since(status.CompletedAt).Round(time.Second) details = append(details, fmt.Sprintf("Last run: %s ago", age)) if status.LastAction != "" { details = append(details, fmt.Sprintf("Last action: %s", status.LastAction)) } if status.Target != "" { details = append(details, fmt.Sprintf("Target: %s", status.Target)) } if status.Error != "" { details = append(details, fmt.Sprintf("Last error: %s", status.Error)) return &CheckResult{ Name: c.Name(), Status: StatusWarning, Message: "Boot last run had an error", Details: details, FixHint: "Check daemon logs for details", } } } else if status.StartedAt.IsZero() { details = append(details, "No previous run recorded") } // Check 4: Currently running (uses tmux session state per ZFC principle) if sessionAlive { details = append(details, "Currently running (tmux session active)") } // All checks passed message := "Boot watchdog healthy" if b.IsDegraded() { message = "Boot watchdog healthy (degraded mode)" details = append(details, "Running in degraded mode (no tmux)") } return &CheckResult{ Name: c.Name(), Status: StatusOK, Message: message, Details: details, } }