ruleEnabled($project, 'new_issue')) { return; } $recipients = $this->getRecipients($project); Notification::send($recipients, new NewIssueNotification($project, $issue)); } public function onRegression(Project $project, Issue $issue): void { if (! $this->ruleEnabled($project, 'regression')) { return; } $recipients = $this->getRecipients($project); Notification::send($recipients, new IssueRegressionNotification($project, $issue)); } public function checkSpike(Project $project, Issue $issue): void { $rule = $project->alertRules() ->where('type', 'spike') ->where('enabled', true) ->first(); if (! $rule) { return; } $threshold = $rule->getSpikeThreshold(); $windowMinutes = $rule->getSpikeWindowMinutes(); $recentCount = Event::where('issue_id', $issue->id) ->where('created_at', '>=', Carbon::now()->subMinutes($windowMinutes)) ->count(); if ($recentCount < $threshold) { return; } // Only send once per spike window — check if we already notified $alreadyNotified = $project->users() ->first() ?->notifications() ->where('type', EventSpikeNotification::class) ->where('created_at', '>=', Carbon::now()->subMinutes($windowMinutes)) ->whereJsonContains('data->issue_id', $issue->id) ->exists(); if ($alreadyNotified) { return; } $recipients = $this->getRecipients($project); Notification::send( $recipients, new EventSpikeNotification($project, $issue, $recentCount, $windowMinutes), ); } private function ruleEnabled(Project $project, string $type): bool { $rule = $project->alertRules()->where('type', $type)->first(); // Default: enabled if no rule exists (opt-out model) return $rule === null || $rule->enabled; } private function getRecipients(Project $project) { return $project->users() ->get() ->filter(function ($user) use ($project) { $pref = $user->notificationPreferenceFor($project); return ! $pref->muted; }); } }