from __future__ import annotations from uuid import UUID import pytest from fastapi import HTTPException from src.api.v1.platform.events import ChannelEventRecurrenceResponse from src.services import channel_event_dossier_service from src.services.channel_event_dossier_service import ( RecurrenceWorkItemNotFoundError, build_dossier_coverage, build_dossier_event, build_dossier_recurrence, build_recurrence_work_item_dry_run, build_recurrence_work_item_handoff, build_recurrence_work_item_preview, build_source_correlation_apply, build_source_correlation_review_decision, fetch_channel_event_dossier, fetch_channel_event_dossier_coverage, fetch_channel_event_dossier_recurrence, ) def test_build_dossier_event_summarizes_source_envelope() -> None: event = build_dossier_event( { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "h" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue redacted", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_url": "https://sentry.example.invalid/issues/issue-1", "content_sha256": "a" * 64, "content_length": 42, "source_refs": { "event_ids": ["issue-1"], "sentry_issue_ids": ["issue-1", "sentry:received:issue-1"], "fingerprints": ["sentry-issue-1"], }, "log_correlation": { "alertname": "Sentry Issue", "severity": "error", "namespace": "sentry", "target_resource": "frontend", "fingerprint": "sentry-issue-1", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:46:00", } ) assert event["provider"] == "sentry" assert event["stage"] == "received" assert event["alertname"] == "Sentry Issue" assert event["severity"] == "error" assert event["source_ref_count"] == 4 assert event["has_redacted_content"] is True assert event["content_sha256"] == "a" * 64 def test_build_dossier_coverage_summarizes_recent_sources() -> None: coverage = build_dossier_coverage( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "h" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue redacted", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": { "sentry_issue_ids": ["issue-1"], "alert_ids": ["sentry:received:issue-1"], "fingerprints": ["fingerprint-1"], }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:46:00", }, { "event_id": "event-2", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "signoz:received:alert-1", "content_hash": "i" * 64, "content_preview": "SignOz alert", "content_redacted": None, "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "signoz", "stage": "received", "source_refs": { "signoz_alerts": ["alert-1"], "alert_ids": ["signoz:received:alert-1"], }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-13T13:45:00", }, { "event_id": "event-3", "project_id": "awoooi", "channel_type": "telegram", "provider_event_id": "telegram:callback:1", "content_hash": None, "content_preview": "Callback", "content_redacted": None, "redaction_version": "audit_sink_v1", "source_envelope": {}, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:44:00", }, ], project_id="awoooi", limit=100, ) assert coverage["project_id"] == "awoooi" assert coverage["summary"]["source_count"] == 3 assert coverage["summary"]["source_envelope_total"] == 2 assert coverage["summary"]["missing_source_envelope_total"] == 1 assert coverage["summary"]["with_source_refs_total"] == 2 assert coverage["summary"]["missing_source_refs_total"] == 1 assert coverage["summary"]["duplicate_total"] == 1 assert coverage["summary"]["redacted_total"] == 1 assert coverage["summary"]["sentry_ref_total"] == 1 assert coverage["summary"]["signoz_ref_total"] == 1 assert coverage["summary"]["alert_ref_total"] == 2 assert coverage["providers"][0]["provider"] == "sentry" def test_build_dossier_recurrence_groups_events_and_run_state() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-2", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:2", "content_hash": "b" * 64, "content_preview": "Host disk pressure", "content_redacted": "Host disk pressure", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-2"], "incident_ids": ["INC-20260513-ABCD"], "fingerprints": ["fp-host-disk"], }, "log_correlation": { "alertname": "HostDiskUsageHigh", "severity": "warning", "namespace": "node", "target_resource": "host-110", "fingerprint": "fp-host-disk", }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-13T13:47:00", "run_id": UUID("11111111-1111-4111-8111-111111111111"), "run_state": "waiting_approval", "run_agent_id": "openclaw", }, { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:1", "content_hash": "a" * 64, "content_preview": "Host disk pressure", "content_redacted": "Host disk pressure", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-1"], "incident_ids": ["INC-20260513-ABCD"], "fingerprints": ["fp-host-disk"], }, "log_correlation": { "alertname": "HostDiskUsageHigh", "severity": "warning", "namespace": "node", "target_resource": "host-110", "fingerprint": "fp-host-disk", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:46:00", "run_id": UUID("22222222-2222-4222-8222-222222222222"), "run_state": "completed", "run_agent_id": "openclaw", }, { "event_id": "event-3", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "c" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "source_refs": {"sentry_issue_ids": ["issue-1"]}, "log_correlation": {"alertname": "Sentry Issue"}, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-13T13:45:00", "run_id": None, "run_state": None, "run_agent_id": None, }, ], project_id="awoooi", limit=100, repair_summaries_by_incident={ "INC-20260513-ABCD": { "schema_version": "awooop_recurrence_repair_summary_v1", "incident_id": "INC-20260513-ABCD", "latest_auto_repair_id": "repair-1", "latest_playbook_id": "playbook-1", "latest_playbook_name": "Restart workload", "latest_success": False, "latest_error_message_preview": "verifier failed", "latest_triggered_by": "auto_repair", "latest_risk_level": "low", "latest_execution_time_ms": 1200, "latest_auto_repair_at": "2026-05-13T13:48:00", "latest_verification_result": "failed", "latest_verification_at": "2026-05-13T13:49:00", "auto_repair_total": 1, "success_total": 0, "failed_total": 1, }, }, ) assert recurrence["summary"]["source_event_total"] == 3 assert recurrence["summary"]["recurrence_group_total"] == 2 assert recurrence["summary"]["recurrent_group_total"] == 1 assert recurrence["summary"]["duplicate_event_total"] == 1 assert recurrence["summary"]["linked_run_total"] == 2 assert recurrence["summary"]["unlinked_event_total"] == 1 assert recurrence["summary"]["auto_repair_linked_total"] == 1 assert recurrence["summary"]["open_work_item_group_total"] == 2 assert recurrence["summary"]["verified_repair_group_total"] == 0 assert recurrence["summary"]["automation_gap_group_total"] == 0 assert recurrence["summary"]["failed_repair_group_total"] == 1 assert recurrence["summary"]["source_correlation_review_group_total"] == 1 assert recurrence["summary"]["source_correlation_decision_recorded_group_total"] == 0 host_group = recurrence["items"][0] assert host_group["recurrence_key"] == "fingerprint:fp-host-disk" assert host_group["occurrence_total"] == 2 assert host_group["duplicate_total"] == 1 assert host_group["linked_run_total"] == 2 assert host_group["latest_run_state"] == "waiting_approval" assert host_group["latest_incident_id"] == "INC-20260513-ABCD" assert host_group["incident_ids"] == ["INC-20260513-ABCD"] assert host_group["latest_stage"] == "received" assert host_group["stage_counts"] == {"received": 2} assert host_group["run_state_counts"] == {"waiting_approval": 1, "completed": 1} assert host_group["alert_ref_total"] == 2 assert host_group["repair_summary"]["status"] == "auto_repair_failed" assert host_group["repair_summary"]["latest_auto_repair_id"] == "repair-1" assert host_group["work_item"] == { "schema_version": "awooop_recurrence_work_item_link_v1", "work_item_id": "verification:INC-20260513-ABCD:repair-1", "incident_id": "INC-20260513-ABCD", "matched_incident_id": None, "auto_repair_id": "repair-1", "status": "open", "kind": "verification", "next_step": "triage_failed_repair", "reason": "auto_repair_failed", "needs_human": True, } source_group = recurrence["items"][1] assert source_group["provider"] == "sentry" assert source_group["latest_stage"] == "received" assert source_group["stage_counts"] == {"received": 1} assert source_group["repair_summary"]["status"] == "source_correlation_review" assert source_group["work_item"] == { "schema_version": "awooop_recurrence_work_item_link_v1", "work_item_id": "source-evidence:sentry:received:issue-1", "incident_id": None, "matched_incident_id": None, "auto_repair_id": None, "status": "open", "kind": "source_correlation_review", "next_step": "review_provider_source_match", "reason": "provider_native_evidence_unlinked", "needs_human": True, } def test_build_recurrence_work_item_preview_allows_source_correlation_review() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "signoz:upstream_canary:canary-1", "content_hash": "a" * 64, "content_preview": "SignOz upstream canary", "content_redacted": "SignOz upstream canary", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "signoz", "stage": "upstream_canary", "source_refs": { "signoz_alerts": ["alert-1"], "alert_ids": ["signoz:upstream_canary:canary-1"], }, "log_correlation": { "alertname": "Source Provider Upstream Canary", "severity": "info", "namespace": "observability", "target_resource": "signoz", "fingerprint": "fp-signoz-canary", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:01:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, ) item = recurrence["items"][0] work_item_id = "source-evidence:signoz:upstream_canary:canary-1" assert recurrence["summary"]["source_correlation_review_group_total"] == 1 assert recurrence["summary"]["source_correlation_decision_recorded_group_total"] == 0 assert recurrence["summary"]["open_work_item_group_total"] == 1 assert item["latest_stage"] == "upstream_canary" assert item["repair_summary"]["status"] == "source_correlation_review" assert item["work_item"]["work_item_id"] == work_item_id assert item["work_item"]["kind"] == "source_correlation_review" assert item["work_item"]["next_step"] == "review_provider_source_match" preview = build_recurrence_work_item_preview( recurrence, work_item_id=work_item_id, ) assert preview["mode"] == "observe" assert preview["allowed"] is True assert preview["plan"]["target_action"] == "review_provider_source_match" assert preview["plan"]["target"]["latest_stage"] == "upstream_canary" dry_run = build_recurrence_work_item_dry_run( recurrence, work_item_id=work_item_id, ) assert dry_run["verification_result_preview"] == "observe_only" assert dry_run["ticket_preview"]["would_create"] is False assert "Source evidence review" in dry_run["ticket_preview"]["title"] assert dry_run["current_state_summary"]["latest_stage"] == "upstream_canary" assert dry_run["current_state_summary"]["latest_provider_event_id"] == ( "signoz:upstream_canary:canary-1" ) def test_source_correlation_review_decision_records_accepted_audit_contract() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "a" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": { "sentry_issue_ids": ["issue-1"], "alert_ids": ["sentry:received:issue-1"], }, "log_correlation": { "alertname": "Sentry Issue", "severity": "error", "namespace": "awoooi-prod", "target_resource": "web", "fingerprint": "fp-sentry-issue-1", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:10:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, ) decision = build_source_correlation_review_decision( recurrence, work_item_id="source-evidence:sentry:received:issue-1", decision="accepted", target_incident_id="INC-20260520-ABC123", reviewer_id="operator_console", operator_note="matches current frontend incident", ) assert decision["schema_version"] == "awooop_source_correlation_review_decision_v1" assert decision["allowed"] is True assert decision["executed"] is True assert decision["decision"] == "accepted" assert decision["review_status"] == "accepted" assert decision["target_incident_id"] == "INC-20260520-ABC123" assert decision["writes_incident_state"] is False assert decision["writes_source_event"] is False assert decision["writes_auto_repair_result"] is False assert decision["writes_ticket"] is False assert decision["creates_external_ticket"] is False assert decision["plan"]["step"] == "record_source_correlation_review_decision" assert decision["plan"]["writes"] == ["alert_operation_log", "timeline_events"] assert decision["next_step"] == "verify_source_match_in_status_chain" def test_build_dossier_recurrence_closes_source_review_after_decision() -> None: decision = { "schema_version": "awooop_source_correlation_review_decision_v1", "review_id": "review-1", "work_item_id": "source-evidence:sentry:received:issue-1", "decision": "accepted", "review_status": "accepted", "target_incident_id": "INC-20260520-ABC123", "reviewer_id": "operator_console", "latest_provider_event_id": "sentry:received:issue-1", "recorded_at": "2026-05-20T13:11:00", } recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "a" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": {"sentry_issue_ids": ["issue-1"]}, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:10:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, source_review_decisions_by_work_item={ "source-evidence:sentry:received:issue-1": decision, }, ) item = recurrence["items"][0] assert recurrence["summary"]["open_work_item_group_total"] == 0 assert recurrence["summary"]["source_correlation_review_group_total"] == 0 assert recurrence["summary"]["source_correlation_decision_recorded_group_total"] == 1 assert item["source_correlation_review"] == decision assert item["repair_summary"]["status"] == "source_correlation_accepted" assert item["work_item"]["status"] == "closed" assert item["work_item"]["matched_incident_id"] == "INC-20260520-ABC123" assert item["work_item"]["next_step"] == "verify_source_match_in_status_chain" def test_source_correlation_apply_requires_accepted_review_and_plans_source_link() -> None: accepted_decision = { "schema_version": "awooop_source_correlation_review_decision_v1", "review_id": "review-1", "work_item_id": "source-evidence:sentry:received:issue-1", "decision": "accepted", "review_status": "accepted", "target_incident_id": "INC-20260520-ABC123", "reviewer_id": "operator_console", "latest_provider_event_id": "sentry:received:issue-1", "recorded_at": "2026-05-20T13:11:00", } recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "a" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": { "sentry_issue_ids": ["issue-1"], "alert_ids": ["sentry:received:issue-1"], }, "log_correlation": { "alertname": "Sentry Issue", "severity": "error", "namespace": "awoooi-prod", "target_resource": "web", "fingerprint": "fp-sentry-issue-1", }, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:10:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, source_review_decisions_by_work_item={ "source-evidence:sentry:received:issue-1": accepted_decision, }, ) apply_payload = build_source_correlation_apply( recurrence, work_item_id="source-evidence:sentry:received:issue-1", ) assert apply_payload["schema_version"] == "awooop_source_correlation_apply_v1" assert apply_payload["allowed"] is True assert apply_payload["apply_status"] == "ready_to_apply" assert apply_payload["target_incident_id"] == "INC-20260520-ABC123" assert apply_payload["writes_source_event"] is True assert apply_payload["writes_incident_state"] is False assert apply_payload["writes_auto_repair_result"] is False assert apply_payload["writes_ticket"] is False assert apply_payload["source_event_stage"] == "source_correlation_linked" assert apply_payload["source_event_provider_event_id"] == ( "sentry:source_correlation_linked:issue-1" ) assert apply_payload["plan"]["writes"] == [ "awooop_conversation_event", "timeline_events", "alert_operation_log", ] assert apply_payload["next_step"] == "verify_source_link_in_status_chain" def test_source_correlation_apply_blocks_without_accepted_review() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "a" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": {"sentry_issue_ids": ["issue-1"]}, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:10:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, ) apply_payload = build_source_correlation_apply( recurrence, work_item_id="source-evidence:sentry:received:issue-1", ) assert apply_payload["allowed"] is False assert apply_payload["executed"] is False assert apply_payload["writes_source_event"] is False assert apply_payload["apply_status"] == "blocked" assert apply_payload["plan"]["writes"] == [] assert apply_payload["next_step"] == "fix_preflight_checks" def test_build_dossier_recurrence_surfaces_source_apply_history() -> None: apply_record = { "schema_version": "awooop_source_correlation_apply_v1", "apply_id": "apply-1", "work_item_id": "source-evidence:sentry:received:issue-1", "apply_status": "applied", "target_incident_id": "INC-20260520-ABC123", "review_id": "review-1", "source_event_id": "source-event-1", "source_event_provider_event_id": ( "sentry:source_correlation_linked:issue-1" ), "recorded_at": "2026-05-20T13:12:00", } recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "sentry:received:issue-1", "content_hash": "a" * 64, "content_preview": "Sentry issue", "content_redacted": "Sentry issue", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "sentry", "stage": "received", "source_refs": {"sentry_issue_ids": ["issue-1"]}, }, "is_duplicate": False, "provider_ts": None, "received_at": "2026-05-20T13:10:00", "run_id": None, "run_state": None, "run_agent_id": None, } ], project_id="awoooi", limit=20, source_applies_by_work_item={ "source-evidence:sentry:received:issue-1": apply_record, }, ) item = recurrence["items"][0] assert recurrence["summary"]["source_correlation_applied_group_total"] == 1 assert item["source_correlation_apply"] == apply_record assert item["work_item"]["matched_incident_id"] == "INC-20260520-ABC123" assert item["work_item"]["next_step"] == "verify_source_link_in_status_chain" def test_build_dossier_recurrence_opens_work_item_for_completed_run_without_repair() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:1", "content_hash": "a" * 64, "content_preview": "Docker container unhealthy", "content_redacted": "Docker container unhealthy", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-1"], "incident_ids": ["INC-20260517-F25B4A"], "fingerprints": ["fp-container-unhealthy"], }, "log_correlation": { "alertname": "DockerContainerUnhealthy", "severity": "warning", "namespace": "momo", "target_resource": "bitan-pharmacy-bitan-1", "fingerprint": "fp-container-unhealthy", }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-17T23:47:00", "run_id": UUID("33333333-3333-4333-8333-333333333333"), "run_state": "completed", "run_agent_id": "openclaw", } ], project_id="awoooi", limit=20, ) item = recurrence["items"][0] assert recurrence["summary"]["open_work_item_group_total"] == 1 assert recurrence["summary"]["automation_gap_group_total"] == 1 assert item["repair_summary"]["status"] == "run_completed_no_repair" assert item["work_item"] == { "schema_version": "awooop_recurrence_work_item_link_v1", "work_item_id": "incident:INC-20260517-F25B4A", "incident_id": "INC-20260517-F25B4A", "matched_incident_id": None, "auto_repair_id": None, "status": "open", "kind": "automation_gap", "next_step": "create_repair_ticket", "reason": "completed_run_without_auto_repair", "needs_human": True, } def test_build_recurrence_work_item_preview_selects_ticket_mode() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:1", "content_hash": "a" * 64, "content_preview": "Docker container unhealthy", "content_redacted": "Docker container unhealthy", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-1"], "incident_ids": ["INC-20260517-F25B4A"], "fingerprints": ["fp-container-unhealthy"], }, "log_correlation": { "alertname": "DockerContainerUnhealthy", "severity": "warning", "namespace": "momo", "target_resource": "bitan-pharmacy-bitan-1", "fingerprint": "fp-container-unhealthy", }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-17T23:47:00", "run_id": UUID("33333333-3333-4333-8333-333333333333"), "run_state": "completed", "run_agent_id": "openclaw", } ], project_id="awoooi", limit=20, ) preview = build_recurrence_work_item_preview( recurrence, work_item_id="incident:INC-20260517-F25B4A", ) assert preview["schema_version"] == "awooop_recurrence_work_item_preview_v1" assert preview["source"] == "channel_event_dossier.recurrence" assert preview["mode"] == "ticket" assert preview["allowed"] is True assert preview["writes_incident_state"] is False assert preview["writes_auto_repair_result"] is False assert preview["writes_ticket"] is False assert preview["plan"]["step"] == "prepare_repair_ticket_preview" assert preview["plan"]["target"]["alertname"] == "DockerContainerUnhealthy" def test_build_recurrence_work_item_dry_run_returns_ticket_preview_without_writes() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:1", "content_hash": "a" * 64, "content_preview": "Docker container unhealthy", "content_redacted": "Docker container unhealthy", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-1"], "incident_ids": ["INC-20260517-F25B4A"], "fingerprints": ["fp-container-unhealthy"], }, "log_correlation": { "alertname": "DockerContainerUnhealthy", "severity": "warning", "namespace": "momo", "target_resource": "bitan-pharmacy-bitan-1", "fingerprint": "fp-container-unhealthy", }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-17T23:47:00", "run_id": UUID("33333333-3333-4333-8333-333333333333"), "run_state": "completed", "run_agent_id": "openclaw", } ], project_id="awoooi", limit=20, ) dry_run = build_recurrence_work_item_dry_run( recurrence, work_item_id="incident:INC-20260517-F25B4A", ) assert dry_run["schema_version"] == "awooop_recurrence_work_item_dry_run_v1" assert dry_run["mode"] == "ticket" assert dry_run["allowed"] is True assert dry_run["executed"] is True assert dry_run["writes_incident_state"] is False assert dry_run["writes_auto_repair_result"] is False assert dry_run["writes_ticket"] is False assert dry_run["verification_result_preview"] == "ticket_preview_ready" assert dry_run["ticket_preview"]["would_create"] is False assert "DockerContainerUnhealthy" in dry_run["ticket_preview"]["title"] assert dry_run["current_state_summary"]["repair_status"] == "run_completed_no_repair" assert dry_run["read_model_route"]["required_scope"] == "read" @pytest.mark.asyncio async def test_source_correlation_dry_run_history_records_without_incident( monkeypatch, ) -> None: appended: list[dict[str, object]] = [] class FakeRecord: id = "alert-op-1" class FakeRepo: async def append(self, event_type: str, **kwargs): # noqa: ANN001 appended.append({"event_type": event_type, **kwargs}) return FakeRecord() from src.repositories import alert_operation_log_repository monkeypatch.setattr( alert_operation_log_repository, "get_alert_operation_log_repository", lambda: FakeRepo(), ) history = ( await channel_event_dossier_service._record_recurrence_work_item_dry_run_history( { "source": "channel_event_dossier.recurrence", "project_id": "awoooi", "work_item_id": ( "source-evidence:sentry:upstream_canary:" "awoooi-canary-codex-t115-production" ), "incident_id": None, "auto_repair_id": None, "mode": "observe", "requested_mode": "auto", "allowed": True, "executed": True, "safety_level": "read_only", "writes_incident_state": False, "writes_auto_repair_result": False, "writes_ticket": False, "verification_result_preview": "observe_only", "current_state_summary": { "work_item_kind": "source_correlation_review", "work_item_next_step": "review_provider_source_match", "repair_status": "source_correlation_review", "latest_stage": "upstream_canary", "latest_provider_event_id": ( "sentry:upstream_canary:" "awoooi-canary-codex-t115-production" ), "latest_run_id": UUID("11111111-1111-4111-8111-111111111111"), }, "ticket_preview": {"would_create": False}, "read_model_route": { "agent_id": "awooop_recurrence_coordinator", "tool_name": "channel_event_dossier.recurrence", "required_scope": "read", }, "checks": [], "next_step": "review_provider_source_match", } ) ) assert history == { "recorded": True, "alert_operation_id": "alert-op-1", "timeline_event_id": None, "timeline_reason": "source_review_not_incident_scoped", } assert appended[0]["event_type"] == "PRE_FLIGHT_PASSED" assert appended[0]["incident_id"] is None assert appended[0]["actor"] == "awooop_recurrence_work_item_service" assert appended[0]["context"]["work_item_id"].startswith("source-evidence:") assert ( appended[0]["context"]["current_state_summary"]["latest_run_id"] == "11111111-1111-4111-8111-111111111111" ) @pytest.mark.asyncio async def test_source_correlation_apply_history_appends_source_event_and_audit( monkeypatch, ) -> None: source_calls: list[dict[str, object]] = [] timeline_calls: list[dict[str, object]] = [] alert_calls: list[dict[str, object]] = [] async def fake_record_external_alert_event(**kwargs): # noqa: ANN003 source_calls.append(kwargs) return UUID("99999999-9999-4999-8999-999999999999") class FakeTimeline: async def add_event(self, **kwargs): # noqa: ANN001 timeline_calls.append(kwargs) return {"id": "timeline-1"} class FakeRecord: id = "alert-op-1" class FakeRepo: async def append(self, event_type: str, **kwargs): # noqa: ANN001 alert_calls.append({"event_type": event_type, **kwargs}) return FakeRecord() from src.repositories import alert_operation_log_repository from src.services import approval_db, channel_hub monkeypatch.setattr( channel_hub, "record_external_alert_event", fake_record_external_alert_event, ) monkeypatch.setattr(approval_db, "get_timeline_service", lambda: FakeTimeline()) monkeypatch.setattr( alert_operation_log_repository, "get_alert_operation_log_repository", lambda: FakeRepo(), ) history = await channel_event_dossier_service._record_source_correlation_apply_history( { "schema_version": "awooop_source_correlation_apply_v1", "source": "channel_event_dossier.recurrence", "project_id": "awoooi", "work_item_id": "source-evidence:sentry:received:issue-1", "review_id": "review-1", "target_incident_id": "INC-20260520-ABC123", "reviewer_id": "operator_console", "operator_note": "matches incident", "allowed": True, "apply_status": "ready_to_apply", "safety_level": "source_review_append_only_apply", "writes_incident_state": False, "writes_source_event": True, "writes_auto_repair_result": False, "writes_ticket": False, "creates_external_ticket": False, "latest_provider_event_id": "sentry:received:issue-1", "source_event_provider_event_id": ( "sentry:source_correlation_linked:issue-1" ), "source_event_stage": "source_correlation_linked", "raw_event_id": "issue-1", "provider": "sentry", "alertname": "Sentry Issue", "severity": "error", "namespace": "awoooi-prod", "target_resource": "web", "fingerprint": "fp-sentry-issue-1", "checks": [], "current_state_summary": {}, "plan": {"writes": ["awooop_conversation_event"]}, "read_model_route": {}, "next_step": "verify_source_link_in_status_chain", } ) assert history == { "recorded": True, "source_event_id": "99999999-9999-4999-8999-999999999999", "alert_operation_id": "alert-op-1", "timeline_event_id": "timeline-1", } assert source_calls[0]["stage"] == "source_correlation_linked" assert source_calls[0]["incident_id"] == "INC-20260520-ABC123" assert source_calls[0]["event_id"] == "issue-1" assert timeline_calls[0]["incident_id"] == "INC-20260520-ABC123" assert alert_calls[0]["actor"] == "awooop_source_correlation_apply_service" assert alert_calls[0]["context"]["schema_version"] == ( "awooop_source_correlation_apply_v1" ) assert alert_calls[0]["context"]["apply_status"] == "applied" def test_build_recurrence_work_item_handoff_records_ticket_proposal_contract() -> None: recurrence = build_dossier_recurrence( [ { "event_id": "event-1", "project_id": "awoooi", "channel_type": "internal", "provider_event_id": "alertmanager:received:1", "content_hash": "a" * 64, "content_preview": "Docker container unhealthy", "content_redacted": "Docker container unhealthy", "redaction_version": "audit_sink_v1", "source_envelope": { "provider": "alertmanager", "source_refs": { "alert_ids": ["alert-1"], "incident_ids": ["INC-20260517-F25B4A"], "fingerprints": ["fp-container-unhealthy"], }, "log_correlation": { "alertname": "DockerContainerUnhealthy", "severity": "warning", "namespace": "momo", "target_resource": "bitan-pharmacy-bitan-1", "fingerprint": "fp-container-unhealthy", }, }, "is_duplicate": True, "provider_ts": None, "received_at": "2026-05-17T23:47:00", "run_id": UUID("33333333-3333-4333-8333-333333333333"), "run_state": "completed", "run_agent_id": "openclaw", } ], project_id="awoooi", limit=20, ) handoff = build_recurrence_work_item_handoff( recurrence, work_item_id="incident:INC-20260517-F25B4A", ) assert handoff["schema_version"] == "awooop_recurrence_work_item_handoff_v1" assert handoff["mode"] == "ticket" assert handoff["handoff_kind"] == "ticket_proposal" assert handoff["handoff_status"] == "ready_to_record" assert handoff["handoff_owner"] == "operator" assert handoff["safety_level"] == "handoff_record_only" assert handoff["allowed"] is True assert handoff["creates_external_ticket"] is False assert handoff["writes_incident_state"] is False assert handoff["writes_auto_repair_result"] is False assert handoff["writes_ticket"] is False assert handoff["ticket_preview"]["would_create"] is False assert handoff["next_step"] == "operator_review_ticket_preview" assert handoff["plan"]["step"] == "record_recurrence_work_item_handoff" assert handoff["plan"]["writes"] == ["timeline_events", "alert_operation_log"] assert handoff["read_model_route"]["required_scope"] == "record_history" def test_build_recurrence_work_item_preview_raises_for_missing_item() -> None: recurrence = build_dossier_recurrence( [], project_id="awoooi", limit=20, ) with pytest.raises(RecurrenceWorkItemNotFoundError): build_recurrence_work_item_preview( recurrence, work_item_id="incident:INC-20260517-MISSING", ) def test_recurrence_response_model_preserves_repair_work_item_fields() -> None: response = ChannelEventRecurrenceResponse.model_validate( { "project_id": "awoooi", "limit": 20, "summary": { "source_event_total": 1, "recurrence_group_total": 1, "recurrent_group_total": 1, "duplicate_event_total": 0, "linked_run_total": 1, "unlinked_event_total": 0, "auto_repair_linked_total": 1, "verified_repair_group_total": 1, "open_work_item_group_total": 0, "manual_gate_group_total": 0, "automation_gap_group_total": 0, "failed_repair_group_total": 0, "source_correlation_review_group_total": 0, "latest_received_at": "2026-05-13T13:47:00", }, "items": [ { "recurrence_key": "fingerprint:fp-host-disk", "provider": "alertmanager", "alertname": "HostDiskUsageHigh", "severity": "warning", "namespace": "node", "target_resource": "host-110", "fingerprint": "fp-host-disk", "latest_stage": "incident_linked", "latest_event_id": "11111111-1111-4111-8111-111111111111", "latest_provider_event_id": "alertmanager:received:1", "latest_content_preview": "Host disk pressure", "latest_run_id": "22222222-2222-4222-8222-222222222222", "latest_run_state": "completed", "latest_agent_id": "openclaw", "latest_incident_id": "INC-20260513-ABCD", "incident_ids": ["INC-20260513-ABCD"], "repair_summary": { "status": "auto_repair_verified", "latest_auto_repair_id": "repair-1", }, "work_item": { "work_item_id": "verification:INC-20260513-ABCD:repair-1", "status": "closed", }, "occurrence_total": 1, "duplicate_total": 0, "linked_run_total": 1, "source_ref_total": 3, "missing_source_refs_total": 0, "sentry_ref_total": 0, "signoz_ref_total": 0, "alert_ref_total": 1, "stage_counts": {"incident_linked": 1}, "run_state_counts": {"completed": 1}, "first_received_at": "2026-05-13T13:47:00", "latest_received_at": "2026-05-13T13:47:00", } ], } ) payload = response.model_dump() assert payload["summary"]["auto_repair_linked_total"] == 1 assert payload["summary"]["source_correlation_review_group_total"] == 0 assert payload["items"][0]["latest_incident_id"] == "INC-20260513-ABCD" assert payload["items"][0]["latest_stage"] == "incident_linked" assert payload["items"][0]["stage_counts"] == {"incident_linked": 1} assert payload["items"][0]["repair_summary"]["status"] == "auto_repair_verified" assert payload["items"][0]["work_item"]["status"] == "closed" @pytest.mark.asyncio async def test_fetch_channel_event_dossier_requires_source() -> None: with pytest.raises(HTTPException) as exc_info: await fetch_channel_event_dossier( project_id="awoooi", run_id=None, provider_event_id=None, limit=20, ) assert exc_info.value.status_code == 422 @pytest.mark.asyncio async def test_fetch_channel_event_dossier_uses_typed_run_filter(monkeypatch) -> None: captured: dict[str, object] = {} class FakeMappings: def all(self) -> list[dict[str, object]]: return [] class FakeResult: def mappings(self) -> FakeMappings: return FakeMappings() class FakeDb: async def execute(self, statement, params): # noqa: ANN001 captured["sql"] = str(statement) captured["params"] = params return FakeResult() class FakeContext: async def __aenter__(self) -> FakeDb: return FakeDb() async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001 return None monkeypatch.setattr( channel_event_dossier_service, "get_db_context", lambda _project_id: FakeContext(), ) run_id = UUID("0a4c365f-609e-5441-bc29-4c7ebc3603b6") result = await fetch_channel_event_dossier( project_id="awoooi", run_id=run_id, provider_event_id=None, limit=20, ) assert result["total"] == 0 assert "run_id = CAST(:run_id AS uuid)" in str(captured["sql"]) assert ":run_id IS NULL" not in str(captured["sql"]) assert captured["params"] == { "project_id": "awoooi", "run_id": str(run_id), "limit": 20, } @pytest.mark.asyncio async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter( monkeypatch, ) -> None: captured: list[dict[str, object]] = [] class FakeMappings: def all(self) -> list[dict[str, object]]: return [] class FakeResult: def mappings(self) -> FakeMappings: return FakeMappings() class FakeDb: async def execute(self, statement, params): # noqa: ANN001 captured.append({"sql": str(statement), "params": params}) return FakeResult() class FakeContext: async def __aenter__(self) -> FakeDb: return FakeDb() async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001 return None monkeypatch.setattr( channel_event_dossier_service, "get_db_context", lambda _project_id: FakeContext(), ) result = await fetch_channel_event_dossier_coverage( project_id=None, provider="sentry", limit=500, ) assert result["project_id"] == "awoooi" assert result["limit"] == 200 coverage_query = captured[0] assert "source_envelope->>'provider'" in str(coverage_query["sql"]) assert coverage_query["params"] == { "project_id": "awoooi", "provider": "sentry", "limit": 200, } @pytest.mark.asyncio async def test_fetch_channel_event_dossier_recurrence_uses_joined_typed_filter( monkeypatch, ) -> None: captured: list[dict[str, object]] = [] class FakeMappings: def all(self) -> list[dict[str, object]]: return [] class FakeResult: def mappings(self) -> FakeMappings: return FakeMappings() class FakeDb: async def execute(self, statement, params): # noqa: ANN001 captured.append({"sql": str(statement), "params": params}) return FakeResult() class FakeContext: async def __aenter__(self) -> FakeDb: return FakeDb() async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001 return None monkeypatch.setattr( channel_event_dossier_service, "get_db_context", lambda _project_id: FakeContext(), ) result = await fetch_channel_event_dossier_recurrence( project_id="awoooi", provider="alertmanager", limit=500, ) assert result["project_id"] == "awoooi" assert result["limit"] == 300 recurrence_query = captured[0] assert "LEFT JOIN awooop_run_state r" in str(recurrence_query["sql"]) assert "e.source_envelope->>'provider'" in str(recurrence_query["sql"]) assert ":provider IS NULL" not in str(recurrence_query["sql"]) assert recurrence_query["params"] == { "project_id": "awoooi", "provider": "alertmanager", "limit": 300, }