From 17bdcb194eed07ccdb912d7738f27c515b247499 Mon Sep 17 00:00:00 2001 From: ogt Date: Tue, 14 Jul 2026 23:11:20 +0800 Subject: [PATCH] fix(api): prefer latest repair terminal truth --- .../services/awooop_truth_chain_service.py | 13 +++++++-- .../tests/test_awooop_truth_chain_service.py | 29 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/api/src/services/awooop_truth_chain_service.py b/apps/api/src/services/awooop_truth_chain_service.py index c733ffcea..bffe61f9e 100644 --- a/apps/api/src/services/awooop_truth_chain_service.py +++ b/apps/api/src/services/awooop_truth_chain_service.py @@ -697,7 +697,11 @@ def build_automation_quality( noop_ops = [row for row in automation_ops if _is_no_action_operation(row)] audit_only_ops = [row for row in automation_ops if _is_audit_only_operation(row)] automation_statuses = {str(row.get("status") or "").lower() for row in effective_ops} - auto_repair_successes = {row.get("success") for row in auto_repair_executions} + latest_auto_repair_success = ( + auto_repair_executions[0].get("success") + if auto_repair_executions + else None + ) has_execution = bool(effective_ops or auto_repair_executions) verification_result = _latest_verification_result(incident, evidence_rows) @@ -758,7 +762,11 @@ def build_automation_quality( gate("timeline_recorded", "passed" if timeline_events else "missing", str(len(timeline_events))) if has_execution and ( - False in auto_repair_successes or automation_statuses & {"failed", "error"} + latest_auto_repair_success is False + or ( + latest_auto_repair_success is None + and automation_statuses & {"failed", "error"} + ) ): verdict = "execution_failed" elif has_execution and verification_status == "success": @@ -824,6 +832,7 @@ def build_automation_quality( "audit_only_operation_records": len(audit_only_ops), "approval_repair_suppressed": approval_suppressed, "auto_repair_execution_records": len(auto_repair_executions), + "latest_auto_repair_success": latest_auto_repair_success, "verification_result": verification_result, "knowledge_entries": len(km_entries), "timeline_events": len(timeline_events), diff --git a/apps/api/tests/test_awooop_truth_chain_service.py b/apps/api/tests/test_awooop_truth_chain_service.py index 44a6081f7..47c7b7ba3 100644 --- a/apps/api/tests/test_awooop_truth_chain_service.py +++ b/apps/api/tests/test_awooop_truth_chain_service.py @@ -889,6 +889,35 @@ def test_automation_quality_marks_verified_auto_repair() -> None: assert quality["blockers"] == [] +def test_latest_verified_repair_supersedes_historical_failed_apply() -> None: + quality = build_automation_quality( + incident={ + "incident_id": "INC-REPAIRED-AFTER-FAILURE", + "status": "RESOLVED", + "verification_result": "success", + }, + approvals=[], + evidence_rows=[{"sensors_attempted": 3, "sensors_succeeded": 3}], + automation_ops=[ + {"operation_type": "ansible_apply_executed", "status": "success"}, + {"operation_type": "ansible_apply_executed", "status": "failed"}, + ], + auto_repair_executions=[ + {"id": "repair-new", "success": True, "playbook_id": "pb-v5"}, + {"id": "repair-old", "success": False, "playbook_id": "pb-v4"}, + ], + gateway_mcp_summary={"total": 3}, + legacy_mcp_summary={"total": 0}, + outbound_rows=[{"message_id": "m-success"}], + km_entries=[{"id": "km-success"}], + timeline_events=[{"id": "tl-success"}], + ) + + assert quality["verdict"] == "auto_repaired_verified" + assert quality["facts"]["latest_auto_repair_success"] is True + assert quality["blockers"] == [] + + def test_automation_quality_marks_degraded_auto_repair_verification() -> None: quality = build_automation_quality( incident={