feat(CORE-001D3B): surface alert history degraded state

This commit is contained in:
Bu5hm4nn
2026-03-26 15:12:04 +01:00
parent 09e03f96a8
commit ff76e326b1
8 changed files with 138 additions and 15 deletions

View File

@@ -2,15 +2,18 @@
from __future__ import annotations
import logging
from dataclasses import dataclass
from decimal import Decimal
from typing import Mapping
from app.domain.portfolio_math import build_alert_context
from app.models.alerts import AlertEvent, AlertHistoryRepository, AlertStatus
from app.models.alerts import AlertEvent, AlertHistoryLoadError, AlertHistoryRepository, AlertStatus
from app.models.portfolio import PortfolioConfig
from app.services.boundary_values import boundary_decimal
logger = logging.getLogger(__name__)
@dataclass(frozen=True, slots=True)
class AlertEvaluationInput:
@@ -74,7 +77,18 @@ class AlertService:
def evaluate(
self, config: PortfolioConfig, portfolio: Mapping[str, object], *, persist: bool = True
) -> AlertStatus:
history = self.repository.load() if persist else []
history: list[AlertEvent] = []
history_unavailable = False
history_notice: str | None = None
try:
history = self.repository.load()
except AlertHistoryLoadError as exc:
history_unavailable = True
history_notice = (
"Alert history is temporarily unavailable due to a storage error. New alerts are not being recorded."
)
logger.warning("Alert history unavailable at %s: %s", exc.history_path, exc)
evaluation = _normalize_alert_evaluation_input(config, portfolio)
if evaluation.ltv_ratio >= evaluation.critical_threshold:
@@ -106,12 +120,21 @@ class AlertService:
email_alerts_enabled=evaluation.email_alerts_enabled,
)
if persist:
if self._should_record(history, event):
if not history_unavailable and self._should_record(history, event):
history.append(event)
self.repository.save(history)
else:
preview_history = [event]
if not persist:
resolved_history = preview_history
elif history_unavailable:
resolved_history = []
elif severity != "ok":
resolved_history = list(reversed(self.repository.load()))
else:
resolved_history = history
return AlertStatus(
severity=severity,
message=message,
@@ -119,11 +142,9 @@ class AlertService:
warning_threshold=float(evaluation.warning_threshold),
critical_threshold=float(evaluation.critical_threshold),
email_alerts_enabled=evaluation.email_alerts_enabled,
history=(
preview_history
if not persist
else list(reversed(self.repository.load() if severity != "ok" else history))
),
history=resolved_history,
history_unavailable=history_unavailable,
history_notice=history_notice,
)
@staticmethod