feat(awooop): expose ai automation log taxonomy
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 21s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled

This commit is contained in:
Your Name
2026-06-29 15:21:17 +08:00
parent 8f0c6d3002
commit fe42ed1b43
10 changed files with 1389 additions and 2 deletions

View File

@@ -151,6 +151,451 @@ def _status_counts(
}
def _trace_stage(
*,
stage_id: str,
display_name: str,
source_tables: list[str],
total: int,
recent: int,
required_for_closed_loop: bool,
feeds_learning: bool,
public_safe: bool = True,
next_action_if_missing: str | None = None,
) -> dict[str, Any]:
present = total > 0
return {
"stage_id": stage_id,
"display_name": display_name,
"source_tables": source_tables,
"recorded": present,
"record_quality": "recorded" if present else "missing",
"total": max(0, total),
"recent": max(0, recent),
"required_for_closed_loop": required_for_closed_loop,
"feeds_learning": feeds_learning,
"public_safe": public_safe,
"next_action_if_missing": None if present else next_action_if_missing,
}
def _trace_total(summary: Mapping[str, Any] | None, *operation_types: str) -> int:
if not isinstance(summary, Mapping):
return 0
if not operation_types:
return _int_value(summary.get("total"))
return sum(
_int_value((summary.get(operation_type) or {}).get("total"))
for operation_type in operation_types
)
def _trace_recent(summary: Mapping[str, Any] | None, *operation_types: str) -> int:
if not isinstance(summary, Mapping):
return 0
if not operation_types:
return _int_value(summary.get("recent"))
return sum(
_int_value((summary.get(operation_type) or {}).get("recent"))
for operation_type in operation_types
)
def _build_trace_ledger(
*,
operation_summary: Mapping[str, Any],
auto_repair_summary: Mapping[str, Any],
verifier_summary: Mapping[str, Any],
km_summary: Mapping[str, Any],
telegram_summary: Mapping[str, Any],
mcp_gateway_summary: Mapping[str, Any],
legacy_mcp_summary: Mapping[str, Any],
service_log_summary: Mapping[str, Any],
executor_log_summary: Mapping[str, Any],
timeline_summary: Mapping[str, Any],
playbook_trust_summary: Mapping[str, Any],
latest_flow_closure: Mapping[str, Any],
loop_ledger: Mapping[str, Any],
) -> dict[str, Any]:
"""Build the full public-safe AI automation trace ledger."""
mcp_total = _trace_total(mcp_gateway_summary) + _trace_total(legacy_mcp_summary)
mcp_recent = _trace_recent(mcp_gateway_summary) + _trace_recent(legacy_mcp_summary)
stages = [
_trace_stage(
stage_id="mcp_context",
display_name="MCP sensor / tool context",
source_tables=["awooop_mcp_gateway_audit", "mcp_audit_log"],
total=mcp_total,
recent=mcp_recent,
required_for_closed_loop=False,
feeds_learning=True,
next_action_if_missing="record_mcp_gateway_or_legacy_mcp_audit_for_every_ai_decision",
),
_trace_stage(
stage_id="service_log_evidence",
display_name="Sanitized service / package log evidence",
source_tables=["incident_evidence.recent_logs", "incident_evidence.evidence_summary"],
total=_trace_total(service_log_summary),
recent=_trace_recent(service_log_summary),
required_for_closed_loop=False,
feeds_learning=True,
next_action_if_missing="collect_sanitized_service_log_evidence_before_ai_decision",
),
_trace_stage(
stage_id="candidate",
display_name="AI candidate / playbook match",
source_tables=["automation_operation_log"],
total=_trace_total(operation_summary, "ansible_candidate_matched"),
recent=_trace_recent(operation_summary, "ansible_candidate_matched"),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="candidate_backfill_worker_enqueue_allowlisted_playbook",
),
_trace_stage(
stage_id="check_mode",
display_name="No-write check-mode / dry-run",
source_tables=["automation_operation_log"],
total=_trace_total(operation_summary, "ansible_check_mode_executed"),
recent=_trace_recent(operation_summary, "ansible_check_mode_executed"),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="ansible_check_mode_worker_claims_candidate",
),
_trace_stage(
stage_id="executor_log_projection",
display_name="Executor stdout / stderr / dry-run projection",
source_tables=[
"automation_operation_log.output",
"automation_operation_log.error",
"automation_operation_log.stderr_feed_back",
"automation_operation_log.dry_run_result",
],
total=_trace_total(executor_log_summary),
recent=_trace_recent(executor_log_summary),
required_for_closed_loop=False,
feeds_learning=True,
next_action_if_missing="persist_sanitized_executor_log_projection_for_failed_or_applied_actions",
),
_trace_stage(
stage_id="controlled_apply",
display_name="Controlled apply execution",
source_tables=["automation_operation_log"],
total=_trace_total(operation_summary, "ansible_apply_executed"),
recent=_trace_recent(operation_summary, "ansible_apply_executed"),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="controlled_apply_worker_waits_for_check_mode_success",
),
_trace_stage(
stage_id="auto_repair_execution_receipt",
display_name="Auto-repair execution receipt",
source_tables=["auto_repair_executions"],
total=_trace_total(auto_repair_summary),
recent=_trace_recent(auto_repair_summary),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="receipt_backfill_records_auto_repair_execution",
),
_trace_stage(
stage_id="post_apply_verifier",
display_name="Post-apply verifier evidence",
source_tables=["incident_evidence"],
total=_trace_total(verifier_summary),
recent=_trace_recent(verifier_summary),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="post_apply_verifier_writes_incident_evidence",
),
_trace_stage(
stage_id="rag_km_learning",
display_name="RAG / KM / PlayBook learning writeback",
source_tables=["knowledge_entries"],
total=_trace_total(km_summary),
recent=_trace_recent(km_summary),
required_for_closed_loop=True,
feeds_learning=True,
next_action_if_missing="hermes_writes_km_playbook_trust_candidate",
),
_trace_stage(
stage_id="playbook_trust",
display_name="PlayBook trust / success-failure learning",
source_tables=[
"playbooks.trust_score",
"playbooks.success_count",
"playbooks.failure_count",
"playbooks.review_required",
],
total=_trace_total(playbook_trust_summary),
recent=_trace_recent(playbook_trust_summary),
required_for_closed_loop=False,
feeds_learning=True,
next_action_if_missing="write_playbook_trust_delta_after_verified_execution",
),
_trace_stage(
stage_id="timeline_projection",
display_name="Operator timeline projection",
source_tables=["timeline_events"],
total=_trace_total(timeline_summary),
recent=_trace_recent(timeline_summary),
required_for_closed_loop=False,
feeds_learning=True,
next_action_if_missing="project_ai_runtime_stage_to_timeline_events",
),
_trace_stage(
stage_id="telegram_receipt",
display_name="Telegram Gateway receipt",
source_tables=["awooop_outbound_message"],
total=_trace_total(telegram_summary),
recent=_trace_recent(telegram_summary),
required_for_closed_loop=True,
feeds_learning=False,
next_action_if_missing="live_apply_gateway_sends_controlled_apply_result_receipt",
),
]
required = [stage for stage in stages if stage["required_for_closed_loop"]]
missing_required = [
str(stage["stage_id"])
for stage in required
if stage["recorded"] is not True
]
recorded_count = sum(1 for stage in stages if stage["recorded"] is True)
return {
"schema_version": "ai_agent_autonomous_trace_ledger_v1",
"purpose": (
"把 AI 自動化每個節點的 public-safe receipt 收斂成同一份 ledger"
"這些紀錄是後續 RAG、KM、PlayBook trust 與報告學習的依據。"
),
"latest_flow_closed": latest_flow_closure.get("closed") is True,
"latest_loop_closed": loop_ledger.get("closed") is True,
"stage_count": len(stages),
"recorded_stage_count": recorded_count,
"required_stage_count": len(required),
"missing_required_stage_ids": missing_required,
"learning_source_stage_ids": [
str(stage["stage_id"])
for stage in stages
if stage["feeds_learning"] is True
],
"public_safety": {
"reads_raw_sessions": False,
"stores_secret_values": False,
"stores_unredacted_telegram_payload": False,
"stores_internal_reasoning": False,
},
"stages": stages,
}
def _build_log_integration_taxonomy(
*,
operation_summary: Mapping[str, Any],
auto_repair_summary: Mapping[str, Any],
verifier_summary: Mapping[str, Any],
km_summary: Mapping[str, Any],
telegram_summary: Mapping[str, Any],
mcp_gateway_summary: Mapping[str, Any],
legacy_mcp_summary: Mapping[str, Any],
service_log_summary: Mapping[str, Any],
executor_log_summary: Mapping[str, Any],
timeline_summary: Mapping[str, Any],
playbook_trust_summary: Mapping[str, Any],
) -> dict[str, Any]:
"""Expose how logs are normalized, labeled, grouped, and fed to agents."""
operation_total = sum(_trace_total(operation_summary, item) for item in _EXECUTOR_OPERATION_TYPES)
operation_recent = sum(_trace_recent(operation_summary, item) for item in _EXECUTOR_OPERATION_TYPES)
source_families = [
{
"source_family_id": "mcp_gateway_tool_calls",
"source_tables": ["awooop_mcp_gateway_audit"],
"normalized_event_schema": "ToolCallEvidence",
"label_dimensions": ["project", "run", "trace", "agent", "tool", "policy_gate"],
"total": _trace_total(mcp_gateway_summary),
"recent": _trace_recent(mcp_gateway_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "hash_only_no_raw_input_output",
"next_action_if_empty": "route_first_class_tools_through_awooop_mcp_gateway",
},
{
"source_family_id": "legacy_mcp_tool_calls",
"source_tables": ["mcp_audit_log"],
"normalized_event_schema": "LegacyToolCallEvidence",
"label_dimensions": ["incident", "session_ref", "flywheel_node", "agent", "tool"],
"total": _trace_total(legacy_mcp_summary),
"recent": _trace_recent(legacy_mcp_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "bridge_to_gateway_hash_or_redacted_summary",
"next_action_if_empty": "keep_legacy_bridge_until_all_callers_use_gateway",
},
{
"source_family_id": "service_package_logs",
"source_tables": [
"incident_evidence.recent_logs",
"incident_evidence.evidence_summary",
"incident_evidence.anomaly_context",
],
"normalized_event_schema": "ServiceLogEvidence",
"label_dimensions": ["project", "product", "website", "service", "package", "incident"],
"total": _trace_total(service_log_summary),
"recent": _trace_recent(service_log_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "sanitized_summary_only",
"next_action_if_empty": "collect_sanitized_service_package_logs_before_decision",
},
{
"source_family_id": "executor_operation_logs",
"source_tables": ["automation_operation_log"],
"normalized_event_schema": "ExecutorOperationEvidence",
"label_dimensions": [
"project",
"service",
"package",
"tool",
"incident",
"operation",
"playbook",
"risk",
],
"total": max(operation_total, _trace_total(executor_log_summary)),
"recent": max(operation_recent, _trace_recent(executor_log_summary)),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "stdout_stderr_tail_or_structured_result_only",
"next_action_if_empty": "persist_executor_operation_log_for_candidate_check_apply",
},
{
"source_family_id": "auto_repair_receipts",
"source_tables": ["auto_repair_executions"],
"normalized_event_schema": "RepairExecutionReceipt",
"label_dimensions": ["incident", "service", "playbook", "risk", "result"],
"total": _trace_total(auto_repair_summary),
"recent": _trace_recent(auto_repair_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "execution_step_refs_not_raw_secrets",
"next_action_if_empty": "write_auto_repair_execution_receipt_after_apply",
},
{
"source_family_id": "post_apply_verifier",
"source_tables": ["incident_evidence.post_execution_state"],
"normalized_event_schema": "VerifierEvidence",
"label_dimensions": ["incident", "operation", "playbook", "service", "result"],
"total": _trace_total(verifier_summary),
"recent": _trace_recent(verifier_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "post_state_summary_no_secret_values",
"next_action_if_empty": "run_post_apply_verifier_for_each_apply",
},
{
"source_family_id": "rag_km_entries",
"source_tables": ["knowledge_entries"],
"normalized_event_schema": "KnowledgeWritebackEvidence",
"label_dimensions": ["project", "incident", "playbook", "path_type", "status"],
"total": _trace_total(km_summary),
"recent": _trace_recent(km_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "curated_summary_and_refs_only",
"next_action_if_empty": "write_km_entry_after_verifier",
},
{
"source_family_id": "playbook_trust_signals",
"source_tables": ["playbooks"],
"normalized_event_schema": "PlayBookTrustSignal",
"label_dimensions": ["project", "playbook", "status", "trust_band", "review_required"],
"total": _trace_total(playbook_trust_summary),
"recent": _trace_recent(playbook_trust_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "aggregate_trust_counters_only",
"next_action_if_empty": "write_trust_delta_after_verified_execution",
},
{
"source_family_id": "operator_timeline_projection",
"source_tables": ["timeline_events"],
"normalized_event_schema": "OperatorTimelineEvent",
"label_dimensions": ["incident", "event_type", "status", "actor", "actor_role"],
"total": _trace_total(timeline_summary),
"recent": _trace_recent(timeline_summary),
"feeds_learning": True,
"public_safe": True,
"raw_payload_policy": "short_public_safe_status_projection",
"next_action_if_empty": "project_ai_runtime_stage_to_timeline_events",
},
{
"source_family_id": "telegram_delivery_receipts",
"source_tables": ["awooop_outbound_message"],
"normalized_event_schema": "NotificationReceipt",
"label_dimensions": ["project", "channel", "incident", "action", "send_status"],
"total": _trace_total(telegram_summary),
"recent": _trace_recent(telegram_summary),
"feeds_learning": False,
"public_safe": True,
"raw_payload_policy": "provider_message_ref_no_unredacted_payload",
"next_action_if_empty": "send_controlled_apply_result_via_gateway",
},
]
label_dimensions = sorted(
{
str(dimension)
for source in source_families
for dimension in source["label_dimensions"]
}
)
active_source_count = sum(1 for source in source_families if _int_value(source["total"]) > 0)
return {
"schema_version": "ai_agent_log_integration_taxonomy_v1",
"purpose": (
"將專案、產品、網站、服務、套件、工具與通知來源的 log "
"統一轉成可貼標、可分群、可回放、可餵 RAG/KM/PlayBook 的 evidence。"
),
"normalized_event_flow": [
"collect_source_log_or_receipt",
"redact_and_hash_sensitive_fields",
"assign_labels",
"correlate_incident_operation_playbook",
"write_trace_ledger",
"retrieve_similar_context_via_rag",
"select_or_repair_playbook",
"run_check_mode_then_controlled_apply",
"verify_and_write_learning_back",
],
"label_dimensions": label_dimensions,
"required_label_dimensions": [
"project",
"source_family",
"incident",
"operation",
"service",
"tool",
"playbook",
],
"source_families": source_families,
"rollups": {
"source_family_count": len(source_families),
"active_source_family_count": active_source_count,
"inactive_source_family_count": len(source_families) - active_source_count,
"label_dimension_count": len(label_dimensions),
"classified_event_total": sum(_int_value(source["total"]) for source in source_families),
"recent_classified_event_total": sum(_int_value(source["recent"]) for source in source_families),
"learning_source_family_count": sum(
1 for source in source_families if source["feeds_learning"] is True
),
},
"public_safety": {
"raw_secret_collection_allowed": False,
"raw_session_collection_allowed": False,
"unredacted_payload_storage_allowed": False,
"internal_reasoning_storage_allowed": False,
},
}
def _first_operation(
rows: Iterable[Mapping[str, Any]],
operation_type: str,
@@ -868,6 +1313,12 @@ def build_runtime_receipt_readback_from_rows(
km_latest_rows: Iterable[Mapping[str, Any] | Any] = (),
telegram_count_rows: Iterable[Mapping[str, Any] | Any] = (),
telegram_latest_rows: Iterable[Mapping[str, Any] | Any] = (),
mcp_gateway_count_rows: Iterable[Mapping[str, Any] | Any] = (),
legacy_mcp_count_rows: Iterable[Mapping[str, Any] | Any] = (),
service_log_count_rows: Iterable[Mapping[str, Any] | Any] = (),
executor_log_count_rows: Iterable[Mapping[str, Any] | Any] = (),
timeline_count_rows: Iterable[Mapping[str, Any] | Any] = (),
playbook_trust_count_rows: Iterable[Mapping[str, Any] | Any] = (),
error_type: str | None = None,
) -> dict[str, Any]:
"""Build the live executor receipt readback from already-fetched rows."""
@@ -888,6 +1339,12 @@ def build_runtime_receipt_readback_from_rows(
)
km_summary = _status_counts(km_count_rows, status_key="status")
telegram_summary = _status_counts(telegram_count_rows, status_key="send_status")
mcp_gateway_summary = _status_counts(mcp_gateway_count_rows, status_key="status")
legacy_mcp_summary = _status_counts(legacy_mcp_count_rows, status_key="status")
service_log_summary = _status_counts(service_log_count_rows, status_key="status")
executor_log_summary = _status_counts(executor_log_count_rows, status_key="status")
timeline_summary = _status_counts(timeline_count_rows, status_key="status")
playbook_trust_summary = _status_counts(playbook_trust_count_rows, status_key="status")
latest_closure = _latest_flow_closure(
operation_latest_rows=operation_latest,
verifier_latest_rows=verifier_latest,
@@ -911,6 +1368,34 @@ def build_runtime_receipt_readback_from_rows(
latest_failure_classification=latest_failure,
controlled_retry_package=retry_package,
)
trace_ledger = _build_trace_ledger(
operation_summary=operation_summary,
auto_repair_summary=auto_repair_summary,
verifier_summary=verifier_summary,
km_summary=km_summary,
telegram_summary=telegram_summary,
mcp_gateway_summary=mcp_gateway_summary,
legacy_mcp_summary=legacy_mcp_summary,
service_log_summary=service_log_summary,
executor_log_summary=executor_log_summary,
timeline_summary=timeline_summary,
playbook_trust_summary=playbook_trust_summary,
latest_flow_closure=latest_closure,
loop_ledger=loop_ledger,
)
log_integration_taxonomy = _build_log_integration_taxonomy(
operation_summary=operation_summary,
auto_repair_summary=auto_repair_summary,
verifier_summary=verifier_summary,
km_summary=km_summary,
telegram_summary=telegram_summary,
mcp_gateway_summary=mcp_gateway_summary,
legacy_mcp_summary=legacy_mcp_summary,
service_log_summary=service_log_summary,
executor_log_summary=executor_log_summary,
timeline_summary=timeline_summary,
playbook_trust_summary=playbook_trust_summary,
)
apply_summary = operation_summary.get("ansible_apply_executed") or {}
readback = {
"schema_version": _LIVE_READBACK_SCHEMA_VERSION,
@@ -1014,10 +1499,22 @@ def build_runtime_receipt_readback_from_rows(
),
),
},
"mcp_context": {
"gateway": mcp_gateway_summary,
"legacy": legacy_mcp_summary,
"total": _trace_total(mcp_gateway_summary) + _trace_total(legacy_mcp_summary),
"recent": _trace_recent(mcp_gateway_summary) + _trace_recent(legacy_mcp_summary),
},
"service_log_evidence": service_log_summary,
"executor_log_projection": executor_log_summary,
"timeline_projection": timeline_summary,
"playbook_trust": playbook_trust_summary,
"latest_flow_closure": latest_closure,
"latest_failure_classification": latest_failure,
"controlled_retry_package": retry_package,
"autonomous_execution_loop_ledger": loop_ledger,
"trace_ledger": trace_ledger,
"log_integration_taxonomy": log_integration_taxonomy,
}
if error_type:
readback["error"] = {
@@ -1076,6 +1573,50 @@ def _attach_runtime_receipt_readback(
== "ready_for_no_write_check_mode_replay"
else 0
),
"live_mcp_context_count": _int_value(readback.get("mcp_context", {}).get("total")),
"live_service_log_evidence_count": _int_value(
readback.get("service_log_evidence", {}).get("total")
),
"live_executor_log_projection_count": _int_value(
readback.get("executor_log_projection", {}).get("total")
),
"live_timeline_projection_count": _int_value(
readback.get("timeline_projection", {}).get("total")
),
"live_playbook_trust_signal_count": _int_value(
readback.get("playbook_trust", {}).get("total")
),
"live_trace_recorded_stage_count": _int_value(
readback.get("trace_ledger", {}).get("recorded_stage_count")
),
"live_trace_required_missing_count": len(
(readback.get("trace_ledger") or {}).get("missing_required_stage_ids") or []
),
"live_log_source_family_count": _int_value(
((readback.get("log_integration_taxonomy") or {}).get("rollups") or {}).get(
"source_family_count"
)
),
"live_log_active_source_family_count": _int_value(
((readback.get("log_integration_taxonomy") or {}).get("rollups") or {}).get(
"active_source_family_count"
)
),
"live_log_label_dimension_count": _int_value(
((readback.get("log_integration_taxonomy") or {}).get("rollups") or {}).get(
"label_dimension_count"
)
),
"live_log_classified_event_total": _int_value(
((readback.get("log_integration_taxonomy") or {}).get("rollups") or {}).get(
"classified_event_total"
)
),
"live_log_recent_classified_event_total": _int_value(
((readback.get("log_integration_taxonomy") or {}).get("rollups") or {}).get(
"recent_classified_event_total"
)
),
})
return payload
@@ -1321,6 +1862,19 @@ async def load_ai_agent_autonomous_runtime_receipt_readback(
try:
async with get_db_context(project_id) as db:
await db.execute(text("SET LOCAL statement_timeout = '5000ms'"))
async def _safe_aux_rows(query_name: str, sql: str) -> list[Mapping[str, Any]]:
try:
return (await db.execute(text(sql), params)).mappings().all()
except Exception as exc: # pragma: no cover - depends on live schema drift
logger.warning(
"ai_agent_autonomous_runtime_trace_aux_read_failed",
project_id=project_id,
query_name=query_name,
error_type=type(exc).__name__,
)
return []
operation_counts = (
await db.execute(text(_RUNTIME_OPERATION_COUNTS_SQL), params)
).mappings().all()
@@ -1351,6 +1905,30 @@ async def load_ai_agent_autonomous_runtime_receipt_readback(
telegram_latest = (
await db.execute(text(_RUNTIME_TELEGRAM_LATEST_SQL), params)
).mappings().all()
mcp_gateway_counts = await _safe_aux_rows(
"mcp_gateway_counts",
_RUNTIME_MCP_GATEWAY_COUNTS_SQL,
)
legacy_mcp_counts = await _safe_aux_rows(
"legacy_mcp_counts",
_RUNTIME_LEGACY_MCP_COUNTS_SQL,
)
service_log_counts = await _safe_aux_rows(
"service_log_counts",
_RUNTIME_SERVICE_LOG_COUNTS_SQL,
)
executor_log_counts = await _safe_aux_rows(
"executor_log_counts",
_RUNTIME_EXECUTOR_LOG_COUNTS_SQL,
)
timeline_counts = await _safe_aux_rows(
"timeline_counts",
_RUNTIME_TIMELINE_COUNTS_SQL,
)
playbook_trust_counts = await _safe_aux_rows(
"playbook_trust_counts",
_RUNTIME_PLAYBOOK_TRUST_COUNTS_SQL,
)
except Exception as exc:
logger.warning(
"ai_agent_autonomous_runtime_receipt_readback_failed",
@@ -1378,6 +1956,12 @@ async def load_ai_agent_autonomous_runtime_receipt_readback(
km_latest_rows=km_latest,
telegram_count_rows=telegram_counts,
telegram_latest_rows=telegram_latest,
mcp_gateway_count_rows=mcp_gateway_counts,
legacy_mcp_count_rows=legacy_mcp_counts,
service_log_count_rows=service_log_counts,
executor_log_count_rows=executor_log_counts,
timeline_count_rows=timeline_counts,
playbook_trust_count_rows=playbook_trust_counts,
)
@@ -1590,6 +2174,136 @@ _RUNTIME_TELEGRAM_LATEST_SQL = """
"""
_RUNTIME_MCP_GATEWAY_COUNTS_SQL = """
SELECT
coalesce(result_status, 'unknown') AS status,
count(*) AS total,
count(*) FILTER (
WHERE created_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM awooop_mcp_gateway_audit
WHERE project_id = :project_id
GROUP BY coalesce(result_status, 'unknown')
ORDER BY status
"""
_RUNTIME_LEGACY_MCP_COUNTS_SQL = """
SELECT
CASE
WHEN success IS TRUE THEN 'success'
WHEN success IS FALSE THEN 'failed'
ELSE 'unknown'
END AS status,
count(*) AS total,
count(*) FILTER (
WHERE created_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM mcp_audit_log
GROUP BY
CASE
WHEN success IS TRUE THEN 'success'
WHEN success IS FALSE THEN 'failed'
ELSE 'unknown'
END
ORDER BY status
"""
_RUNTIME_SERVICE_LOG_COUNTS_SQL = """
SELECT
'sanitized_recent_logs' AS status,
count(*) AS total,
count(*) FILTER (
WHERE collected_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM incident_evidence
WHERE recent_logs IS NOT NULL
OR evidence_summary IS NOT NULL
OR mcp_health IS NOT NULL
OR anomaly_context IS NOT NULL
"""
_RUNTIME_EXECUTOR_LOG_COUNTS_SQL = """
SELECT
coalesce(status, 'unknown') AS status,
count(*) AS total,
count(*) FILTER (
WHERE created_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM automation_operation_log
WHERE operation_type IN (
'ansible_candidate_matched',
'ansible_check_mode_executed',
'ansible_apply_executed',
'ansible_rollback_executed',
'ansible_execution_skipped'
)
AND (
output IS NOT NULL
OR error IS NOT NULL
OR stderr_feed_back IS NOT NULL
OR dry_run_result IS NOT NULL
)
GROUP BY coalesce(status, 'unknown')
ORDER BY status
"""
_RUNTIME_TIMELINE_COUNTS_SQL = """
SELECT
coalesce(status, 'unknown') AS status,
count(*) AS total,
count(*) FILTER (
WHERE created_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM timeline_events
WHERE event_type IN (
'mcp_call',
'verifier',
'ai_agent_deploy_control_plane_decision',
'controlled_apply',
'auto_repair',
'km_writeback'
)
OR actor IN (
'ansible_check_mode_worker',
'ansible_controlled_apply_worker',
'post_apply_verifier',
'truth_chain_reconciliation'
)
OR actor_role IN ('mcp', 'replay', 'verifier', 'executor')
"""
_RUNTIME_PLAYBOOK_TRUST_COUNTS_SQL = """
SELECT
CASE
WHEN review_required IS TRUE THEN 'review_required'
WHEN trust_score >= 0.8 THEN 'high_trust'
WHEN trust_score < 0.3 THEN 'low_trust'
WHEN success_count > 0 OR failure_count > 0 THEN 'learning_active'
ELSE 'seeded_not_used'
END AS status,
count(*) AS total,
count(*) FILTER (
WHERE updated_at >= NOW() - (:lookback_hours * INTERVAL '1 hour')
) AS recent
FROM playbooks
WHERE project_id = :project_id
GROUP BY
CASE
WHEN review_required IS TRUE THEN 'review_required'
WHEN trust_score >= 0.8 THEN 'high_trust'
WHEN trust_score < 0.3 THEN 'low_trust'
WHEN success_count > 0 OR failure_count > 0 THEN 'learning_active'
ELSE 'seeded_not_used'
END
ORDER BY status
"""
def _validate_payload(payload: dict[str, Any]) -> None:
if payload.get("schema_version") != _SCHEMA_VERSION:
raise ValueError(f"schema_version must be {_SCHEMA_VERSION}")