fix(governance): stop km healthcheck requeue
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 2m1s
CD Pipeline / build-and-deploy (push) Successful in 4m45s
CD Pipeline / post-deploy-checks (push) Successful in 1m46s

This commit is contained in:
Your Name
2026-05-19 23:01:03 +08:00
parent ac0d2329f7
commit 8342cfa460
4 changed files with 82 additions and 1 deletions

View File

@@ -37,6 +37,7 @@ from src.repositories.governance_remediation_dispatch_repo import (
DispatchAlreadyActive,
create_dispatch,
get_active_for_event,
list_by_event,
transition_status,
)
from src.services.decision_fusion_adapter import FusedDecision, get_decision_fusion_adapter
@@ -154,6 +155,13 @@ async def dispatch_governance_event(event: AiGovernanceEvent) -> str | None:
# knowledge_degradation 的 run_kb_growth_healthcheck 是治理工作項 intake
# 不是自動修復執行;先落 pending dispatch讓既有 unresolved 事件也能在 AwoooP 追蹤。
if _is_kb_growth_healthcheck_event(event):
if await _has_kb_growth_review_draft(event_id):
logger.debug(
"governance_kb_healthcheck_dispatch_skipped_waiting_review",
event_id=event_id,
event_type=event_type,
)
return None
return await _record_kb_growth_healthcheck_dispatch(event)
# Step 1: Redis skip 冷卻檢查(防止一般 skip 事件每 30s 重新做 LLM 呼叫)。
@@ -431,6 +439,37 @@ def _is_kb_growth_healthcheck_event(event: AiGovernanceEvent) -> bool:
return details.get("next_action") == "run_kb_growth_healthcheck"
async def _has_kb_growth_review_draft(event_id: str) -> bool:
"""同一治理事件已有 Hermes review draft 時,不再重複建立 pending dispatch。"""
try:
rows = await list_by_event(event_id)
except Exception as exc:
logger.warning(
"governance_kb_healthcheck_history_lookup_failed",
event_id=event_id,
error=str(exc),
)
return False
for row in rows:
if row.executor_type != "hermes_kb_growth_healthcheck":
continue
if row.dispatch_status != "succeeded":
continue
context = row.decision_context if isinstance(row.decision_context, dict) else {}
workflow = context.get("workflow") if isinstance(context.get("workflow"), dict) else {}
worker_result = (
context.get("worker_result")
if isinstance(context.get("worker_result"), dict)
else {}
)
if workflow.get("current_stage") == "waiting_owner_review":
return True
if worker_result.get("status") == "draft_created":
return True
return False
def _extract_event_next_action(details: dict[str, Any], fallback: str) -> str:
remediation = details.get("remediation")
if isinstance(remediation, dict):