From e86998ec582cd658f7537b1d991456612cfcbed0 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 24 Dec 2025 15:11:46 +0000 Subject: [PATCH] fix: AI Patrol only runs when AI is enabled. Related to #885 Users who haven't enabled AI were seeing AI patrol findings from heuristic analysis that they couldn't dismiss (license-gated). - IsPatrolEnabled() now checks if Enabled is true - IsAlertTriggeredAnalysisEnabled() also checks Enabled - Updated tests to reflect new behavior AI patrol and alert-triggered analysis require AI to be enabled as a master switch. This prevents confusing UX where users see AI features without having configured them. --- internal/config/ai.go | 11 ++++++++++- internal/config/ai_config_test.go | 30 +++++++++++++++++++++--------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/internal/config/ai.go b/internal/config/ai.go index b1ac165c6..7fa35a8a9 100644 --- a/internal/config/ai.go +++ b/internal/config/ai.go @@ -445,8 +445,13 @@ func PresetToMinutes(preset string) int { } // IsPatrolEnabled returns true if patrol should run -// Note: Patrol uses local heuristics and doesn't require an AI API key +// Note: Patrol uses local heuristics and doesn't require an AI API key, +// but still requires AI to be enabled as a master switch func (c *AIConfig) IsPatrolEnabled() bool { + // If AI is disabled globally, patrol is disabled + if !c.Enabled { + return false + } // If preset is "disabled", patrol is disabled if c.PatrolSchedulePreset == "disabled" { return false @@ -456,6 +461,10 @@ func (c *AIConfig) IsPatrolEnabled() bool { // IsAlertTriggeredAnalysisEnabled returns true if AI should analyze resources when alerts fire func (c *AIConfig) IsAlertTriggeredAnalysisEnabled() bool { + // Requires AI to be enabled as a master switch + if !c.Enabled { + return false + } return c.AlertTriggeredAnalysis } diff --git a/internal/config/ai_config_test.go b/internal/config/ai_config_test.go index 5fc5f6625..8fa4a0bd6 100644 --- a/internal/config/ai_config_test.go +++ b/internal/config/ai_config_test.go @@ -606,18 +606,23 @@ func TestAIConfig_IsPatrolEnabled(t *testing.T) { expected bool }{ { - name: "patrol disabled by preset", - config: AIConfig{PatrolEnabled: true, PatrolSchedulePreset: "disabled"}, + name: "patrol disabled when AI disabled", + config: AIConfig{Enabled: false, PatrolEnabled: true}, expected: false, }, { - name: "patrol enabled", - config: AIConfig{PatrolEnabled: true}, + name: "patrol disabled by preset", + config: AIConfig{Enabled: true, PatrolEnabled: true, PatrolSchedulePreset: "disabled"}, + expected: false, + }, + { + name: "patrol enabled when AI enabled", + config: AIConfig{Enabled: true, PatrolEnabled: true}, expected: true, }, { name: "patrol disabled by flag", - config: AIConfig{PatrolEnabled: false}, + config: AIConfig{Enabled: true, PatrolEnabled: false}, expected: false, }, } @@ -633,15 +638,22 @@ func TestAIConfig_IsPatrolEnabled(t *testing.T) { } func TestAIConfig_IsAlertTriggeredAnalysisEnabled(t *testing.T) { - t.Run("enabled", func(t *testing.T) { - config := AIConfig{AlertTriggeredAnalysis: true} + t.Run("enabled when AI enabled", func(t *testing.T) { + config := AIConfig{Enabled: true, AlertTriggeredAnalysis: true} if !config.IsAlertTriggeredAnalysisEnabled() { t.Error("expected true") } }) - t.Run("disabled", func(t *testing.T) { - config := AIConfig{AlertTriggeredAnalysis: false} + t.Run("disabled when AI disabled", func(t *testing.T) { + config := AIConfig{Enabled: false, AlertTriggeredAnalysis: true} + if config.IsAlertTriggeredAnalysisEnabled() { + t.Error("expected false when AI is disabled") + } + }) + + t.Run("disabled by flag", func(t *testing.T) { + config := AIConfig{Enabled: true, AlertTriggeredAnalysis: false} if config.IsAlertTriggeredAnalysisEnabled() { t.Error("expected false") }