feat(governance): surface stale km burndown
All checks were successful
CD Pipeline / tests (push) Successful in 5m28s
Code Review / ai-code-review (push) Successful in 12s
Type Sync Check / check-type-sync (push) Successful in 25s
CD Pipeline / build-and-deploy (push) Successful in 4m6s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s
All checks were successful
CD Pipeline / tests (push) Successful in 5m28s
Code Review / ai-code-review (push) Successful in 12s
Type Sync Check / check-type-sync (push) Successful in 25s
CD Pipeline / build-and-deploy (push) Successful in 4m6s
CD Pipeline / post-deploy-checks (push) Successful in 1m32s
This commit is contained in:
@@ -15,6 +15,7 @@ Unit Tests — AI Governance Endpoints (PR 1)
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
@@ -40,6 +41,8 @@ from src.models.governance import (
|
||||
KnowledgeStaleOwnerReviewBatchItem,
|
||||
KnowledgeStaleOwnerReviewBatchQueueRequest,
|
||||
KnowledgeStaleOwnerReviewBatchQueueResponse,
|
||||
KnowledgeStaleOwnerReviewBurnDownItem,
|
||||
KnowledgeStaleOwnerReviewBurnDownResponse,
|
||||
KnowledgeStaleOwnerReviewCompleteRequest,
|
||||
KnowledgeStaleOwnerReviewCompleteResponse,
|
||||
KnowledgeStaleOwnerReviewInboxItem,
|
||||
@@ -61,10 +64,12 @@ from src.services.governance_km_stale_review_service import (
|
||||
_build_batch_owner_review_decision_context,
|
||||
_build_batch_queue_plan_fingerprint,
|
||||
_build_completion_plan_fingerprint,
|
||||
_build_owner_review_burndown_items,
|
||||
_build_owner_review_completion_audit_context,
|
||||
_build_owner_review_inbox_item,
|
||||
_build_stale_owner_review_decision_context,
|
||||
_completion_stage_for_outcome,
|
||||
_entries_to_stale_threshold,
|
||||
)
|
||||
from src.services.governance_km_stale_review_service import (
|
||||
_build_stale_ratio_recheck_context as _build_stale_owner_review_recheck_context,
|
||||
@@ -999,6 +1004,146 @@ class TestKmReviewDraftDedupe:
|
||||
assert item.recommended_action == "refresh_with_evidence"
|
||||
assert item.correlation_sources == ["incident", "playbook", "sentry", "signoz"]
|
||||
|
||||
def test_owner_review_burndown_endpoint_returns_completion_progress(self, client):
|
||||
"""Burn-down endpoint 應把 pending、completion audit 與 recheck snapshot 集中呈現。"""
|
||||
fake = KnowledgeStaleOwnerReviewBurnDownResponse(
|
||||
project_id="awoooi",
|
||||
burn_down_status="above_threshold",
|
||||
current_snapshot=KnowledgeReviewDraftStaleRatioSnapshot(
|
||||
stale_count=118,
|
||||
total_count=200,
|
||||
stale_ratio=0.59,
|
||||
threshold=0.2,
|
||||
stale_days=7,
|
||||
),
|
||||
entries_to_threshold=78,
|
||||
pending_owner_reviews=9,
|
||||
completed_owner_reviews=1,
|
||||
completion_audit_total=1,
|
||||
stale_ratio_recheck_total=1,
|
||||
latest_stale_count_delta=-1,
|
||||
latest_stale_ratio_delta=-0.005,
|
||||
returned=1,
|
||||
items=[
|
||||
KnowledgeStaleOwnerReviewBurnDownItem(
|
||||
completion_dispatch_id="dispatch-audit-001",
|
||||
governance_event_id="event-001",
|
||||
source_dispatch_id="dispatch-001",
|
||||
recheck_dispatch_id="dispatch-recheck-001",
|
||||
entry_id="km-001",
|
||||
project_id="awoooi",
|
||||
dispatch_status="succeeded",
|
||||
workflow_stage="km_writeback_after_approval",
|
||||
review_outcome="refresh_with_evidence",
|
||||
owner="operator_console",
|
||||
completed_at=NOW,
|
||||
stale_ratio_snapshot=KnowledgeReviewDraftStaleRatioSnapshot(
|
||||
stale_count=118,
|
||||
total_count=200,
|
||||
stale_ratio=0.59,
|
||||
threshold=0.2,
|
||||
stale_days=7,
|
||||
),
|
||||
stale_count_delta=-1,
|
||||
stale_ratio_delta=-0.005,
|
||||
above_threshold=True,
|
||||
)
|
||||
],
|
||||
generated_at=NOW,
|
||||
)
|
||||
captured: dict = {}
|
||||
|
||||
async def mock_burndown(**kwargs):
|
||||
captured.update(kwargs)
|
||||
return fake
|
||||
|
||||
with patch(
|
||||
"src.api.v1.ai_governance.query_km_stale_owner_review_burndown",
|
||||
new=mock_burndown,
|
||||
):
|
||||
r = client.get(
|
||||
"/api/v1/ai/governance/km-stale-owner-review-burndown"
|
||||
"?project_id=awoooi&limit=12"
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert captured == {"project_id": "awoooi", "limit": 12}
|
||||
data = r.json()
|
||||
assert data["schema_version"] == "km_stale_owner_review_burndown_v1"
|
||||
assert data["burn_down_status"] == "above_threshold"
|
||||
assert data["writes_on_read"] is False
|
||||
assert data["manual_review_required"] is True
|
||||
assert data["pending_owner_reviews"] == 9
|
||||
assert data["completion_audit_total"] == 1
|
||||
assert data["stale_ratio_recheck_total"] == 1
|
||||
assert data["latest_stale_count_delta"] == -1
|
||||
assert data["items"][0]["recheck_dispatch_id"] == "dispatch-recheck-001"
|
||||
|
||||
def test_owner_review_burndown_items_compute_chronological_delta(self):
|
||||
older_snapshot = {
|
||||
"stale_count": 119,
|
||||
"total_count": 200,
|
||||
"stale_ratio": 0.595,
|
||||
"threshold": 0.2,
|
||||
"stale_days": 7,
|
||||
}
|
||||
newer_snapshot = {
|
||||
"stale_count": 118,
|
||||
"total_count": 200,
|
||||
"stale_ratio": 0.59,
|
||||
"threshold": 0.2,
|
||||
"stale_days": 7,
|
||||
}
|
||||
rows = [
|
||||
SimpleNamespace(
|
||||
id="dispatch-audit-new",
|
||||
governance_event_id="event-001",
|
||||
dispatch_status="succeeded",
|
||||
completed_at=NOW,
|
||||
decision_context={
|
||||
"project_id": "awoooi",
|
||||
"owner": "operator_console",
|
||||
"review_outcome": "refresh_with_evidence",
|
||||
"workflow": {
|
||||
"project_id": "awoooi",
|
||||
"entry_id": "km-002",
|
||||
"source_dispatch_id": "dispatch-002",
|
||||
"current_stage": "km_writeback_after_approval",
|
||||
"stale_ratio_snapshot": newer_snapshot,
|
||||
},
|
||||
"stale_ratio_recheck": {"dispatch_id": "dispatch-recheck-new"},
|
||||
},
|
||||
),
|
||||
SimpleNamespace(
|
||||
id="dispatch-audit-old",
|
||||
governance_event_id="event-001",
|
||||
dispatch_status="succeeded",
|
||||
completed_at=NOW - timedelta(minutes=10),
|
||||
decision_context={
|
||||
"project_id": "awoooi",
|
||||
"owner": "operator_console",
|
||||
"review_outcome": "refresh_with_evidence",
|
||||
"workflow": {
|
||||
"project_id": "awoooi",
|
||||
"entry_id": "km-001",
|
||||
"source_dispatch_id": "dispatch-001",
|
||||
"current_stage": "km_writeback_after_approval",
|
||||
"stale_ratio_snapshot": older_snapshot,
|
||||
},
|
||||
"stale_ratio_recheck": {"dispatch_id": "dispatch-recheck-old"},
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
items = _build_owner_review_burndown_items(rows, project_id="awoooi")
|
||||
|
||||
assert items[0].completion_dispatch_id == "dispatch-audit-new"
|
||||
assert items[0].stale_count_delta == -1
|
||||
assert items[0].stale_ratio_delta == pytest.approx(-0.005)
|
||||
assert items[0].above_threshold is True
|
||||
assert items[1].stale_count_delta is None
|
||||
assert _entries_to_stale_threshold(items[0].stale_ratio_snapshot) == 78
|
||||
|
||||
def test_stale_owner_review_batch_context_is_operator_visible(self):
|
||||
request = KnowledgeStaleOwnerReviewBatchQueueRequest(
|
||||
project_id="awoooi",
|
||||
|
||||
Reference in New Issue
Block a user