feat(awooop): record source correlation review decisions
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / tests (push) Successful in 3m59s
CD Pipeline / build-and-deploy (push) Successful in 3m48s
CD Pipeline / post-deploy-checks (push) Successful in 1m55s

This commit is contained in:
Your Name
2026-05-21 09:53:36 +08:00
parent ee5a54ecba
commit 88e7477a7c
8 changed files with 933 additions and 26 deletions

View File

@@ -15,6 +15,7 @@ from src.services.channel_event_dossier_service import (
build_recurrence_work_item_dry_run,
build_recurrence_work_item_handoff,
build_recurrence_work_item_preview,
build_source_correlation_review_decision,
fetch_channel_event_dossier,
fetch_channel_event_dossier_coverage,
fetch_channel_event_dossier_recurrence,
@@ -268,6 +269,7 @@ def test_build_dossier_recurrence_groups_events_and_run_state() -> None:
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"
@@ -287,6 +289,7 @@ def test_build_dossier_recurrence_groups_events_and_run_state() -> None:
"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",
@@ -304,6 +307,7 @@ def test_build_dossier_recurrence_groups_events_and_run_state() -> None:
"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",
@@ -355,6 +359,7 @@ def test_build_recurrence_work_item_preview_allows_source_correlation_review() -
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"
@@ -386,6 +391,124 @@ def test_build_recurrence_work_item_preview_allows_source_correlation_review() -
)
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_build_dossier_recurrence_opens_work_item_for_completed_run_without_repair() -> None:
recurrence = build_dossier_recurrence(
[
@@ -433,6 +556,7 @@ def test_build_dossier_recurrence_opens_work_item_for_completed_run_without_repa
"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",
@@ -852,7 +976,7 @@ async def test_fetch_channel_event_dossier_uses_typed_run_filter(monkeypatch) ->
async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
captured: list[dict[str, object]] = []
class FakeMappings:
def all(self) -> list[dict[str, object]]:
@@ -864,8 +988,7 @@ async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter(
class FakeDb:
async def execute(self, statement, params): # noqa: ANN001
captured["sql"] = str(statement)
captured["params"] = params
captured.append({"sql": str(statement), "params": params})
return FakeResult()
class FakeContext:
@@ -889,8 +1012,9 @@ async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter(
assert result["project_id"] == "awoooi"
assert result["limit"] == 200
assert "source_envelope->>'provider'" in str(captured["sql"])
assert captured["params"] == {
coverage_query = captured[0]
assert "source_envelope->>'provider'" in str(coverage_query["sql"])
assert coverage_query["params"] == {
"project_id": "awoooi",
"provider": "sentry",
"limit": 200,
@@ -901,7 +1025,7 @@ async def test_fetch_channel_event_dossier_coverage_uses_typed_provider_filter(
async def test_fetch_channel_event_dossier_recurrence_uses_joined_typed_filter(
monkeypatch,
) -> None:
captured: dict[str, object] = {}
captured: list[dict[str, object]] = []
class FakeMappings:
def all(self) -> list[dict[str, object]]:
@@ -913,8 +1037,7 @@ async def test_fetch_channel_event_dossier_recurrence_uses_joined_typed_filter(
class FakeDb:
async def execute(self, statement, params): # noqa: ANN001
captured["sql"] = str(statement)
captured["params"] = params
captured.append({"sql": str(statement), "params": params})
return FakeResult()
class FakeContext:
@@ -938,10 +1061,11 @@ async def test_fetch_channel_event_dossier_recurrence_uses_joined_typed_filter(
assert result["project_id"] == "awoooi"
assert result["limit"] == 300
assert "LEFT JOIN awooop_run_state r" in str(captured["sql"])
assert "e.source_envelope->>'provider'" in str(captured["sql"])
assert ":provider IS NULL" not in str(captured["sql"])
assert captured["params"] == {
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,