diff --git a/apps/api/src/services/platform_operator_service.py b/apps/api/src/services/platform_operator_service.py index 92573af4..71085a58 100644 --- a/apps/api/src/services/platform_operator_service.py +++ b/apps/api/src/services/platform_operator_service.py @@ -580,12 +580,15 @@ def _callback_reply_audit_summary_from_row( if callback_total <= 0: snapshot_status = "no_callback" next_action = "press_telegram_detail_or_history" - elif missing > 0: - snapshot_status = "not_captured" + elif captured > 0 and (missing > 0 or partial > 0): + snapshot_status = "partial" next_action = "press_telegram_detail_or_history_after_rollout" elif partial > 0: snapshot_status = "partial" next_action = "press_telegram_detail_or_history_after_rollout" + elif missing > 0: + snapshot_status = "not_captured" + next_action = "press_telegram_detail_or_history_after_rollout" elif outbound_total > 0 and outbound_incident_refs == 0: snapshot_status = "captured" next_action = "review_outbound_source_refs" diff --git a/apps/api/tests/test_awooop_operator_timeline_labels.py b/apps/api/tests/test_awooop_operator_timeline_labels.py index 4b6dbe9f..97300879 100644 --- a/apps/api/tests/test_awooop_operator_timeline_labels.py +++ b/apps/api/tests/test_awooop_operator_timeline_labels.py @@ -679,7 +679,7 @@ def test_list_callback_replies_response_preserves_callback_evidence() -> None: "callback_snapshot_partial_total": 1, "callback_snapshot_missing_total": 1, "callback_incident_total": 2, - "snapshot_status": "not_captured", + "snapshot_status": "partial", "next_action": "press_telegram_detail_or_history_after_rollout", "latest_outbound_at": datetime(2026, 5, 18, 7, 40, 0), "latest_callback_at": datetime(2026, 5, 18, 7, 31, 37), @@ -704,7 +704,7 @@ def test_list_callback_replies_response_preserves_callback_evidence() -> None: assert dumped["items"][0]["run_detail_href"].endswith("project_id=awoooi") assert dumped["summary"]["outbound_total"] == 120 assert dumped["summary"]["callback_snapshot_missing_total"] == 1 - assert dumped["summary"]["snapshot_status"] == "not_captured" + assert dumped["summary"]["snapshot_status"] == "partial" def test_list_callback_replies_keeps_audit_summary_separate_from_km_summary() -> None: @@ -749,6 +749,37 @@ def test_callback_reply_audit_summary_marks_missing_snapshots() -> None: assert summary["next_action"] == "press_telegram_detail_or_history_after_rollout" +def test_callback_reply_audit_summary_marks_mixed_legacy_snapshots_partial() -> None: + summary = _callback_reply_audit_summary_from_row( + { + "outbound_total": 5221, + "outbound_source_envelope_total": 4905, + "outbound_source_refs_total": 4676, + "outbound_incident_ref_total": 920, + "outbound_failed_total": 0, + "callback_total": 3, + "callback_sent_total": 3, + "callback_fallback_total": 0, + "callback_rescue_total": 0, + "callback_failed_total": 0, + "callback_detail_total": 0, + "callback_history_total": 3, + "callback_snapshot_captured_total": 1, + "callback_snapshot_partial_total": 0, + "callback_snapshot_missing_total": 2, + "callback_incident_total": 1, + "latest_outbound_at": datetime(2026, 5, 25, 9, 15, 14), + "latest_callback_at": datetime(2026, 5, 25, 9, 30, 0), + }, + project_id="awoooi", + ) + + assert summary["callback_snapshot_captured_total"] == 1 + assert summary["callback_snapshot_missing_total"] == 2 + assert summary["snapshot_status"] == "partial" + assert summary["next_action"] == "press_telegram_detail_or_history_after_rollout" + + @pytest.mark.asyncio async def test_km_stale_completion_summary_matches_callback_incident( monkeypatch, diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index d67d0abd..cf76cf01 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -20339,3 +20339,70 @@ GET /api/v1/health: - KM governance:約 84.5%。 - AI Provider lane visibility:約 92.2%。 - 完整 AI 自動化管理產品化:約 97.2%。 + +--- + +## 2026-05-25 T184 — Telegram Callback Snapshot 實機驗證與 Partial Summary 校正 + +**背景**: + +- T183 上線後,Run 監控已能看到 Telegram outbound / callback coverage。 +- production truth 顯示 callback reply 只有 `2` 筆,且皆為舊 rollout 前資料,`callback_snapshot_captured_total=0`、`callback_snapshot_missing_total=2`。 +- UI 下一步正確提示需要重新按 Telegram「詳情 / 歷史」補新版 snapshot,但仍需證明新版 handler 真的會把 AwoooP 狀態鏈 / MCP / Sentry / SigNoz / PlayBook / KM 快照寫回 DB。 + +**production read-only callback 驗證(完成)**: + +```text +before: + callback_total = 2 + callback_snapshot_captured_total = 0 + callback_snapshot_missing_total = 2 + snapshot_status = not_captured + next_action = press_telegram_detail_or_history_after_rollout + +trigger: + POST /api/v1/telegram/webhook + callback_data = history:INC-20260524-16109D + result = ok=True, message=info:history + +after: + callback_total = 3 + callback_snapshot_captured_total = 1 + callback_snapshot_missing_total = 2 + latest_callback.action = history + latest_callback.incident_id = INC-20260524-16109D + latest_callback.evidence_capture_status = captured +``` + +**本輪修正**: + +- `callback_snapshot_captured_total > 0` 且仍有舊資料 missing / partial 時,coverage summary 改顯示 `snapshot_status=partial`。 +- 避免操作者誤解成「新版流程完全沒有捕捉 snapshot」;實際語意是「新版已成功捕捉,legacy callback 仍缺 snapshot」。 +- 新增 regression test: + - 純 legacy missing → `not_captured`。 + - mixed captured + legacy missing → `partial`。 + +**local validation(完成)**: + +```text +python3 -m py_compile apps/api/src/services/platform_operator_service.py apps/api/tests/test_awooop_operator_timeline_labels.py +git diff --check +PYTHONPATH=. DATABASE_URL='postgresql+asyncpg://test:test@localhost/test' /Users/ogt/.pyenv/shims/pytest tests/test_awooop_operator_timeline_labels.py -q + 53 passed in 0.85s +``` + +**目前整體進度**: + +- AwoooP 告警可觀測鏈:約 99.58%。 +- 低風險自動修復閉環:約 95.8%。 +- 前端 AI 自動化管理介面同步:約 99.1%。 +- 首頁 KPI / 小龍蝦流程 truth alignment:約 96.5%。 +- Telegram 詳情 / 歷史可追溯:約 98.9%。 +- Telegram outbound / callback DB coverage 可視化:約 98.8%。 +- callback / DB replayability:約 98.4%。 +- MCP / 自建 MCP 可視化:約 95.1%。 +- Sentry / SigNoz source correlation:約 93.6%。 +- Ansible / PlayBook 可視化:約 92.6%。 +- KM governance:約 84.6%。 +- AI Provider lane visibility:約 92.2%。 +- 完整 AI 自動化管理產品化:約 97.3%。