fix(alerts): PENDING 收斂無 TTL → 老記錄永久封鎖 Telegram 告警
Some checks are pending
CD Pipeline / build-and-deploy (push) Has started running

根因:find_by_fingerprint 的 PENDING 匹配條件無時間上限,
2026-04-12 建立的 3 筆 PENDING approval records(hit=77/30/17)
持續吃掉所有同指紋告警,造成 2+ 小時 Telegram 靜音。

修正(approval_db.py):
  - PENDING_TTL_HOURS = 24:PENDING 記錄逾 24h 不再收斂新告警
  - 原本:OR(status=PENDING, created_at>=30min前)
  - 修正:OR(PENDING AND created_at>=24h前, created_at>=30min前)

緊急修復:kubectl exec 直接將 7 筆過期 PENDING 記錄設為 expired,
即時恢復 Telegram 告警流(不等部署)。

Phase 6 AI 自我治理閉環(ADR-087):
  - feat(db): 新增 ai_governance_events 表 + 3 個 index(base.py + models.py)
  - feat(svc): ai_slo_calculator.py — 7d 滾動 SLO(success/override/false_neg)
  - feat(svc): trust_drift_detector.py — Playbook 信任度極端偏態偵測
  - feat(job): kb_rot_cleaner.py — K8s API/Prom metric/老舊 incident_case 腐爛清理
  - feat(svc): decision_manager.py — 自我降級守衛(SLO 違反 → 提高門檻/保守模式)

2026-04-15 ogt + Claude Sonnet 4.6(亞太)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-15 18:56:16 +08:00
parent 37f4553349
commit fab65e7d7a
7 changed files with 1112 additions and 3 deletions

View File

@@ -220,6 +220,20 @@ async def init_db() -> None:
""")
)
# 2026-04-15 ogt + Claude Sonnet 4.6(亞太): Phase 6 自我治理閉環
# ADR-087: ai_governance_events 不可變 Event Sourcing 表
# create_all 已建表,此處補 INDEX部分環境 create 不跑 Index
await conn.execute(
text("""
CREATE INDEX IF NOT EXISTS ix_ai_governance_event_type
ON ai_governance_events (event_type);
CREATE INDEX IF NOT EXISTS ix_ai_governance_triggered_at
ON ai_governance_events (triggered_at);
CREATE INDEX IF NOT EXISTS ix_ai_governance_resolved
ON ai_governance_events (resolved);
""")
)
async def close_db() -> None:
"""

View File

@@ -1116,3 +1116,62 @@ class AgentSession(Base):
# 查詢某 session 中特定 role 的 turnCoordinator 聚合時常用)
Index("ix_agent_sessions_session_role", "session_id", "agent_role"),
)
# =============================================================================
# AiGovernanceEvent — Phase 6 自我治理事件溯源(不可刪除)
# ADR-087: AI 自我治理閉環SLO 違反 / 信任漂移 / KB 腐爛 / 自我降級
# 2026-04-15 ogt + Claude Sonnet 4.6(亞太): Phase 6 初始建立
#
# 核心鐵律:
# - 不可變 Event Sourcing — 只 INSERT禁止 UPDATE/DELETE
# - 所有治理事件必須落地 PGSLO dashboard 依賴此表
# - resolved=True 僅由人工或下次計算時補填,不可自動翻轉未解決項目
# =============================================================================
class AiGovernanceEvent(Base):
"""
AI 自我治理事件記錄(不可變)
event_type 值:
slo_violation — SLO 計算結果違反閾值
trust_drift — Playbook 信任度分布偏態(全高或全低)
kb_stale — KB 條目引用已廢棄 K8s API / Prometheus query
self_demotion — 信心閾值自動調高(自我降級)
conservative_mode — 連續 SLO 違反,全系統切保守模式
replay_degraded — 離線回放一致率連續下降
immutable — 只 INSERT禁 UPDATE / DELETE
"""
__tablename__ = "ai_governance_events"
id: Mapped[str] = mapped_column(
String(36), primary_key=True, default=generate_uuid,
comment="主鍵UUID"
)
event_type: Mapped[str] = mapped_column(
String(40), nullable=False,
comment="slo_violation / trust_drift / kb_stale / self_demotion / conservative_mode / replay_degraded"
)
triggered_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=taipei_now, nullable=False,
comment="事件觸發時間(台北時區)"
)
details: Mapped[dict] = mapped_column(
JSON, nullable=False, default=dict,
comment="事件詳情 JSONBSLO 數值、漂移分布等)"
)
resolved: Mapped[bool] = mapped_column(
default=False, nullable=False,
comment="是否已解決(人工確認或下次計算恢復正常後補填)"
)
resolved_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True,
comment="解決時間(僅人工/系統補填,不得自動反轉未解決項目)"
)
__table_args__ = (
Index("ix_ai_governance_event_type", "event_type"),
Index("ix_ai_governance_triggered_at", "triggered_at"),
Index("ix_ai_governance_resolved", "resolved"),
)