feat(governance): surface stale km owner review inbox
All checks were successful
CD Pipeline / tests (push) Successful in 5m29s
Code Review / ai-code-review (push) Successful in 16s
Type Sync Check / check-type-sync (push) Successful in 28s
CD Pipeline / build-and-deploy (push) Successful in 4m12s
CD Pipeline / post-deploy-checks (push) Successful in 1m30s

This commit is contained in:
Your Name
2026-05-24 21:32:29 +08:00
parent d04377dd20
commit 0c447acb19
7 changed files with 676 additions and 0 deletions

View File

@@ -42,6 +42,8 @@ from src.models.governance import (
KnowledgeStaleOwnerReviewBatchQueueResponse,
KnowledgeStaleOwnerReviewCompleteRequest,
KnowledgeStaleOwnerReviewCompleteResponse,
KnowledgeStaleOwnerReviewInboxItem,
KnowledgeStaleOwnerReviewInboxResponse,
KnowledgeStaleOwnerReviewRequest,
KnowledgeStaleOwnerReviewResponse,
map_severity,
@@ -60,6 +62,7 @@ from src.services.governance_km_stale_review_service import (
_build_batch_queue_plan_fingerprint,
_build_completion_plan_fingerprint,
_build_owner_review_completion_audit_context,
_build_owner_review_inbox_item,
_build_stale_owner_review_decision_context,
_completion_stage_for_outcome,
)
@@ -878,6 +881,124 @@ class TestKmReviewDraftDedupe:
assert r.status_code == 409
assert "batch queue plan" in r.json()["detail"]
def test_owner_review_inbox_endpoint_returns_sorted_work_items(self, client):
"""Owner-review inbox 應回傳 pending dispatch 與 KM priority context。"""
fake = KnowledgeStaleOwnerReviewInboxResponse(
project_id="awoooi",
dispatch_status="pending",
total=1,
returned=1,
items=[
KnowledgeStaleOwnerReviewInboxItem(
dispatch_id="dispatch-001",
governance_event_id="event-001",
entry_id="km-001",
project_id="awoooi",
title="Sentry checkout failure repair",
dispatch_status="pending",
workflow_stage="waiting_owner_review",
next_action="owner_review_stale_km_candidate",
owner="operator_console",
owner_note="p0_p1_stale_km_batch",
batch_governance_event_id="event-batch-001",
batch_dispatch_id="dispatch-batch-001",
priority_tier="P0",
priority_score=265,
recommended_action="refresh_with_evidence",
stale_days=35,
view_count=7,
correlation_sources=["incident", "playbook", "sentry"],
reasons=["linked_incident", "linked_playbook"],
related_incident_id="INC-20260513-79ED5E",
related_playbook_id="pb:auto-repair-canary",
queued_at=NOW,
)
],
generated_at=NOW,
)
captured: dict = {}
async def mock_inbox(**kwargs):
captured.update(kwargs)
return fake
with patch(
"src.api.v1.ai_governance.query_km_stale_owner_review_inbox",
new=mock_inbox,
):
r = client.get(
"/api/v1/ai/governance/km-stale-owner-reviews"
"?project_id=awoooi&dispatch_status=pending&limit=20"
)
assert r.status_code == 200
assert captured == {
"project_id": "awoooi",
"dispatch_status": "pending",
"limit": 20,
}
data = r.json()
assert data["schema_version"] == "km_stale_owner_review_inbox_v1"
assert data["total"] == 1
assert data["writes_on_read"] is False
assert data["manual_review_required"] is True
assert data["items"][0]["dispatch_id"] == "dispatch-001"
assert data["items"][0]["workflow_stage"] == "waiting_owner_review"
assert data["items"][0]["batch_dispatch_id"] == "dispatch-batch-001"
def test_owner_review_inbox_context_keeps_batch_and_priority_visible(self):
record = KnowledgeEntryRecord(
id="km-001",
project_id="awoooi",
title="Sentry checkout failure repair",
content="Use Sentry and SigNoz evidence before writeback.",
entry_type=EntryType.AUTO_RUNBOOK,
category="AI系統",
tags=["sentry", "signoz"],
source=EntrySource.AI_EXTRACTED,
status=EntryStatus.REVIEW,
related_incident_id="INC-20260513-79ED5E",
related_playbook_id="pb:auto-repair-canary",
view_count=7,
updated_at=NOW - timedelta(days=35),
)
candidate = _build_km_stale_candidate(record, now=NOW, threshold_days=7)
row = type("Row", (), {
"id": "dispatch-001",
"governance_event_id": "event-001",
"dispatch_status": "pending",
"dispatched_at": NOW,
"started_at": None,
"completed_at": None,
})()
ctx = {
"owner": "operator_console",
"owner_note": "p0_p1_stale_km_batch",
"next_action": "owner_review_stale_km_candidate",
"workflow": {
"entry_id": "km-001",
"project_id": "awoooi",
"batch_governance_event_id": "event-batch-001",
"batch_dispatch_id": "dispatch-batch-001",
"current_stage": "waiting_owner_review",
"stage_by_dispatch_status": {"pending": "waiting_owner_review"},
},
}
item = _build_owner_review_inbox_item(
row=row,
candidate=candidate,
decision_context=ctx,
)
assert item.dispatch_id == "dispatch-001"
assert item.batch_dispatch_id == "dispatch-batch-001"
assert item.priority_tier == "P0"
assert item.priority_score > 200
assert item.workflow_stage == "waiting_owner_review"
assert item.recommended_action == "refresh_with_evidence"
assert item.correlation_sources == ["incident", "playbook", "sentry", "signoz"]
def test_stale_owner_review_batch_context_is_operator_visible(self):
request = KnowledgeStaleOwnerReviewBatchQueueRequest(
project_id="awoooi",