fix(awooop): surface telegram alert automation coverage
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 58s
CD Pipeline / build-and-deploy (push) Successful in 5m42s
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-03 09:31:05 +08:00
parent e28ebd5b3e
commit cc5868b657
6 changed files with 751 additions and 5 deletions

View File

@@ -46,6 +46,28 @@ _ALERT_LOG_EVENT_TYPES = (
"EXECUTION_STARTED",
"EXECUTION_COMPLETED",
)
_REQUIRED_TAG_DIMENSIONS = (
(
"project",
"project_id",
"bind every alert/log receipt to the project source of truth",
),
(
"product",
"product_id",
"separate AWOOOI/AwoooP/IwoooS and adjacent product automation lanes",
),
("website", "website_id", "cluster public website and route evidence"),
("service", "service_name", "cluster runtime service, pod, job, or daemon signals"),
("package", "package_name", "cluster package, dependency, and version-radar signals"),
("tool", "tool_id", "cluster MCP, script, exporter, and connector evidence"),
("log", "log_source", "preserve metadata-only log source identity"),
("alert", "alertname", "bind alert fingerprints, severity, and recurrence state"),
("playbook", "playbook_id", "route candidates into controlled apply playbooks"),
("rag", "rag_context_ref", "make receipts retrievable by RAG without raw payloads"),
("mcp", "mcp_evidence_ref", "bind tool evidence without bypassing the MCP registry"),
("schedule", "schedule_id", "cluster cron, worker, and maintenance-window signals"),
)
logger = get_logger(__name__)
@@ -137,6 +159,12 @@ def build_telegram_alert_monitoring_coverage_readback(
ai_alert_ready_total = _int(
ai_alert_summary.get("learning_writeback_ready_total")
)
runtime_event_type_counts = {
str(key): _int(value)
for key, value in _dict(
runtime_log_readback.get("event_type_counts_7d")
).items()
}
coverage_matrix = _coverage_matrix(
project_id=project_id,
@@ -165,6 +193,38 @@ def build_telegram_alert_monitoring_coverage_readback(
ai_alert_ready_total=ai_alert_ready_total,
)
ready = not active_blockers
monitoring_asset_rollups = _monitoring_asset_rollups(
monitoring_inventory,
monitoring_readback_plan,
)
alert_receipt_pipeline = _alert_receipt_pipeline(
monitoring_surface_count=monitoring_surface_count,
live_evidence_received_count=live_evidence_received_count,
monitoring_live_gap_count=monitoring_live_gap_count,
direct_gap_count=direct_gap_count,
runtime_db_ok=runtime_db_ok,
runtime_summary=runtime_summary,
ai_alert_db_ok=ai_alert_db_ok,
ai_alert_total=ai_alert_total,
ai_alert_ready_total=ai_alert_ready_total,
verifier_ready=verifier_ready,
source_ready=source_ready,
)
work_item_progress = _work_item_progress(
monitoring_live_gap_count=monitoring_live_gap_count,
direct_gap_count=direct_gap_count,
runtime_db_ok=runtime_db_ok,
runtime_summary=runtime_summary,
ai_alert_db_ok=ai_alert_db_ok,
ai_alert_total=ai_alert_total,
ai_alert_ready_total=ai_alert_ready_total,
verifier_ready=verifier_ready,
source_ready=source_ready,
)
ai_controlled_gap_queue = _ai_controlled_gap_queue(
active_blockers=active_blockers,
monitoring_asset_rollups=monitoring_asset_rollups,
)
return {
"schema_version": SCHEMA_VERSION,
@@ -178,6 +238,14 @@ def build_telegram_alert_monitoring_coverage_readback(
),
"mode": "metadata_only_db_log_ai_context_readback",
"operator_answer": {
"all_telegram_monitoring_alerts_fully_audited": ready,
"all_telegram_monitoring_alert_surfaces_have_db_or_log_receipt": (
monitoring_live_gap_count == 0 and runtime_db_ok and ai_alert_db_ok
),
"all_telegram_monitoring_alerts_ai_agent_automation_ready": (
ready and ai_alert_total > 0 and ai_alert_ready_total >= ai_alert_total
),
"ai_controlled_gap_queue_present": bool(ai_controlled_gap_queue),
"all_known_telegram_egress_routes_controlled": direct_gap_count == 0,
"all_known_telegram_egress_routes_have_db_or_log_receipt": (
direct_gap_count == 0 and ai_alert_db_ok and runtime_db_ok
@@ -248,8 +316,27 @@ def build_telegram_alert_monitoring_coverage_readback(
"source_contract_ready": source_ready,
"runtime_db_readback_ok": runtime_db_ok,
"ai_alert_card_db_readback_ok": ai_alert_db_ok,
"runtime_event_type_with_receipts_count": len(runtime_event_type_counts),
"alert_receipt_pipeline_stage_count": len(alert_receipt_pipeline),
"alert_receipt_pipeline_ready_count": sum(
1 for stage in alert_receipt_pipeline if stage["ready"]
),
"ai_controlled_gap_queue_count": len(ai_controlled_gap_queue),
"required_tag_dimension_count": len(_REQUIRED_TAG_DIMENSIONS),
"monitoring_asset_config_kind_count": len(
monitoring_asset_rollups["by_config_kind"]
),
"telegram_monitoring_audit_completion_percent": work_item_progress[
"completion_percent"
],
"full_coverage_ready": ready,
},
"required_tag_dimensions": _required_tag_dimensions(),
"monitoring_asset_rollups": monitoring_asset_rollups,
"runtime_event_type_counts_7d": runtime_event_type_counts,
"alert_receipt_pipeline": alert_receipt_pipeline,
"work_item_progress": work_item_progress,
"ai_controlled_gap_queue": ai_controlled_gap_queue,
"coverage_matrix": coverage_matrix,
"active_blockers": active_blockers,
"next_controlled_actions": _next_controlled_actions(active_blockers),
@@ -435,6 +522,240 @@ async def _load_runtime_log_readback_once(*, project_id: str) -> dict[str, Any]:
}
def _required_tag_dimensions() -> list[dict[str, str]]:
return [
{
"dimension": dimension,
"canonical_field": canonical_field,
"why": why,
}
for dimension, canonical_field, why in _REQUIRED_TAG_DIMENSIONS
]
def _monitoring_asset_rollups(
monitoring_inventory: Mapping[str, Any],
monitoring_readback_plan: Mapping[str, Any],
) -> dict[str, Any]:
surfaces = _list_of_dicts(monitoring_inventory.get("observability_surfaces"))
readback_candidates = _list_of_dicts(
monitoring_readback_plan.get("readback_candidates")
)
inventory_source = readback_candidates or surfaces
accepted_live_receipts = sum(
1
for item in inventory_source
if bool(item.get("post_incident_readback_accepted"))
or bool(item.get("live_evidence_received"))
)
live_receipt_gap_samples = [
_monitoring_gap_sample(item)
for item in inventory_source
if bool(item.get("requires_live_evidence"))
and not bool(item.get("post_incident_readback_accepted"))
and not bool(item.get("live_evidence_received"))
][:12]
return {
"surface_count": len(surfaces) or len(inventory_source),
"readback_candidate_count": len(readback_candidates),
"accepted_live_receipt_count": accepted_live_receipts,
"live_receipt_gap_count": max(
len(inventory_source) - accepted_live_receipts,
0,
),
"write_capable_surface_count": sum(
1 for item in inventory_source if bool(item.get("write_capable_surface"))
),
"requires_live_evidence_count": sum(
1 for item in inventory_source if bool(item.get("requires_live_evidence"))
),
"by_config_kind": _count_by_key(inventory_source, "config_kind"),
"by_control_tier": _count_by_key(inventory_source, "control_tier"),
"by_status": _count_by_key(inventory_source, "status"),
"by_reviewer_outcome": _count_by_key(inventory_source, "reviewer_outcome"),
"by_expected_scope_prefix": _count_by_expected_scope_prefix(inventory_source),
"live_receipt_gap_samples": live_receipt_gap_samples,
}
def _monitoring_gap_sample(item: Mapping[str, Any]) -> dict[str, Any]:
return {
"surface_id": str(item.get("surface_id") or ""),
"label": str(item.get("label") or ""),
"config_kind": str(item.get("config_kind") or "unknown"),
"control_tier": str(item.get("control_tier") or "unknown"),
"expected_scope": str(item.get("expected_scope") or ""),
"repo_source_path": str(
item.get("repo_source_path") or item.get("source_path") or ""
),
"status": str(item.get("status") or "waiting_live_receipt"),
"reviewer_outcome": str(
item.get("reviewer_outcome") or "waiting_post_incident_readback"
),
"requires_live_evidence": bool(item.get("requires_live_evidence")),
"write_capable_surface": bool(item.get("write_capable_surface")),
"missing_receipt_fields": [
key
for key in (
"monitoring_incident_or_change_ref",
"receiver_receipt_readback_ref",
"notification_delivery_metadata_ref",
"alert_chain_health_readback_ref",
"postcheck_readback_ref",
"recurrence_guard_ref",
)
if not item.get(key)
],
}
def _alert_receipt_pipeline(
*,
monitoring_surface_count: int,
live_evidence_received_count: int,
monitoring_live_gap_count: int,
direct_gap_count: int,
runtime_db_ok: bool,
runtime_summary: Mapping[str, Any],
ai_alert_db_ok: bool,
ai_alert_total: int,
ai_alert_ready_total: int,
verifier_ready: bool,
source_ready: bool,
) -> list[dict[str, Any]]:
runtime_total = _int(runtime_summary.get("alert_operation_log_total_7d"))
outbound_total = _int(runtime_summary.get("outbound_telegram_total_7d"))
return [
{
"stage_id": "monitoring_inventory_live_receipts",
"ready": monitoring_live_gap_count == 0,
"receipt_count": min(live_evidence_received_count, monitoring_surface_count),
"gap_count": monitoring_live_gap_count,
"next_action": "ingest_monitoring_surface_live_receipts_to_db_log",
},
{
"stage_id": "telegram_egress_gateway_control",
"ready": direct_gap_count == 0,
"receipt_count": 0,
"gap_count": direct_gap_count,
"next_action": "keep_all_telegram_alerts_on_gateway_receipt_path",
},
{
"stage_id": "alert_operation_log_runtime_readback",
"ready": runtime_db_ok and runtime_total > 0,
"receipt_count": runtime_total,
"gap_count": 0 if runtime_db_ok and runtime_total > 0 else 1,
"next_action": "append_alert_lifecycle_events_with_metadata_tags",
},
{
"stage_id": "awooop_outbound_telegram_ai_alert_card_mirror",
"ready": ai_alert_db_ok and ai_alert_total > 0 and outbound_total >= 0,
"receipt_count": ai_alert_total,
"gap_count": 0 if ai_alert_db_ok and ai_alert_total > 0 else 1,
"next_action": "mirror_telegram_cards_with_ai_automation_metadata",
},
{
"stage_id": "km_rag_mcp_playbook_ai_agent_learning_refs",
"ready": ai_alert_total > 0 and ai_alert_ready_total >= ai_alert_total,
"receipt_count": ai_alert_ready_total,
"gap_count": max(ai_alert_total - ai_alert_ready_total, 0)
if ai_alert_total > 0
else 1,
"next_action": "write_metadata_receipts_to_km_rag_mcp_playbook_context",
},
{
"stage_id": "ai_loop_post_apply_verifier",
"ready": verifier_ready,
"receipt_count": 1 if verifier_ready else 0,
"gap_count": 0 if verifier_ready else 1,
"next_action": "verify_context_receipts_before_calling_ai_automation_ready",
},
{
"stage_id": "source_contract_and_operator_surface",
"ready": source_ready,
"receipt_count": 1 if source_ready else 0,
"gap_count": 0 if source_ready else 1,
"next_action": "surface_receipts_in_runs_work_items_alerts",
},
]
def _work_item_progress(
*,
monitoring_live_gap_count: int,
direct_gap_count: int,
runtime_db_ok: bool,
runtime_summary: Mapping[str, Any],
ai_alert_db_ok: bool,
ai_alert_total: int,
ai_alert_ready_total: int,
verifier_ready: bool,
source_ready: bool,
) -> dict[str, Any]:
runtime_total = _int(runtime_summary.get("alert_operation_log_total_7d"))
checks = [
("source_contract_ready", source_ready),
("direct_telegram_send_gap_closed", direct_gap_count == 0),
("runtime_alert_operation_log_readback_ready", runtime_db_ok),
("runtime_alert_operation_log_has_recent_events", runtime_total > 0),
("ai_alert_card_delivery_readback_ready", ai_alert_db_ok),
("ai_alert_card_delivery_has_receipts", ai_alert_total > 0),
(
"ai_alert_card_learning_writeback_refs_ready",
ai_alert_total > 0 and ai_alert_ready_total >= ai_alert_total,
),
("ai_loop_post_apply_verifier_ready", verifier_ready),
("monitoring_live_receipt_coverage_complete", monitoring_live_gap_count == 0),
]
completed = sum(1 for _, passed in checks if passed)
return {
"work_item_id": "CIR-P0-TG-001",
"priority": "P0",
"completed_check_count": completed,
"total_check_count": len(checks),
"completion_percent": round(completed * 100 / len(checks), 1),
"checks": [
{
"check_id": check_id,
"status": "completed" if passed else "in_progress",
}
for check_id, passed in checks
],
}
def _ai_controlled_gap_queue(
*,
active_blockers: list[str],
monitoring_asset_rollups: Mapping[str, Any],
) -> list[dict[str, Any]]:
gap_samples = _list_of_dicts(monitoring_asset_rollups.get("live_receipt_gap_samples"))
queue: list[dict[str, Any]] = []
for index, blocker in enumerate(active_blockers, start=1):
queue.append({
"work_item_id": f"CIR-P0-TG-001-{index:02d}",
"parent_work_item_id": "CIR-P0-TG-001",
"priority": "P0" if index <= 4 else "P1",
"blocker": blocker,
"status": "queued_ai_controlled_apply",
"owner_review_required_for_low_medium_high": False,
"critical_break_glass_required": True,
"target_selector": _gap_target_selector(blocker),
"controlled_next_action": _gap_next_action(blocker),
"post_verifier": (
"/api/v1/agents/telegram-alert-monitoring-coverage-readback"
),
"sample_surfaces": gap_samples[:3] if blocker.startswith("monitoring_") else [],
"operation_boundaries": {
"requires_secret": False,
"requires_runtime_send": False,
"stores_raw_payload": False,
"destructive_action": False,
},
})
return queue
def _coverage_matrix(
*,
project_id: str,
@@ -781,6 +1102,64 @@ def _source_contract_blockers(source_contract: Mapping[str, Any]) -> list[str]:
]
def _gap_target_selector(blocker: str) -> dict[str, Any]:
if blocker.startswith("monitoring_live_receipt_gap"):
return {
"target_surface": "monitoring_inventory",
"target_tables": ["alert_operation_log", "awooop_outbound_message"],
"target_contexts": ["km", "rag", "playbook", "mcp", "ai_agent"],
}
if blocker.startswith("telegram_direct_send_gap"):
return {
"target_surface": "telegram_egress",
"target_tables": ["awooop_outbound_message"],
"target_contexts": ["telegram_gateway"],
}
if blocker.startswith("awooop_ai_alert_card"):
return {
"target_surface": "awooop_ai_alert_card_delivery",
"target_tables": ["awooop_outbound_message"],
"target_contexts": ["km", "rag", "playbook", "mcp", "verifier", "ai_agent"],
}
if blocker.startswith("runtime_alert_operation_log"):
return {
"target_surface": "alert_operation_log",
"target_tables": ["alert_operation_log"],
"target_contexts": ["alert_lifecycle", "ai_lane"],
}
if blocker.startswith("source_contract_missing"):
return {
"target_surface": "source_contract",
"target_tables": [],
"target_contexts": ["runs", "work_items", "alerts"],
}
return {
"target_surface": "telegram_monitoring_ai_automation",
"target_tables": ["alert_operation_log", "awooop_outbound_message"],
"target_contexts": ["km", "rag", "playbook", "mcp", "verifier", "ai_agent"],
}
def _gap_next_action(blocker: str) -> str:
if blocker.startswith("monitoring_live_receipt_gap"):
return "classify_monitoring_surfaces_and_ingest_metadata_live_receipts"
if blocker.startswith("telegram_direct_send_gap"):
return "migrate_direct_sender_to_telegram_gateway_receipt_path"
if blocker == "runtime_alert_operation_log_db_readback_unavailable":
return "retry_runtime_alert_operation_log_readback_with_bounded_timeout"
if blocker == "awooop_ai_alert_card_delivery_db_readback_unavailable":
return "retry_awooop_ai_alert_card_delivery_readback"
if blocker == "awooop_ai_alert_card_delivery_receipts_missing":
return "bind_telegram_alert_cards_to_awooop_outbound_message_mirror"
if blocker == "awooop_ai_alert_card_learning_writeback_refs_missing":
return "populate_km_rag_mcp_playbook_ai_agent_learning_refs"
if blocker == "telegram_alert_ai_loop_post_apply_verifier_not_ready":
return "run_ai_loop_context_post_apply_verifier_readback"
if blocker.startswith("source_contract_missing"):
return "restore_operator_visible_source_contract_marker"
return "continue_ai_controlled_gap_closure"
def _load_required_json(path: Path) -> dict[str, Any]:
if not path.exists():
raise FileNotFoundError(f"required monitoring coverage source missing: {path}")
@@ -841,6 +1220,29 @@ def _read_source_contract_text(repo_root: Path, relative_path: str) -> str:
return ""
def _list_of_dicts(value: Any) -> list[dict[str, Any]]:
if not isinstance(value, list):
return []
return [dict(item) for item in value if isinstance(item, Mapping)]
def _count_by_key(items: list[dict[str, Any]], key: str) -> dict[str, int]:
counts: dict[str, int] = {}
for item in items:
value = str(item.get(key) or "unknown")
counts[value] = counts.get(value, 0) + 1
return dict(sorted(counts.items()))
def _count_by_expected_scope_prefix(items: list[dict[str, Any]]) -> dict[str, int]:
counts: dict[str, int] = {}
for item in items:
expected_scope = str(item.get("expected_scope") or "unknown")
prefix = expected_scope.split("_", 1)[0] or "unknown"
counts[prefix] = counts.get(prefix, 0) + 1
return dict(sorted(counts.items()))
def _dict(value: Any) -> dict[str, Any]:
return dict(value) if isinstance(value, Mapping) else {}

View File

@@ -39,12 +39,60 @@ def _payload() -> dict:
"alert_rule_surface_count": 13,
"telegram_surface_count": 3,
},
"observability_surfaces": [
{
"surface_id": "prometheus_k8s_base_config",
"label": "K8s Prometheus base config",
"config_kind": "prometheus_config",
"control_tier": "C1",
"expected_scope": "k8s_monitoring_prometheus_base",
"requires_live_evidence": True,
"live_evidence_received": False,
},
{
"surface_id": "telegram_gateway_config",
"label": "Telegram Gateway config",
"config_kind": "telegram_gateway",
"control_tier": "C1",
"expected_scope": "telegram_gateway",
"requires_live_evidence": True,
"live_evidence_received": False,
},
],
},
monitoring_readback_plan={
"schema_version": "monitoring_post_incident_readback_plan_v1",
"summary": {
"readback_candidate_count": 60,
},
"readback_candidates": [
{
"surface_id": "prometheus_k8s_base_config",
"label": "K8s Prometheus base config",
"config_kind": "prometheus_config",
"control_tier": "C1",
"expected_scope": "k8s_monitoring_prometheus_base",
"repo_source_path": "k8s/monitoring/prometheus.yml",
"requires_live_evidence": True,
"write_capable_surface": False,
"post_incident_readback_accepted": False,
"status": "waiting_post_incident_readback",
"reviewer_outcome": "waiting_post_incident_readback",
},
{
"surface_id": "telegram_gateway_config",
"label": "Telegram Gateway config",
"config_kind": "telegram_gateway",
"control_tier": "C1",
"expected_scope": "telegram_gateway",
"repo_source_path": "apps/api/src/services/telegram_gateway.py",
"requires_live_evidence": True,
"write_capable_surface": False,
"post_incident_readback_accepted": False,
"status": "waiting_post_incident_readback",
"reviewer_outcome": "waiting_post_incident_readback",
},
],
},
telegram_matrix={
"schema_version": "telegram_alert_ai_automation_matrix_v1",
@@ -84,6 +132,13 @@ def _payload() -> dict:
"telegram_sent_event_count_7d": 8,
"km_converted_event_count_7d": 4,
"playbook_draft_event_count_7d": 3,
"outbound_telegram_total_7d": 8,
},
"event_type_counts_7d": {
"ALERT_RECEIVED": 15,
"TELEGRAM_SENT": 8,
"KM_CONVERTED": 4,
"PLAYBOOK_DRAFT_CREATED": 3,
},
},
source_contract=_source_contract(),
@@ -120,6 +175,23 @@ def test_telegram_alert_monitoring_coverage_readback_surfaces_live_gaps():
is False
)
assert payload["operator_answer"]["manual_default_terminal_state_allowed"] is False
assert (
payload["operator_answer"]["all_telegram_monitoring_alerts_fully_audited"]
is False
)
assert (
payload["operator_answer"][
"all_telegram_monitoring_alert_surfaces_have_db_or_log_receipt"
]
is False
)
assert (
payload["operator_answer"][
"all_telegram_monitoring_alerts_ai_agent_automation_ready"
]
is False
)
assert payload["operator_answer"]["ai_controlled_gap_queue_present"] is True
summary = payload["summary"]
assert summary["monitoring_surface_count"] == 60
@@ -130,8 +202,43 @@ def test_telegram_alert_monitoring_coverage_readback_surfaces_live_gaps():
assert summary["verified_target_count"] == 6
assert summary["runtime_telegram_sent_event_count_7d"] == 8
assert summary["source_contract_ready"] is True
assert summary["runtime_event_type_with_receipts_count"] == 4
assert summary["alert_receipt_pipeline_stage_count"] == 7
assert summary["alert_receipt_pipeline_ready_count"] == 6
assert summary["ai_controlled_gap_queue_count"] == 1
assert summary["required_tag_dimension_count"] == 12
assert summary["monitoring_asset_config_kind_count"] == 2
assert summary["telegram_monitoring_audit_completion_percent"] == 88.9
assert "monitoring_live_receipt_gap:60" in payload["active_blockers"]
tag_dimensions = {item["dimension"] for item in payload["required_tag_dimensions"]}
assert {"project", "product", "website", "service", "package", "tool"} <= tag_dimensions
assert {"log", "alert", "playbook", "rag", "mcp", "schedule"} <= tag_dimensions
asset_rollups = payload["monitoring_asset_rollups"]
assert asset_rollups["by_config_kind"] == {
"prometheus_config": 1,
"telegram_gateway": 1,
}
assert asset_rollups["live_receipt_gap_count"] == 2
assert asset_rollups["live_receipt_gap_samples"][0]["surface_id"] == (
"prometheus_k8s_base_config"
)
pipeline = {stage["stage_id"]: stage for stage in payload["alert_receipt_pipeline"]}
assert pipeline["monitoring_inventory_live_receipts"]["ready"] is False
assert pipeline["alert_operation_log_runtime_readback"]["receipt_count"] == 41
assert pipeline["km_rag_mcp_playbook_ai_agent_learning_refs"]["ready"] is True
assert payload["runtime_event_type_counts_7d"]["ALERT_RECEIVED"] == 15
progress = payload["work_item_progress"]
assert progress["work_item_id"] == "CIR-P0-TG-001"
assert progress["completed_check_count"] == 8
assert progress["total_check_count"] == 9
assert payload["ai_controlled_gap_queue"][0]["controlled_next_action"] == (
"classify_monitoring_surfaces_and_ingest_metadata_live_receipts"
)
matrix = {item["surface_id"]: item for item in payload["coverage_matrix"]}
assert matrix["telegram_gateway_outbound_mirror"]["gap_count"] == 0
assert matrix["telegram_alert_ai_loop_context_verifier"]["status"] == (

View File

@@ -11964,6 +11964,10 @@
"subtitle": "Monitoring alerts aligned to DB/log receipts, AI routes, controlled queue, and KM/RAG/MCP/PlayBook context.",
"status": "Coverage status",
"statusDetail": "coverage endpoint readback",
"completion": "Overall progress",
"completionDetail": "pipeline {ready}/{total}",
"tags": "Tag dimensions",
"tagsDetail": "asset kinds {kinds}",
"direct": "Direct gaps",
"directDetail": "gateway controlled: {ready}",
"monitoring": "Live receipts",
@@ -11975,6 +11979,8 @@
"logs": "7d alert log",
"logsDetail": "TG {telegram} / KM {km} / PB {playbook}",
"blockers": "Active blockers",
"queue": "AI controlled queue",
"pipeline": "Receipt pipeline",
"noBlockers": "No active blocker"
},
"recent": "近 24h {count}",

View File

@@ -11964,6 +11964,10 @@
"subtitle": "監控告警對齊 DB/log receipt、AI route、controlled queue 與 KM/RAG/MCP/PlayBook context。",
"status": "Coverage status",
"statusDetail": "coverage endpoint readback",
"completion": "整體完成度",
"completionDetail": "流水線 {ready}/{total}",
"tags": "貼標維度",
"tagsDetail": "資產類型 {kinds}",
"direct": "Direct gaps",
"directDetail": "gateway controlled{ready}",
"monitoring": "Live receipts",
@@ -11975,6 +11979,8 @@
"logs": "7d alert log",
"logsDetail": "TG {telegram} / KM {km} / PB {playbook}",
"blockers": "Active blockers",
"queue": "AI controlled queue",
"pipeline": "收據流水線",
"noBlockers": "無 active blocker"
},
"recent": "近 24h {count}",

View File

@@ -284,6 +284,10 @@ type TelegramMonitoringCoveragePayload = {
status?: string | null;
active_blockers?: string[] | null;
operator_answer?: {
all_telegram_monitoring_alerts_fully_audited?: boolean | null;
all_telegram_monitoring_alert_surfaces_have_db_or_log_receipt?: boolean | null;
all_telegram_monitoring_alerts_ai_agent_automation_ready?: boolean | null;
ai_controlled_gap_queue_present?: boolean | null;
all_known_telegram_egress_routes_controlled?: boolean | null;
all_known_telegram_egress_routes_have_db_or_log_receipt?: boolean | null;
all_verified_ai_alert_context_receipts_reusable_by_ai_agent?: boolean | null;
@@ -309,8 +313,53 @@ type TelegramMonitoringCoveragePayload = {
source_contract_ready?: boolean | null;
runtime_db_readback_ok?: boolean | null;
ai_alert_card_db_readback_ok?: boolean | null;
runtime_event_type_with_receipts_count?: number | null;
alert_receipt_pipeline_stage_count?: number | null;
alert_receipt_pipeline_ready_count?: number | null;
ai_controlled_gap_queue_count?: number | null;
required_tag_dimension_count?: number | null;
monitoring_asset_config_kind_count?: number | null;
telegram_monitoring_audit_completion_percent?: number | null;
full_coverage_ready?: boolean | null;
} | null;
required_tag_dimensions?: Array<{
dimension?: string | null;
canonical_field?: string | null;
why?: string | null;
}> | null;
monitoring_asset_rollups?: {
by_config_kind?: Record<string, number | null | undefined> | null;
live_receipt_gap_samples?: Array<{
surface_id?: string | null;
label?: string | null;
config_kind?: string | null;
status?: string | null;
}> | null;
} | null;
runtime_event_type_counts_7d?: Record<string, number | null | undefined> | null;
alert_receipt_pipeline?: Array<{
stage_id?: string | null;
ready?: boolean | null;
receipt_count?: number | null;
gap_count?: number | null;
next_action?: string | null;
}> | null;
work_item_progress?: {
completion_percent?: number | null;
completed_check_count?: number | null;
total_check_count?: number | null;
checks?: Array<{
check_id?: string | null;
status?: string | null;
}> | null;
} | null;
ai_controlled_gap_queue?: Array<{
work_item_id?: string | null;
priority?: string | null;
blocker?: string | null;
status?: string | null;
controlled_next_action?: string | null;
}> | null;
operation_boundaries?: {
telegram_send_performed?: boolean | null;
bot_api_call_performed?: boolean | null;
@@ -550,6 +599,12 @@ export function AutonomousRuntimeReceiptPanel({
const telegramCoverageSummary = telegramCoveragePayload?.summary ?? {};
const telegramCoverageAnswers = telegramCoveragePayload?.operator_answer ?? {};
const telegramCoverageBlockers = telegramCoveragePayload?.active_blockers ?? [];
const telegramCoverageProgress = telegramCoveragePayload?.work_item_progress;
const telegramCoveragePipeline = telegramCoveragePayload?.alert_receipt_pipeline ?? [];
const telegramCoverageGapQueue = telegramCoveragePayload?.ai_controlled_gap_queue ?? [];
const telegramCoverageTags = telegramCoveragePayload?.required_tag_dimensions ?? [];
const telegramCoverageAssetKinds = telegramCoveragePayload?.monitoring_asset_rollups?.by_config_kind ?? {};
const telegramCoverageAssetKindNames = Object.keys(telegramCoverageAssetKinds).slice(0, 5);
const consumerReady = consumerRollups.controlled_consumer_readback_ready === true
|| consumerPayload?.status === "controlled_writeback_consumer_readback_ready";
const telegramPostApplyVerifierReady =
@@ -1139,6 +1194,51 @@ export function AutonomousRuntimeReceiptPanel({
? "neutral" as Tone
: telegramMonitoringCoverageReady ? "ok" as Tone : "warn" as Tone,
},
{
key: "completion",
label: t("monitoringCoverage.completion"),
value: !hasTelegramCoverageReadback
? "--"
: `${numberValue(
telegramCoverageProgress?.completion_percent
?? telegramCoverageSummary.telegram_monitoring_audit_completion_percent
?? 0
)}%`,
detail: t("monitoringCoverage.completionDetail", {
ready: readbackNumber(
telegramCoverageSummary.alert_receipt_pipeline_ready_count,
hasTelegramCoverageReadback
),
total: readbackNumber(
telegramCoverageSummary.alert_receipt_pipeline_stage_count,
hasTelegramCoverageReadback
),
}),
icon: Gauge,
tone: !hasTelegramCoverageReadback
? "neutral" as Tone
: telegramMonitoringCoverageReady ? "ok" as Tone : "warn" as Tone,
},
{
key: "tags",
label: t("monitoringCoverage.tags"),
value: readbackNumber(
telegramCoverageSummary.required_tag_dimension_count,
hasTelegramCoverageReadback
),
detail: t("monitoringCoverage.tagsDetail", {
kinds: readbackNumber(
telegramCoverageSummary.monitoring_asset_config_kind_count,
hasTelegramCoverageReadback
),
}),
icon: SlidersHorizontal,
tone: !hasTelegramCoverageReadback
? "neutral" as Tone
: toNumber(telegramCoverageSummary.required_tag_dimension_count) > 0
? "ok" as Tone
: "warn" as Tone,
},
{
key: "direct",
label: t("monitoringCoverage.direct"),
@@ -1245,17 +1345,24 @@ export function AutonomousRuntimeReceiptPanel({
},
{
key: "blockers",
label: t("monitoringCoverage.blockers"),
value: readbackNumber(telegramCoverageBlockers.length, hasTelegramCoverageReadback),
label: t("monitoringCoverage.queue"),
value: readbackNumber(
telegramCoverageSummary.ai_controlled_gap_queue_count
?? telegramCoverageGapQueue.length
?? telegramCoverageBlockers.length,
hasTelegramCoverageReadback
),
detail: !hasTelegramCoverageReadback
? t("states.loading")
: telegramCoverageGapQueue.length > 0
? telegramCoverageGapQueue[0]?.controlled_next_action ?? telegramCoverageGapQueue[0]?.blocker ?? "--"
: telegramCoverageBlockers.length > 0
? telegramCoverageBlockers[0]
: t("monitoringCoverage.noBlockers"),
? telegramCoverageBlockers[0]
: t("monitoringCoverage.noBlockers"),
icon: TriangleAlert,
tone: !hasTelegramCoverageReadback
? "neutral" as Tone
: telegramCoverageBlockers.length > 0 ? "warn" as Tone : "ok" as Tone,
: telegramCoverageBlockers.length > 0 || telegramCoverageGapQueue.length > 0 ? "warn" as Tone : "ok" as Tone,
},
];
const visibleWorkItems = orderedWorkItems.filter((item) => matchesWorkFilter(item, workFilter));
@@ -1498,6 +1605,103 @@ export function AutonomousRuntimeReceiptPanel({
);
})}
</div>
{mode === "full" ? (
<div className="grid gap-px border-t border-[#e0ddd4] bg-[#e0ddd4] lg:grid-cols-3">
<div className="bg-white px-4 py-3">
<div className="flex items-center justify-between gap-3">
<p className="text-xs font-semibold text-[#77736a]">
{t("monitoringCoverage.pipeline")}
</p>
<span className="font-mono text-xs text-[#77736a]">
{readbackRatio(
telegramCoverageSummary.alert_receipt_pipeline_ready_count,
telegramCoverageSummary.alert_receipt_pipeline_stage_count,
hasTelegramCoverageReadback
)}
</span>
</div>
<div className="mt-3 grid gap-2">
{telegramCoveragePipeline.slice(0, 7).map((stage) => (
<div
key={stage.stage_id ?? stage.next_action ?? "stage"}
className="flex items-center justify-between gap-3 border border-[#eee9dd] px-2 py-1.5"
>
<div className="min-w-0 truncate text-xs font-semibold text-[#141413]">
{stage.stage_id ?? "--"}
</div>
<span className={cn(
"shrink-0 border px-2 py-0.5 text-xs font-semibold",
toneClass(stage.ready ? "ok" : "warn")
)}>
{readbackNumber(stage.gap_count, hasTelegramCoverageReadback)}
</span>
</div>
))}
</div>
</div>
<div className="bg-white px-4 py-3">
<div className="flex items-center justify-between gap-3">
<p className="text-xs font-semibold text-[#77736a]">
{t("monitoringCoverage.queue")}
</p>
<span className="font-mono text-xs text-[#77736a]">
{readbackNumber(telegramCoverageGapQueue.length, hasTelegramCoverageReadback)}
</span>
</div>
<div className="mt-3 grid gap-2">
{telegramCoverageGapQueue.length > 0 ? (
telegramCoverageGapQueue.slice(0, 4).map((item) => (
<div
key={item.work_item_id ?? item.blocker ?? "gap"}
className="grid gap-1 border border-[#eee9dd] px-2 py-1.5"
>
<div className="flex min-w-0 items-center justify-between gap-2">
<span className="truncate text-xs font-semibold text-[#141413]">
{item.controlled_next_action ?? item.blocker ?? "--"}
</span>
<span className="font-mono text-xs text-[#77736a]">
{item.priority ?? "--"}
</span>
</div>
<span className="truncate font-mono text-xs text-[#77736a]">
{item.work_item_id ?? item.status ?? "--"}
</span>
</div>
))
) : (
<div className="border border-[#eee9dd] px-2 py-2 text-xs font-semibold text-[#17602a]">
{t("monitoringCoverage.noBlockers")}
</div>
)}
</div>
</div>
<div className="bg-white px-4 py-3">
<div className="flex items-center justify-between gap-3">
<p className="text-xs font-semibold text-[#77736a]">
{t("monitoringCoverage.tags")}
</p>
<span className="font-mono text-xs text-[#77736a]">
{readbackNumber(telegramCoverageTags.length, hasTelegramCoverageReadback)}
</span>
</div>
<div className="mt-3 flex flex-wrap gap-2">
{telegramCoverageTags.slice(0, 12).map((tag) => (
<span
key={tag.dimension ?? tag.canonical_field ?? "tag"}
className="border border-[#eee9dd] px-2 py-1 font-mono text-xs text-[#141413]"
>
{tag.dimension}
</span>
))}
</div>
<p className="mt-3 truncate text-xs leading-5 text-[#5f5b52]">
{telegramCoverageAssetKindNames.length > 0
? telegramCoverageAssetKindNames.join(" / ")
: t("states.loading")}
</p>
</div>
</div>
) : null}
</div>
</div>

View File

@@ -53989,3 +53989,24 @@ production browser smoke:
**下一步**
- 跑 web build、UI density、runner pressure guard 與 Knowledge Base desktop / mobile smoke通過後 commit / push 到 Gitea main等 deploy marker 再驗證正式頁面顯示新的資產分類與主知識 API fail-soft。
## 2026-07-03 — Telegram 監控告警 AI automation coverage 可視化
**完成內容**
- `/api/v1/agents/telegram-alert-monitoring-coverage-readback` 從單純 gap summary 升級為可操作 readback新增 required tag dimensionsproject / product / website / service / package / tool / log / alert / playbook / rag / mcp / schedule、monitoring asset rollups、runtime event type counts、alert receipt pipeline、work item progress 與 AI controlled gap queue。
- operator answer 現在可直接回答:是否完成 Telegram 監控告警全量清查、是否都有 DB/log receipt、是否已可被 AI Agent automation 使用、是否仍有 AI controlled gap queue。
- AwoooP 共用 `AutonomousRuntimeReceiptPanel` 已接入新欄位完整模式顯示整體完成度、收據流水線、AI controlled queue 與貼標維度Runs / Work Items / Alerts / Approvals 共用同一 readback surface。
- 保持 metadata-only 邊界:沒有送 Telegram、沒有呼叫 Bot API、沒有改 receiver、沒有保存 raw payload、沒有讀 secret、沒有 workflow dispatch。
**本地驗證結果**
- `DATABASE_URL=postgresql+asyncpg://test:test@localhost/test PYTHONPATH=apps/api python3.11 -m pytest apps/api/tests/test_telegram_alert_monitoring_coverage_readback_api.py apps/api/tests/test_telegram_alert_ai_automation_matrix_api.py apps/api/tests/test_ai_agent_log_controlled_writeback_consumer_readback_api.py -q -p no:cacheprovider``11 passed`
- `pnpm --dir apps/web typecheck``NEXT_PUBLIC_API_URL=https://awoooi.wooo.work pnpm --dir apps/web build`、i18n JSON parse、`pnpm --dir apps/web audit:ui-density``python3.11 ops/runner/guard-gitea-runner-pressure.py --root .``git diff --check`:通過。
- UI density guard 仍指出既有大型債務:`:locale/iwooos``:locale/awooop/work-items``:locale/awooop/runs` 仍是下一批 UI/UX 主線壓縮對象。
**仍維持**
- 沒有讀 secret / token / `.env` / raw sessions / SQLite / auth。
- 沒有使用 GitHub / gh / GitHub API / GitHub Actions。
- 沒有重啟主機,沒有 Docker / Nginx / K3s / DB / firewall restart沒有 workflow_dispatch沒有 DROP / TRUNCATE / restore / prune沒有送 Telegram 測試訊息。
**下一步**
- commit / push 到 Gitea main等 deploy marker 後讀回 `/api/v1/agents/telegram-alert-monitoring-coverage-readback`,並以 Playwright smoke 驗證 `/zh-TW/awooop`、Runs、Work Items、Alerts、Approvals 顯示 completion / pipeline / queue / tag dimensions。