feat(governance): expose km draft dedupe plan
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
Type Sync Check / check-type-sync (push) Successful in 33s
CD Pipeline / tests (push) Successful in 4m3s
E2E Health Check / e2e-health (push) Successful in 23s
CD Pipeline / build-and-deploy (push) Successful in 4m54s
CD Pipeline / post-deploy-checks (push) Successful in 2m9s

This commit is contained in:
Your Name
2026-05-19 23:56:03 +08:00
parent 65badab6fd
commit 0cd6301d0e
8 changed files with 507 additions and 8 deletions

View File

@@ -29,10 +29,14 @@ from src.models.governance import (
GovernanceEventsResponse,
GovernanceQueueResponse,
GovernanceSummaryResponse,
KnowledgeReviewDraftDedupeGroup,
KnowledgeReviewDraftDedupeResponse,
map_severity,
)
from src.services.governance_query_service import (
_build_km_review_draft_dedupe_groups,
_extract_kb_draft_entry_id,
_extract_governance_event_id_from_tags,
_extract_remediation,
_extract_worker_status,
_merge_dispatch_ids,
@@ -402,6 +406,90 @@ class TestQueueEndpoint:
assert "d.operator_note" not in source
class TestKmReviewDraftDedupe:
def test_endpoint_passes_limit_and_returns_owner_review_plan(self, client):
"""Work Items 需要 read-only canonical/duplicate plan不直接封存 KM。"""
fake = KnowledgeReviewDraftDedupeResponse(
total_review_drafts=3,
event_group_total=1,
duplicate_draft_total=2,
generated_at=NOW,
groups=[
KnowledgeReviewDraftDedupeGroup(
governance_event_id="event-001",
canonical_entry_id="km-canonical",
canonical_title="KM healthcheck review draft - event",
preferred_source="dispatch_context",
duplicate_entry_ids=["km-dup-1", "km-dup-2"],
duplicate_count=2,
total_entries=3,
suggested_action="owner_review_canonical_then_archive_duplicates",
owner_action="review_canonical_and_archive_duplicate_drafts",
writes_on_read=False,
can_archive_without_owner_approval=False,
)
],
)
captured: dict = {}
async def mock_query(**kwargs):
captured.update(kwargs)
return fake
with patch("src.api.v1.ai_governance.query_km_review_draft_dedupe", new=mock_query):
r = client.get("/api/v1/ai/governance/km-review-drafts/dedupe?limit=50")
assert r.status_code == 200
assert captured["limit"] == 50
data = r.json()
assert data["duplicate_draft_total"] == 2
assert data["groups"][0]["canonical_entry_id"] == "km-canonical"
assert data["groups"][0]["writes_on_read"] is False
assert data["groups"][0]["can_archive_without_owner_approval"] is False
def test_governance_event_tag_extraction(self):
assert _extract_governance_event_id_from_tags([
"agent:Hermes",
"governance_event:event-001",
]) == "event-001"
assert _extract_governance_event_id_from_tags(["agent:Hermes"]) is None
def test_build_dedupe_groups_prefers_dispatch_context_draft(self):
"""若 dispatch context 指出 canonical draftdedupe plan 應以它為準。"""
rows = [
{
"id": "km-latest",
"title": "latest draft",
"tags": ["governance_event:event-001"],
"updated_at": datetime(2026, 5, 2, 14, 0, tzinfo=TAIPEI),
},
{
"id": "km-canonical",
"title": "canonical draft",
"tags": ["governance_event:event-001"],
"updated_at": datetime(2026, 5, 2, 13, 0, tzinfo=TAIPEI),
},
{
"id": "km-other",
"title": "other event draft",
"tags": ["governance_event:event-002"],
"updated_at": datetime(2026, 5, 2, 12, 0, tzinfo=TAIPEI),
},
]
groups = _build_km_review_draft_dedupe_groups(
rows,
{"event-001": "km-canonical"},
)
first = groups[0]
assert first.governance_event_id == "event-001"
assert first.canonical_entry_id == "km-canonical"
assert first.preferred_source == "dispatch_context"
assert first.duplicate_entry_ids == ["km-latest"]
assert first.writes_on_read is False
# =============================================================================
# 4. summary endpoint compliance_rate
# =============================================================================