Merge remote-tracking branch 'gitea/main' into codex/p0-006-ai-log-triage-20260709

This commit is contained in:
ogt
2026-07-09 23:22:09 +08:00
15 changed files with 1557 additions and 99 deletions

View File

@@ -1426,6 +1426,183 @@ def test_runtime_receipt_recovery_flags_zero_live_log_events():
assert readback["work_item_progress"]["rollups"]["not_started_count"] == 10
def test_trace_ledger_uses_controlled_apply_chain_for_auto_repair_receipt_gap():
readback = build_runtime_receipt_readback_from_rows(
project_id="awoooi",
db_read_status="ok",
operation_count_rows=[
{"operation_type": "ansible_candidate_matched", "total": 1, "recent": 1},
{"operation_type": "ansible_check_mode_executed", "total": 1, "recent": 1},
{"operation_type": "ansible_apply_executed", "total": 1, "recent": 1},
],
verifier_count_rows=[
{"verification_result": "success", "total": 1, "recent": 1},
],
km_count_rows=[
{"status": "review", "total": 1, "recent": 1},
],
telegram_count_rows=[
{"send_status": "sent", "total": 1, "recent": 1},
],
)
trace = readback["trace_ledger"]
auto_repair_stage = next(
stage
for stage in trace["stages"]
if stage["stage_id"] == "auto_repair_execution_receipt"
)
assert auto_repair_stage["recorded"] is True
assert auto_repair_stage["record_quality"] == (
"controlled_apply_receipt_chain_fallback"
)
assert "auto_repair_execution_receipt" not in trace["missing_required_stage_ids"]
progress_items = {
item["work_item_id"]: item
for item in readback["work_item_progress"]["ordered_items"]
}
assert progress_items["P0-B-trace-ledger"]["status"] == "completed"
auto_repair_source = next(
item
for item in readback["log_integration_taxonomy"]["source_families"]
if item["source_family_id"] == "auto_repair_receipts"
)
assert auto_repair_source["total"] == 1
assert auto_repair_source["record_quality"] == (
"controlled_apply_receipt_chain_fallback"
)
def test_consumer_receipts_close_taxonomy_decision_and_learning_gaps():
readback = build_runtime_receipt_readback_from_rows(
project_id="awoooi",
db_read_status="ok",
operation_count_rows=[
{
"operation_type": "ansible_candidate_matched",
"status": "success",
"total": 2013,
"recent": 12,
},
{
"operation_type": "ansible_check_mode_executed",
"status": "success",
"total": 2013,
"recent": 12,
},
{
"operation_type": "ansible_apply_executed",
"status": "success",
"total": 161,
"recent": 0,
},
{
"operation_type": "ansible_learning_writeback_recorded",
"status": "success",
"total": 122,
"recent": 0,
},
],
operation_latest_rows=[],
auto_repair_count_rows=[],
verifier_count_rows=[
{"verification_result": "success", "total": 161, "recent": 0},
],
km_count_rows=[
{"status": "review", "total": 161, "recent": 0},
],
telegram_count_rows=[
{"send_status": "sent", "total": 300, "recent": 16},
],
executor_log_count_rows=[
{"status": "success", "total": 2141, "recent": 12},
],
log_controlled_writeback_consumer=_log_controlled_writeback_consumer_readback(),
)
assert readback["latest_flow_closure"]["closed"] is False
assert readback["latest_failure_classification"]["classification"] == (
"no_controlled_apply_observed"
)
taxonomy = readback["log_integration_taxonomy"]
assert taxonomy["rollups"]["source_family_count"] == 10
assert taxonomy["rollups"]["active_source_family_count"] == 10
assert taxonomy["rollups"]["inactive_source_family_count"] == 0
quality_by_source = {
item["source_family_id"]: item.get("record_quality")
for item in taxonomy["source_families"]
}
assert quality_by_source["mcp_gateway_tool_calls"] == (
"controlled_consumer_context_fallback"
)
assert quality_by_source["service_package_logs"] == (
"controlled_metadata_receipt_fallback"
)
assert quality_by_source["playbook_trust_signals"] == (
"controlled_consumer_context_fallback"
)
assert quality_by_source["operator_timeline_projection"] == (
"controlled_consumer_context_fallback"
)
decision_wiring = readback["agent_decision_wiring"]
assert decision_wiring["status"] == "completed"
assert decision_wiring["missing_required_stage_ids"] == []
learning_loop = readback["learning_loop"]
assert learning_loop["status"] == "completed"
assert learning_loop["missing_required_stage_ids"] == []
assert learning_loop["rollups"]["playbook_trust_total"] == 1
assert learning_loop["rollups"]["repair_feedback_ready_count"] == 1
assert learning_loop["rollups"]["next_decision_ready_count"] == 1
learning_quality = {
item["stage_id"]: item.get("record_quality")
for item in learning_loop["stages"]
}
assert learning_quality["verified_execution_outcome"] == (
"aggregate_verifier_receipt_fallback"
)
assert learning_quality["km_learning_writeback"] == "aggregate_km_receipt_fallback"
assert learning_quality["playbook_trust_delta"] == (
"controlled_consumer_context_fallback"
)
progress_items = {
item["work_item_id"]: item
for item in readback["work_item_progress"]["ordered_items"]
}
assert progress_items["P1-A-ingestion-coverage"]["status"] == "completed"
assert progress_items["P1-B-agent-decision-wiring"]["status"] == "completed"
assert progress_items["P1-C-learning-loop"]["status"] == "completed"
alert_noise = readback["alert_noise_reduction"]
assert alert_noise["status"] == "completed"
assert alert_noise["missing_required_stage_ids"] == []
assert alert_noise["rollups"]["raw_alert_received_total"] == 0
assert alert_noise["rollups"]["raw_suppressed_alert_total"] == 0
assert alert_noise["rollups"]["controlled_alert_receipt_chain_fallback_total"] == 1
alert_quality = {
item["stage_id"]: item.get("record_quality")
for item in alert_noise["stages"]
}
assert alert_quality["alert_intake_receipts"] == (
"controlled_alert_receipt_chain_fallback"
)
assert alert_quality["duplicate_convergence"] == (
"controlled_alert_receipt_chain_fallback"
)
assert alert_quality["notification_suppression"] == (
"controlled_alert_receipt_chain_fallback"
)
assert alert_quality["learning_feedback"] == (
"controlled_alert_receipt_chain_fallback"
)
assert progress_items["P1-D-alert-noise-reduction"]["status"] == "completed"
def test_runtime_receipt_recovery_keeps_partial_live_log_events_visible():
readback = build_runtime_receipt_readback_from_rows(
project_id="awoooi",

View File

@@ -304,6 +304,57 @@ async def test_log_controlled_writeback_consumer_loader_uses_direct_fallback(
assert payload["operation_boundaries"]["metadata_ledger_read_performed"] is True
@pytest.mark.asyncio
async def test_log_controlled_writeback_consumer_maps_ready_bindings_when_alert_registry_times_out(
monkeypatch,
):
fake_db = _FakeDb(_ledger_rows(), _consumer_receipt_rows())
monkeypatch.setattr(
consumer_module,
"get_db_context",
lambda project_id: _FakeContext(fake_db),
)
async def alert_registry_timeout(**_kwargs):
raise TimeoutError("alert registry unavailable")
monkeypatch.setattr(
consumer_module,
"list_ai_alert_card_delivery_readback",
alert_registry_timeout,
)
payload = await load_latest_ai_agent_log_controlled_writeback_consumer_readback()
assert payload["status"] == "controlled_writeback_consumer_readback_ready"
assert payload["rollups"]["telegram_alert_learning_context_readback_ready"] is True
assert payload["rollups"]["telegram_alert_learning_context_receipt_count"] == 6
assert (
payload["rollups"]["telegram_alert_learning_ai_agent_context_receipt_count"]
== 1
)
context = payload["telegram_alert_learning_context"]
assert context["status"] == "ai_loop_agent_context_receipt_ready"
assert context["source_registry_status"] == (
"fallback_log_controlled_writeback_consumer_context"
)
assert context["active_blockers"] == []
assert {receipt["target"] for receipt in context["context_receipts"]} == {
"km",
"rag",
"playbook",
"mcp",
"verifier",
"ai_agent",
}
assert all(
receipt["status"] == "ready_for_ai_loop_context"
for receipt in context["context_receipts"]
)
assert context["operation_boundaries"]["runtime_target_write_performed"] is True
assert context["operation_boundaries"]["raw_payload_included"] is False
def test_log_controlled_writeback_consumer_direct_rows_parse_json_text():
dispatch_row = _ledger_rows()[0]
consumer_row = _consumer_receipt_rows()[0]

View File

@@ -1,5 +1,7 @@
from pathlib import Path
from src.services.platform_operator_service import _provider_event_id_prefix_bounds
ROOT = Path(__file__).resolve().parents[1]
MIGRATION = "awooop_conversation_event_hot_path_indexes_2026-07-01.sql"
ROLLBACK = "awooop_conversation_event_hot_path_indexes_2026-07-01_down.sql"
@@ -57,7 +59,9 @@ def test_provider_recent_indexes_match_live_hot_queries() -> None:
assert "COALESCE(NULLIF(source_envelope->>'provider', '')" in coverage_source
assert "ORDER BY received_at DESC" in coverage_source
assert "LOWER(COALESCE(NULLIF(source_envelope->>'provider', '')" in operator_source
assert "_provider_event_id_prefix_bounds" in operator_source
assert "provider_event_id >= prefix_lower" in operator_source
assert "provider_event_id < prefix_upper" in operator_source
assert "idx_awooop_conv_event_project_provider_recent" in sql
assert "idx_awooop_conv_event_project_provider_lower_recent" in sql
@@ -67,6 +71,18 @@ def test_provider_recent_indexes_match_live_hot_queries() -> None:
assert "event_id DESC" in sql
def test_recent_provider_prefix_uses_btree_range_bounds() -> None:
assert _provider_event_id_prefix_bounds("alertmanager") == (
"alertmanager:",
"alertmanager;",
)
assert _provider_event_id_prefix_bounds("alertmanager:received") == (
"alertmanager:received",
"alertmanager:receivee",
)
assert _provider_event_id_prefix_bounds(" SignOz ") == ("signoz:", "signoz;")
def test_truth_chain_source_lookup_indexes_match_or_terms() -> None:
sql = _migration_sql()
truth_chain_source = _read("src/services/awooop_truth_chain_service.py")

View File

@@ -21,6 +21,8 @@ from src.services.ollama_failover_manager import OllamaEndpoint, OllamaRoutingRe
from src.services.ollama_health_monitor import HealthReport, HealthStatus
from src.services.platform_operator_service import (
_RUN_CONTEXT_QUERY_CHUNK_SIZE,
_ai_alert_card_delivery_item,
_ai_alert_card_delivery_summary_from_row,
_ai_route_health_map,
_ai_route_lane_state,
_ai_route_policy_order,
@@ -32,8 +34,6 @@ from src.services.platform_operator_service import (
_cicd_duration_seconds,
_cicd_event_item_from_row,
_collect_run_incident_ids,
_ai_alert_card_delivery_item,
_ai_alert_card_delivery_summary_from_row,
_is_source_correlation_applied_link,
_iter_run_context_batches,
_legacy_mcp_timeline_status,
@@ -46,8 +46,8 @@ from src.services.platform_operator_service import (
_recent_event_source_summary,
_remediation_summary_matches_incident_id,
_remediation_summary_matches_status,
_repair_candidate_projection_from_metadata,
_remediation_timeline_summary,
_repair_candidate_projection_from_metadata,
_run_callback_reply_summary,
_run_remediation_list_summary,
_score_source_correlation_event,
@@ -1253,6 +1253,204 @@ def test_list_ai_alert_cards_response_preserves_delivery_metadata() -> None:
assert dumped["summary"]["production_write_count"] == 0
def test_ai_alert_card_source_unavailable_response_exposes_ai_repair_queue() -> None:
response = ListAiAlertCardsResponse.model_validate(
platform_operator_service._ai_alert_card_delivery_source_unavailable_response(
project_id="awoooi",
event_type=None,
lane=None,
page=1,
per_page=6,
error_type="ProgrammingError",
)
)
dumped = response.model_dump(mode="json")
assert dumped["total"] == 0
assert dumped["summary"]["status"] == (
"source_unavailable_ai_controlled_repair_required"
)
assert dumped["summary"]["db_read_status"] == "unavailable"
assert dumped["summary"]["controlled_repair_queue_present"] is True
assert dumped["summary"]["learning_registry_target_count"] == 6
assert dumped["summary"]["learning_registry_missing_target_count"] == 6
assert dumped["summary"]["source_unavailable_next_action"] == (
"repair_awooop_ai_alert_card_delivery_readback"
)
assert dumped["learning_registry"]["status"] == (
"source_unavailable_ai_controlled_repair_required"
)
assert dumped["learning_registry"]["missing_target_count"] == 6
assert {target["target"] for target in dumped["learning_registry"]["targets"]} == {
"km",
"playbook",
"rag",
"mcp",
"verifier",
"ai_agent",
}
assert all(
target["status"]
== "blocked_by_ai_alert_card_delivery_readback_unavailable"
for target in dumped["learning_registry"]["targets"]
)
assert dumped["learning_registry"]["operation_boundaries"]["secret_value_read"] is False
assert dumped["learning_registry"]["operation_boundaries"]["github_api_used"] is False
@pytest.mark.asyncio
async def test_list_ai_alert_card_delivery_readback_failsofts_db_unavailable(
monkeypatch: pytest.MonkeyPatch,
) -> None:
class FailingDbContext:
async def __aenter__(self):
raise RuntimeError("db unavailable")
async def __aexit__(self, exc_type, exc, tb):
return False
monkeypatch.setattr(
platform_operator_service,
"get_db_context",
lambda _project_id: FailingDbContext(),
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
page=1,
per_page=6,
refresh=True,
)
assert payload["total"] == 0
assert payload["summary"]["db_read_status"] == "unavailable"
assert payload["summary"]["controlled_repair_queue_present"] is True
assert payload["learning_registry"]["missing_target_count"] == 6
@pytest.mark.asyncio
async def test_list_ai_alert_card_delivery_readback_failsofts_slow_db(
monkeypatch: pytest.MonkeyPatch,
) -> None:
class SlowDb:
async def execute(self, *_args, **_kwargs):
await asyncio.sleep(1)
class SlowDbContext:
async def __aenter__(self):
return SlowDb()
async def __aexit__(self, exc_type, exc, tb):
return False
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_QUERY_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_DB_CONTEXT_EXIT_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"get_db_context",
lambda _project_id: SlowDbContext(),
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
page=1,
per_page=6,
refresh=True,
)
assert payload["summary"]["db_read_status"] == "unavailable"
assert payload["summary"]["source_unavailable_reason"] == "TimeoutError"
assert payload["summary"]["learning_registry_next_action"] == (
"repair_awooop_ai_alert_card_delivery_readback_then_consume_learning_registry"
)
@pytest.mark.asyncio
async def test_list_ai_alert_card_delivery_readback_uses_cache_after_live_timeout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
class SlowDb:
async def execute(self, *_args, **_kwargs):
await asyncio.sleep(1)
class SlowDbContext:
async def __aenter__(self):
return SlowDb()
async def __aexit__(self, exc_type, exc, tb):
return False
cached_response = (
platform_operator_service._ai_alert_card_delivery_source_unavailable_response(
project_id="awoooi",
event_type=None,
lane=None,
page=1,
per_page=6,
error_type="seed",
)
)
cached_response["summary"].update({
"status": "observed",
"db_read_status": "ok",
"total": 36,
"learning_registry_missing_target_count": 0,
})
cached_response["learning_registry"].update({
"status": "learning_registry_ready",
"ready_target_count": 6,
"missing_target_count": 0,
})
async def cached_summary(*_args, **_kwargs):
return cached_response
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_QUERY_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"_AI_ALERT_CARD_DB_CONTEXT_EXIT_TIMEOUT_SECONDS",
0.01,
)
monkeypatch.setattr(
platform_operator_service,
"get_db_context",
lambda _project_id: SlowDbContext(),
)
monkeypatch.setattr(
platform_operator_service,
"get_cached_operator_summary_async",
cached_summary,
)
payload = await platform_operator_service.list_ai_alert_card_delivery_readback(
project_id="awoooi",
page=1,
per_page=6,
refresh=True,
)
assert payload["summary"]["status"] == "observed"
assert payload["summary"]["db_read_status"] == "cached_after_live_timeout"
assert payload["summary"]["source_unavailable_reason"] == "TimeoutError"
assert payload["summary"]["live_refresh_status"] == (
"live_refresh_unavailable_serving_cached_readback"
)
assert payload["learning_registry"]["status"] == "learning_registry_ready"
assert payload["learning_registry"]["missing_target_count"] == 0
def test_list_callback_replies_keeps_audit_summary_separate_from_km_summary() -> None:
source = inspect.getsource(platform_operator_service.list_callback_replies)

View File

@@ -71,6 +71,9 @@ def test_iwooos_security_operating_system_readback_exposes_api_validator() -> No
assert payload["summary"]["operation_packet_validator_available_count"] == 1
assert payload["summary"]["operation_packet_required_field_count"] == 24
assert payload["summary"]["wazuh_registry_accepted_count"] == 6
assert payload["summary"]["alert_receipt_accepted_count"] == 0
assert payload["summary"]["alert_receipt_observed_count"] == 0
assert payload["summary"]["alert_receipt_source_status"] == "not_connected"
assert payload["summary"]["runtime_gate_count"] == 0
assert payload["operation_packet_validation_endpoint"] == (
"/api/v1/iwooos/security-operating-system/validate-operation-packet"
@@ -87,6 +90,26 @@ def test_iwooos_security_operating_system_readback_exposes_api_validator() -> No
)
def test_iwooos_security_operating_system_accepts_alert_receipt_readback() -> None:
payload = load_latest_iwooos_security_operating_system(
alert_receipt_readback={
"events": [{"provider_event_id": "alertmanager:received:alert-a"}],
"total": 1,
"limit": 20,
"source_status": "ready",
}
)
assert payload["summary"]["alert_receipt_accepted_count"] == 1
assert payload["summary"]["alert_receipt_observed_count"] == 1
assert payload["summary"]["alert_receipt_source_status"] == "ready"
assert payload["summary"]["runtime_gate_count"] == 0
assert any(
marker == "iwooos_security_operating_system_alert_receipt_accepted_count=1"
for marker in payload["boundary_markers"]
)
def test_iwooos_security_operating_system_api_is_public_safe() -> None:
response = _client().get("/api/v1/iwooos/security-operating-system")
@@ -103,6 +126,31 @@ def test_iwooos_security_operating_system_api_is_public_safe() -> None:
assert "WAZUH_API_PASSWORD" not in response.text
def test_iwooos_security_operating_system_api_counts_alertmanager_receipts(
monkeypatch,
) -> None:
async def fake_recent_events(**_: object) -> dict[str, object]:
return {
"events": [{"provider_event_id": "alertmanager:received:alert-a"}],
"total": 1,
"limit": 20,
}
monkeypatch.setattr(
"src.api.v1.iwooos.list_recent_channel_events",
fake_recent_events,
)
response = _client().get("/api/v1/iwooos/security-operating-system")
assert response.status_code == 200
data = response.json()
assert data["summary"]["alert_receipt_accepted_count"] == 1
assert data["summary"]["alert_receipt_observed_count"] == 1
assert data["summary"]["alert_receipt_source_status"] == "ready"
assert data["summary"]["runtime_gate_count"] == 0
def test_iwooos_security_operation_packet_validator_accepts_redacted_loop() -> None:
payload = validate_iwooos_security_operation_packet(_valid_operation_packet())

View File

@@ -9,6 +9,7 @@ from fastapi.testclient import TestClient
from src.api.v1 import agents
from src.api.v1.agents import router
from src.services import platform_operator_service
from src.services import telegram_alert_monitoring_coverage_readback as coverage_service
from src.services.telegram_alert_monitoring_coverage_readback import (
_inspect_source_contract,
@@ -353,6 +354,38 @@ async def test_telegram_alert_monitoring_live_readbacks_are_bounded(
assert runtime_log["event_type_counts_7d"] == {}
@pytest.mark.asyncio
async def test_telegram_alert_monitoring_marks_ai_alert_cards_source_unavailable(
monkeypatch: pytest.MonkeyPatch,
):
async def source_unavailable_ai_alert_cards(**_kwargs):
return (
platform_operator_service
._ai_alert_card_delivery_source_unavailable_response(
project_id="awoooi",
event_type=None,
lane=None,
page=1,
per_page=6,
error_type="ProgrammingError",
)
)
monkeypatch.setattr(
coverage_service,
"list_ai_alert_card_delivery_readback",
source_unavailable_ai_alert_cards,
)
ai_cards = await coverage_service._load_ai_alert_card_delivery(project_id="awoooi")
assert ai_cards["status"] == "unavailable"
assert ai_cards["summary"]["db_read_status"] == "unavailable"
assert ai_cards["learning_registry"]["status"] == (
"source_unavailable_ai_controlled_repair_required"
)
def test_telegram_alert_monitoring_coverage_source_contract(tmp_path):
(tmp_path / "apps/api/src/api/v1").mkdir(parents=True)
(tmp_path / "apps/api/src/repositories").mkdir(parents=True)