diff --git a/apps/api/src/agents/protocol.py b/apps/api/src/agents/protocol.py index d52474eb0..37e4a8c14 100644 --- a/apps/api/src/agents/protocol.py +++ b/apps/api/src/agents/protocol.py @@ -133,7 +133,8 @@ class RecommendedAction: label: str # UI 顯示文字(如「查 Pod 日誌」) emoji: str # UI 圖示(如「📋」) mcp_provider: Literal[ # MCP provider 限制在已知清單 - "k8s", "ssh", "prometheus", "signoz", "database", "internal" + "k8s", "ssh", "prometheus", "signoz", "database", "internal", + "controlled", ] mcp_tool: str # MCP tool 名(必須在 B2 allowlist) params: dict[str, str] # 參數模板(支援 {labels.xxx} / {incident_id}) diff --git a/apps/api/src/agents/solver_agent.py b/apps/api/src/agents/solver_agent.py index 384822fcc..fbf83cd48 100644 --- a/apps/api/src/agents/solver_agent.py +++ b/apps/api/src/agents/solver_agent.py @@ -92,7 +92,7 @@ _CALLBACK_SPEC_PATH = Path(__file__).parent.parent / "services" / "callback_acti # 有效的 mcp_provider 清單(與 RecommendedAction schema 對應) _VALID_MCP_PROVIDERS = frozenset( - {"k8s", "ssh", "prometheus", "signoz", "database", "internal"} + {"k8s", "ssh", "prometheus", "signoz", "database", "internal", "controlled"} ) # 有效的 risk 等級(與 RecommendedAction schema 對應) @@ -710,7 +710,7 @@ blast_radius 參考: recommended_actions 規則(北極星 §1.1 修復多樣性): - 第一個動作必須是診斷/查看類(低風險),讓 SRE 先確認情況 - 不要全部是 restart 類動作 -- mcp_provider 必須是以下之一:k8s | ssh | prometheus | signoz | database | internal +- mcp_provider 必須是以下之一:k8s | ssh | prometheus | signoz | database | internal | controlled - risk 必須是以下之一:low | medium | high | critical - params 中可使用模板:{{labels.namespace}} / {{labels.pod}} / {{incident_id}} diff --git a/apps/api/tests/test_current_owner_controlled_apply_policy.py b/apps/api/tests/test_current_owner_controlled_apply_policy.py index 2149ad658..677675d4d 100644 --- a/apps/api/tests/test_current_owner_controlled_apply_policy.py +++ b/apps/api/tests/test_current_owner_controlled_apply_policy.py @@ -86,7 +86,9 @@ def test_medium_telegram_card_is_automation_progress_not_action_required() -> No assert "AI CONTROLLED AUTOMATION" in rendered assert "ACTION REQUIRED" not in rendered assert "awaiting_approval" not in rendered - assert "check>apply>verify>writeback" in rendered + assert "流程進度" in rendered + assert "執行:controlled_queue_evaluation" in rendered + assert "驗證:not_started" in rendered def test_critical_telegram_card_stays_break_glass() -> None: diff --git a/apps/api/tests/test_sre_k3s_controlled_automation_work_items_api.py b/apps/api/tests/test_sre_k3s_controlled_automation_work_items_api.py index e0fd4c5e6..67552e8cc 100644 --- a/apps/api/tests/test_sre_k3s_controlled_automation_work_items_api.py +++ b/apps/api/tests/test_sre_k3s_controlled_automation_work_items_api.py @@ -117,7 +117,7 @@ def test_loader_returns_fixed_architecture_provider_order_and_agent99_bridge() - "by_priority": {"P0": 16, "P1": 2}, "by_status": { "source_implemented_runtime_pending": 17, - "planned": 1, + "in_progress": 1, }, "source_implemented_items": 17, "runtime_closed_items": 0, diff --git a/apps/api/tests/test_telegram_integration.py b/apps/api/tests/test_telegram_integration.py index ad7ba800e..6abe9499e 100644 --- a/apps/api/tests/test_telegram_integration.py +++ b/apps/api/tests/test_telegram_integration.py @@ -136,7 +136,8 @@ class TestTelegramMessageStructure: assert "api-service" in result assert "INC-20260321-0001" in result assert "後端" in result - assert "88%" in result + assert "AI 結果未附 durable provider receipt" in result + assert "88%" not in result def test_telegram_message_with_signoz_metrics(self): """✅ 含 SignOz 指標的訊息格式""" diff --git a/apps/api/tests/test_telegram_message_templates.py b/apps/api/tests/test_telegram_message_templates.py index 2ef2294d0..902af98ff 100644 --- a/apps/api/tests/test_telegram_message_templates.py +++ b/apps/api/tests/test_telegram_message_templates.py @@ -2460,9 +2460,11 @@ class TestTelegramMessageFormat: assert "處置狀態" in result assert "規則建議待 AI controlled policy 判定" in result assert "AI 自動化鏈路" in result - assert "OpenClaw" in result - assert "NemoTron" in result - assert "ElephantAlpha" in result + assert "分析:not_observed" in result + assert "Agent:not_observed" in result + assert "OpenClaw" not in result + assert "NemoTron" not in result + assert "ElephantAlpha" not in result assert "自動化資產沉澱" in result assert "KM" in result assert "PlayBook" in result @@ -2471,6 +2473,39 @@ class TestTelegramMessageFormat: assert "Verifier" in result assert len(result) <= 4096 # Telegram HTML message limit + def test_telegram_message_names_only_receipt_backed_ai_and_agent(self): + msg = TelegramMessage( + status_emoji="⚠️", + risk_level="MEDIUM", + resource_name="backup_restore", + root_cause="Backup freshness evidence verified", + suggested_action="NO_ACTION", + estimated_downtime="none", + approval_id="INC-20260719-AI-RECEIPT", + automation_quality={ + "verdict": "diagnostic_only", + "facts": { + "analysis_receipt_observed": True, + "analysis_provider": "ollama_gcp_a:qwen3", + "execution_actors": ["Agent99 BackupCheck (read-only)"], + "latest_execution_backend": "Agent99", + "latest_operation_type": "BackupCheck", + "latest_operation_status": "completed", + "verification_result": "passed", + "knowledge_entries": 1, + "mcp_gateway_total": 1, + }, + }, + ) + + result = msg.format() + + assert "分析:ollama_gcp_a:qwen3" in result + assert "Agent99 BackupCheck (read-only)" in result + assert "Executor:Agent99" in result + assert "Verifier:passed" in result + assert "Learning:KM 1 / MCP 1" in result + def test_telegram_message_ai_proposal_marks_controlled_apply(self): """有 AI 信心分數的修復建議必須排入 controlled apply。""" msg = TelegramMessage( diff --git a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json index 5a4584c60..e761a09dd 100644 --- a/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json +++ b/docs/operations/sre-k3s-controlled-automation-work-items.snapshot.json @@ -919,7 +919,7 @@ "title": "Gitea CD、production runtime 與 visible closure", "owner_lane": "ReleaseControl", "risk": "high", - "status": "planned", + "status": "in_progress", "dependencies": [ "AIA-SRE-001", "AIA-SRE-002", @@ -938,12 +938,17 @@ "verifier": "deploy marker, image SHA, API readback and desktop/mobile smoke", "rollback": "previous immutable image revision and ConfigMap source SHA", "exit_condition": "source/test/CD/runtime/UI evidence all match one deployed SHA; gitea-native exact metrics are fresh and the retired legacy GitHub exporter is absent from production", + "confirmed_truth": [ + "the local release candidate is merged with the latest observed gitea/main and passes the CD-equivalent non-integration API gate with 5666 passed and 23 skipped, including the 977-test changed-file suite; 4 focused web tests, web typecheck and JSON/YAML parsing also pass", + "stale Telegram tests that required unobserved OpenClaw/NemoTron/ElephantAlpha labels, a promotional check/apply/verify/writeback string or an unreceipted 88 percent confidence were replaced by no-false-AI receipt and actual stage-state contracts; receipt-backed provider, Agent99 executor, verifier and KM/MCP evidence remain visible", + "the Solver, RecommendedAction schema and callback registry now agree that host/systemd actions use the controlled provider and host_ansible_reconcile typed route instead of generic SSH fallback" + ], "runtime_gaps": [ "no program item is runtime closed for this source SHA", "gitea-native exact target is missing while legacy GitHub exporter runtime drift remains", - "source/test readiness, CD terminal, production runtime and visible UI evidence are separate incomplete layers" + "local source/test readiness is complete, but no normal push, CD terminal, deploy marker, production runtime or visible UI/Telegram evidence exists for this candidate" ], - "next_action": "commit phase 1, normal push after shared writer release, then terminal CD plus production API/runtime/UI readback and exact gitea-native/legacy-exporter verification" + "next_action": "hand the exact local candidate SHA to the authorized release lane for one normal push after shared-writer coordination, then require terminal CD plus production API/runtime/UI/Telegram readback and exact gitea-native/legacy-exporter verification" } ], "rollups": { @@ -954,7 +959,7 @@ }, "by_status": { "source_implemented_runtime_pending": 17, - "planned": 1 + "in_progress": 1 }, "source_implemented_items": 17, "runtime_closed_items": 0,