Commit Graph

26 Commits

Author SHA1 Message Date
OG T
8de807c40d feat(GAP-D5 Task 4.2): Postmortem 自動組裝 hook
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
incident_service.resolve_incident() 結尾 fire-and-forget 呼叫
report_generation_service.trigger_postmortem(),補完孤兒服務的觸發路徑。

觸發條件(由 trigger_postmortem 內部判斷):
- duration > POSTMORTEM_MIN_DURATION_MINUTES (10min)
- 含 AI root_cause / resolution_action / provider / auto_repaired

背景:
- report_generation_service.py 539 行服務於先前 session 建立
- main.py:322 已啟動 run_daily_report_loop(Task 4.1 )
- trigger_postmortem 在 src/ 下無呼叫方 → 本 commit 補上

MASTER 藍圖 Phase 4 至此完整收官:
 Task 4.1 日度巡檢報告(08:00 台北排程,生產環境已跑)
 Task 4.2 Postmortem 自動組裝(本 commit 接上 resolve hook)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-14 18:25:15 +08:00
OG T
1a4b52ed28 fix(alert): fingerprint 加 alertname 防跨告警指紋衝突 + 補入缺漏心跳分類
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
問題根因:
1. generate_fingerprint 用 alert_type(大量 alertname 落入 "custom")
   → 不同告警名稱同目標共用指紋 → 30 分鐘 debounce 互相擋截
2. classify_alert_early 漏掉 DeadMansSwitch / NoAlertsReceived /
   PrometheusNotConnectedToAlertmanager → 落入 TYPE-3 一般告警

修復:
- alert_analyzer_service.py: 指紋改為 namespace:deployment:alertname:target_resource
  alertname 取自 labels(Alertmanager),fallback 到 alert_type(其他來源)
- incident_service.py: DeadMansSwitch → backup/TYPE-1;
  NoAlertsReceived + PrometheusNotConnectedToAlertmanager → alertchain_health/TYPE-8M
- 補 2 個測試,全套 627 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 22:50:20 +08:00
OG T
0a4b7e9609 fix(classify): HostBackupFailed 精確補入 backup/TYPE-1(測試通過)
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
前次修法用 'backup' in alertname_lower 太寬,導致 BackupJobFailed warning
被分到 TYPE-1,破壞 test_backup_keyword_warning_not_type1。

改為精確白名單:
  _BACKUP_TYPE1_NAMES = {HostBackupFailed, HostBackupStale, HostBackupMissing,
                         BackupRestoreTestFailed, BackupRestoreTestStale}
  + alertname.startswith('HostBackup') 兜底

結果:664 passed, 0 failed

2026-04-12 ogt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 20:03:46 +08:00
OG T
f25d82a88a fix(adr075): 修補斷點E — _push_to_telegram_background 補 TYPE-8M routing
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
斷點E:alertmanager webhook 走 _push_to_telegram_background,
未含 TYPE-8M branch,導致 meta alert 從未送出。

- webhooks.py: 新增 alert_category 參數 + TYPE-8M branch
- incident_service.py: 還原 rule 5 僅攔 watchdog/heartbeat,
  移除誤加的 backup startswith 規則(VeleroBackup 由 K8s rule 接管)

Tests: 52/52 passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 20:01:51 +08:00
OG T
1f7975170a fix(classify): HostBackupFailed 補入 backup/TYPE-1 規則
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 1m51s
classify_alert_early() 的 backup 規則只攔 watchdog/Heartbeat,
HostBackupFailed 先被 Host prefix 規則攔走 → host_resource/TYPE-3 → 跑 LLM → 審批卡。

修法:在 Host prefix 前新增 backup 關鍵字/前綴攔截:
  - HostBackup* / Backup* / VeleroBackup* / BackupRestore*
  - alertname 含 "backup"(大小寫不敏感)

影響:所有備份相關告警直接走 TYPE-1 info 通知,不進 LLM。
HostHighCpu / HostDown 等非備份的 Host 告警不受影響。

2026-04-12 ogt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 19:52:05 +08:00
OG T
24c1b5677b feat(adr075): Step1-3 classify補丁+新按鈕+TYPE-5S/6B/7E格式函數
Step-1 incident_service.py classify_alert_early():
  - 新增 secops (TYPE-5S): UnauthorizedSSH/KubeAudit/CVE/WAFAttack/PodAbnormal
  - 新增 business (TYPE-6B): AITokenCost/GeminiAPIError/SLOBurn/MomoScraper
  - 新增 flywheel_health MCPProvider/OllamaDown/NemotronDown 前綴
  - ssl_cert: 依 days_remaining 決定 TYPE-1(≥14d) vs TYPE-3(<14d)

Step-2 telegram_gateway.py _build_inline_keyboard():
  - 新增 secops: [隔離] [封鎖IP] [驅逐] [確認授權]
  - 新增 business: [暫停1h] [查SignOz] [忽略]
  - 新增 flywheel_health: [觸發診斷] [飛輪面板] [靜默]

Step-3 telegram_gateway.py 新增格式化函數 (Tier 2):
  - send_secops_card() — TYPE-5S 防禦按鈕+nonce
  - send_business_alert() — TYPE-6B 業務損失速率
  - send_escalation_card() — TYPE-7E P0/P1 升級,發 DM+群組

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:50:37 +08:00
OG T
2cef2098d3 feat(adr-075): 修復 Telegram 動態按鈕 4 個斷點 + 新增 7 種告警分類
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
斷點 A: decision_manager 提取 alert_category/notification_type 傳入 send_approval_card
斷點 B: send_approval_card 新增參數並傳遞至 _build_inline_keyboard
斷點 C: 互動型通知 (TYPE-3/4/4D/8M) 禁止發 SRE 群組,防 nonce 洩漏
斷點 D: _CATEGORY_BUTTONS k8s_workload → kubernetes + 新增 6 類按鈕組

classify_alert_early 新增: alertchain_health, flywheel_health, storage,
devops_tool, external_site, ssl_cert, host_resource (從 infrastructure 分離)
Test: 52 classify + 664 total passed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 18:35:56 +08:00
OG T
f0e14136ca fix(flywheel): 修補飛輪四個核心斷點,讓完整流程真正串接起來
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
1. incident_service.py: save_to_episodic_memory() 補寫 alertname/notification_type/alert_category
   → 之前這3欄在DB永遠NULL,LLM無alertname,Playbook匹配全失敗

2. telegram_gateway.py: Telegram批准後呼叫 execute_approved_action()
   → 之前sign_approval()只改DB狀態,380筆批准0筆真正執行kubectl指令

3. approval_execution.py: 執行成功後呼叫 resolve_incident()
   webhooks.py: auto-repair成功後呼叫 resolve_incident()
   → 之前Incident永遠停在INVESTIGATING,KM轉換永遠不觸發,Playbook=0

4. webhooks.py: TYPE-1告警短路,不進LLM
   → 之前Heartbeat/Backup/Info仍燒LLM token,產生垃圾修復建議

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 17:01:10 +08:00
OG T
a28625f088 fix(cr): 首席架構師 CR P0/P1/P2 全修補
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
P0-1: incident_service.py — 刪除 classify_alert_early 死碼 L131-132
P0-2: cron_backup_restore_test.sh — date +%s%3N→+%s,修正毫秒時間戳
P1-2: gitea_webhook.py — fingerprint 移除 sha_short,收斂同 branch 失敗
heartbeat: 還原原始空格對齊格式(統帥要求原本怎樣就怎樣)

P1-1(積木化)/P1-3(TYPE-4)/P2-1(timeZone)/P2-2(IP)/P2-3(WS重連) 待後續處理

2026-04-12 ogt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 16:10:46 +08:00
OG T
d72c7d5ac4 fix(P0): classify_alert_early 參數名稱修正 _labels→labels
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
webhooks.py 呼叫傳 labels= 但函數定義用 _labels,導致所有
Alertmanager webhook 500,告警鏈路完全中斷。

2026-04-12 ogt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 16:02:25 +08:00
OG T
1074936e54 fix(classify): backup/heartbeat severity=warning/critical 告警恢復告警卡片格式
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 2m38s
根因:classify_alert_early() backup 規則無 severity 條件,導致
VeleroBackupFailed / HostBackupFailed (warning/critical) 被分為 TYPE-1
(純資訊無按鈕),告警卡片格式遺失。

修復:
- backup/heartbeat 關鍵字只在 severity=info/none 才命中 TYPE-1
- severity=warning/critical 的 backup 告警走正確 prefix 規則
  (Velero→kubernetes TYPE-3, HostBackup→infrastructure TYPE-3)
- Watchdog (severity=none) 由 severity 規則先命中,維持 TYPE-1
- 補強測試:25 cases,含 VeleroBackupFailed critical → kubernetes TYPE-3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 15:24:00 +08:00
OG T
c09521a1c6 fix(cr): Code Review P0/P1 全修補 — 積木化+SSH路由+安全守衛順序
Some checks failed
CD Pipeline / build-and-deploy (push) Failing after 2m30s
P0-1: classify_alert_early 移至 incident_service (Service層),webhooks.py import 修正
P0-2: _ssh_execute() 改用 self._ssh,移除冗餘 SSHProvider() 實例化
P1-1: infrastructure SSH routing 移至 kubectl safety guard 之前,docker指令不再被攔截
P1-2: alert_rule_engine 新增 get_risk_for_alertname() public API
P1-3: classify_notification() docstring 修正 ORM→Pydantic

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:51:12 +08:00
OG T
f2fc4712ad feat(flywheel): Phase 4 — KM conversion hook + daily vectorize cron
Some checks failed
CD Pipeline / build-and-deploy (push) Has been cancelled
ADR-073 Phase 4-2: incident_service.resolve_incident() KM conversion hook
- resolve 時 fire-and-forget KMConversionService.convert(incident)
- 已解決的 Incident 自動轉換為結構化 KM 條目,完成飛輪「學習固化」節點
- KMConversionService (Phase 4-1) 已存在 (km_conversion_service.py, 336 lines)

ADR-073 Phase 4-3: 15-cronjob-km-vectorize.yaml
- 每日 03:00 台北時間呼叫 /api/v1/knowledge/embed-all
- 自動向量化當日新增 KM 條目,確保 RAG 查詢不遺漏新知識
- 加入 kustomization.yaml resources

Phase 4-4: handle_callback log_manual_fix 已存在 (telegram_gateway.py:2468)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:40:33 +08:00
OG T
d4b8b1588b feat(flywheel): Phase 2-2/2-4 — classify_alert_early + alertname/notification_type/alert_category 寫入
ADR-073 Phase 2-2: 早期分診,在 LLM 分析前決定 alert_category + notification_type
- webhooks.py: 新增 classify_alert_early() — 6 條規則覆蓋 config_drift/info/backup/infra/k8s/db/general
- webhooks.py: alertmanager_webhook 呼叫 classify_alert_early() 並傳入兩個 create_incident_for_approval() 呼叫點
- incident_service.py: create_incident_for_approval() 新增 notification_type/alert_category 參數,寫入 Incident model
- incident_repository.py: _incident_to_record_data() 新增 alertname/notification_type/alert_category 序列化
- db/models.py: IncidentRecord ORM 新增 alertname/notification_type/alert_category 三個 mapped_column

防止 HostBackupFailed 等告警被誤路由到 K8s executor (ADR-073 Phase 2-4 同步完成)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:33:11 +08:00
OG T
670cd5df86 refactor(flywheel): 首席架構師審查修正 C1/C2/I1/I2/I3/I4/M1
Some checks are pending
CD Pipeline / build-and-deploy (push) Has started running
C1 — Repository 層修正 (積木化鐵律):
  新增 PlaybookEmbeddingRepository (pgvector UPSERT)
  playbook_embedding_service 改透過 Repository 存取 DB,不再直接 db.execute(text(...))

C2 — Router 層業務邏輯移入 Service 層:
  create_incident_for_approval + extract_affected_services (去掉底線前綴) 移入 incident_service.py
  webhooks.py 改從 incident_service import,自身不再含業務邏輯

I1 — _infra_jobs 提升為 module-level frozenset (_INFRA_JOB_NAMES),避免每次呼叫重建

I2 — _persist_embeddings_to_db 補齊 PlaybookRAGService / list[Playbook] 型別標注

I3 — embedding 格式顯式化: "[" + ",".join(str(float(x)) for x in embedding) + "]"
  防止 pgvector 因格式差異靜默解析失敗

I4 — import asyncio 移至 main.py 頂層,移除 try 區塊內重複 import

M1 — similarity.py: 移除死代碼 `if union > 0 else 0.0`
  union 在兩個集合都非空時不可能為 0

2026-04-10 Asia/Taipei — Claude Sonnet 4.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 11:35:10 +08:00
OG T
b7ea362efc fix(api): Review #2 技術債清理 — I1/S1/S2/S3 全數修正
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 12m13s
I1: error_type 欄位補全
- AnomalyCounter.derive_key_from_incident() 新增
  從 signal.labels 提取 reason/error_type,確保四欄位完整

S1: 三處 signature 建構邏輯統一
- auto_repair_service._derive_anomaly_key() → 委託 derive_key_from_incident()
- approval_execution._get_anomaly_key_from_approval() → 同上
- incident_service.resolve_incident() B4 → 同上
- 消除 3 處重複的 signature 建構程式碼

S2: Redis Pipeline 批次查詢
- get_all_disposition_stats() 從 N+1 hgetall 改為 2 次 Pipeline
- Pipeline 1: 批次 hgetall 所有 disposition key
- Pipeline 2: 批次 hget metadata (alert_name)
- 效能從 O(2N) Redis round-trip 降至 O(2)

S3: auto_repair.py get_incident AttributeError 修復
- get_incident() → get_from_working_memory() (pre-existing bug)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-07 13:13:42 +08:00
OG T
561bcb638b fix(api): Sprint 4 首席架構師 Review P0 修正 — hash 統一 + 積木化合規
P0-1: anomaly_key hash 推導統一
- B1: 新增 _derive_anomaly_key() 使用 AnomalyCounter.hash_signature()
  取代 symptoms.compute_hash()
- B3/B4: namespace 改用 signal.labels.get("namespace", "")
  修正 getattr(signal, "namespace", "") 永遠回傳空字串

P0-2: Router 層積木化合規
- C1/C2: 封裝 get_all_disposition_stats() 到 AnomalyCounter
- Router 不再直接存取 counter.redis
- stats.py 移除未使用的 days/stats 參數

P1: get_frequency() 填充 disposition 欄位
- 與 _record_anomaly_impl() 一致,回傳完整處置統計

首席架構師評分: 82/100 → P0 全數修正

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-07 12:53:12 +08:00
OG T
9253281d46 feat(api): Sprint 4 Phase A+B — 告警處置統計資料層+寫入層
Phase A: 資料層
- A1: IncidentFrequencyStats 新增 4 欄位 (human_approved/manual_resolved/cold_start_trust/total_resolution)
- A2: AnomalyCounter.record_disposition() — Redis HINCRBY 原子遞增
- A3: get_disposition_stats() — HGETALL 回傳處置分佈
- AnomalyFrequency dataclass 擴充 + to_dict() 同步
- _record_anomaly_impl() 整合 disposition stats

Phase B: 寫入層觸發點接線
- B1: 自動修復成功 → record_disposition("auto_repair")
- B2: 冷啟動信任成功 → record_disposition("cold_start_trust")
  - AutoRepairDecision 新增 is_cold_start flag
  - execute_auto_repair() 接收並區分處置類型
- B3: 人工批准執行成功 → record_disposition("human_approved")
  - 新增 _get_anomaly_key_from_approval() helper
- B4: 手動處理推斷 → resolve_incident() 排除法判定
  - 若 resolved 且無 auto/human/cold_start 紀錄 → manual_resolved

安全設計: 所有 disposition 記錄走 try/except,失敗不阻塞主流程

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-07 11:54:46 +08:00
OG T
c1834a7156 feat(kb+apm): KB Phase 2-A 自動萃取 + KB-D Markdown 詳情面板 + APM 趨勢圖
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 7m28s
- KB-A: 新增 knowledge_extractor_service.py (Ollama llama3.2:3b 本地推理)
- KB-A: incident_service.py resolve hook (fire-and-forget asyncio.create_task)
- KB-D: 引入 react-markdown + remark-gfm,知識庫詳情面板 Markdown 渲染
- KB-D: 批准/封存按鈕串接 API (POST /knowledge/{id}/approve, PATCH status)
- KB-D: i18n 新增 approving/archiving 載入狀態文字
- APM: apm/page.tsx 整合 TimeSeriesChart sparkline (使用 trend[] 欄位)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 14:40:27 +08:00
OG T
6dc1505584 feat(incident): add trigger_reanalysis() with Redis 10min dedup (ADR-050) 2026-04-01 21:06:39 +08:00
OG T
89f0bae3f2 feat(safety-net): complete wave 1 atomicity (adr-038, adr-039, debounce, graceful degrade, xclaim)
Some checks failed
E2E Health Check / e2e-health (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
2026-03-29 23:55:38 +08:00
OG T
d89f0520f9 fix(api): 修復 34 個 Ruff lint 錯誤
- 自動修復 import 排序、unused imports
- 手動修復 raise from、isinstance union、unused variable
- scripts/ 暫時保留 (非 CI 阻擋)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-29 15:27:49 +08:00
OG T
a470a514e6 refactor(api): Phase 17 P0 Router 層違規全部修復
消除 Router 層直接存取 Redis/DB 的違規:

incidents.py (6 處):
- 改用 IncidentService.get_active_incidents()
- 改用 IncidentService.get_from_working_memory()
- 改用 IncidentService.update_outcome()
- 改用 IncidentService.resolve_incident()
- 改用 IncidentService.find_by_proposal_id()

stats.py (8 處):
- 新增 StatsService 封裝快取邏輯
- 移除直接 Redis 存取

audit_logs.py (7 處):
- 新增 AuditLogRepository 封裝 DB 操作
- Router 改用 Repository 層

webhooks.py (2 處):
- 新增 SignalProducerService 封裝 Redis Stream
- 改用 IncidentService.save_to_working_memory()

符合 leWOOOgo 積木化規範:
Router → Service → Repository → DB/Redis

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-26 13:06:47 +08:00
OG T
c0ad8f8686 fix(api): 方案 C - Incident 解析相容舊格式 Enum
問題: Redis 存有舊 Enum 值 (status='open', severity='critical')
      導致 Pydantic 驗證失敗

解法:
- normalize_status(): 'open' → 'investigating'
- normalize_severity(): 'critical' → 'P0' 等
- 應用於 get_from_working_memory, get_active_incidents, _record_to_incident

優點:
- 零資料風險 (不動 Redis)
- 回滾 = git revert (秒級)
- 新舊格式都能讀

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-25 14:14:58 +08:00
OG T
6f049877fc fix(lint): ruff auto-fix + lewooogo-core src 加入 git
- Python: ruff --fix 修復 280 個 lint 錯誤
- lewooogo-core: src/ 目錄未追蹤,導致 CI eslint 失敗

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-23 23:51:37 +08:00
OG T
196d269b92 feat: add all application source code
- apps/api: FastAPI backend with Dockerfile
- apps/web: Next.js frontend with Dockerfile
- apps/sensor: Signal collection agent
- packages: shared packages

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-22 18:57:44 +08:00