fix(governance): fail soft owner review readbacks
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m17s
CD Pipeline / build-and-deploy (push) Successful in 4m43s
CD Pipeline / post-deploy-checks (push) Has been cancelled

This commit is contained in:
Your Name
2026-07-03 09:51:15 +08:00
parent e565b23068
commit f3c72ec237
2 changed files with 97 additions and 4 deletions

View File

@@ -341,6 +341,24 @@ async def get_km_stale_owner_reviews(
)
except KmStaleOwnerReviewError as exc:
raise HTTPException(status_code=exc.status_code, detail=exc.detail) from exc
except Exception as exc:
logger.warning(
"km_stale_owner_reviews_readback_degraded",
project_id=project_id,
dispatch_status=dispatch_status,
limit=limit,
error_type=type(exc).__name__,
)
return KnowledgeStaleOwnerReviewInboxResponse(
project_id=project_id,
dispatch_status=dispatch_status,
total=0,
returned=0,
writes_on_read=False,
manual_review_required=True,
items=[],
generated_at=now_taipei(),
)
# =============================================================================
@@ -366,10 +384,35 @@ async def get_km_stale_owner_review_burndown(
project_id=project_id,
limit=limit,
)
return await query_km_stale_owner_review_burndown(
project_id=project_id,
limit=limit,
)
try:
return await query_km_stale_owner_review_burndown(
project_id=project_id,
limit=limit,
)
except Exception as exc:
logger.warning(
"km_stale_owner_review_burndown_readback_degraded",
project_id=project_id,
limit=limit,
error_type=type(exc).__name__,
)
return KnowledgeStaleOwnerReviewBurnDownResponse(
project_id=project_id,
burn_down_status="no_data",
current_snapshot=None,
entries_to_threshold=0,
pending_owner_reviews=0,
completed_owner_reviews=0,
completion_audit_total=0,
stale_ratio_recheck_total=0,
latest_stale_count_delta=None,
latest_stale_ratio_delta=None,
writes_on_read=False,
manual_review_required=True,
returned=0,
items=[],
generated_at=now_taipei(),
)
# =============================================================================

View File

@@ -1028,6 +1028,29 @@ class TestKmReviewDraftDedupe:
assert data["items"][0]["workflow_stage"] == "waiting_owner_review"
assert data["items"][0]["batch_dispatch_id"] == "dispatch-batch-001"
def test_owner_review_inbox_readback_exception_returns_empty_inbox(self, client):
"""owner review inbox 背景讀取失敗時應 fail-soft不讓 Work Items 收到 500."""
with patch(
"src.api.v1.ai_governance.query_km_stale_owner_review_inbox",
new=AsyncMock(side_effect=RuntimeError("temporary inbox failure")),
):
r = client.get(
"/api/v1/ai/governance/km-stale-owner-reviews"
"?project_id=awoooi&dispatch_status=pending&limit=30"
)
assert r.status_code == 200
data = r.json()
assert data["schema_version"] == "km_stale_owner_review_inbox_v1"
assert data["project_id"] == "awoooi"
assert data["dispatch_status"] == "pending"
assert data["total"] == 0
assert data["returned"] == 0
assert data["writes_on_read"] is False
assert data["manual_review_required"] is True
assert data["items"] == []
assert data["generated_at"]
def test_owner_review_inbox_context_keeps_batch_and_priority_visible(self):
record = KnowledgeEntryRecord(
id="km-001",
@@ -1156,6 +1179,33 @@ class TestKmReviewDraftDedupe:
assert data["latest_stale_count_delta"] == -1
assert data["items"][0]["recheck_dispatch_id"] == "dispatch-recheck-001"
def test_owner_review_burndown_readback_exception_returns_no_data(self, client):
"""burndown 背景讀取失敗時應 fail-soft不讓 Work Items 收到 500."""
with patch(
"src.api.v1.ai_governance.query_km_stale_owner_review_burndown",
new=AsyncMock(side_effect=RuntimeError("temporary burndown failure")),
):
r = client.get(
"/api/v1/ai/governance/km-stale-owner-review-burndown"
"?project_id=awoooi&limit=20"
)
assert r.status_code == 200
data = r.json()
assert data["schema_version"] == "km_stale_owner_review_burndown_v1"
assert data["project_id"] == "awoooi"
assert data["burn_down_status"] == "no_data"
assert data["entries_to_threshold"] == 0
assert data["pending_owner_reviews"] == 0
assert data["completed_owner_reviews"] == 0
assert data["completion_audit_total"] == 0
assert data["stale_ratio_recheck_total"] == 0
assert data["writes_on_read"] is False
assert data["manual_review_required"] is True
assert data["returned"] == 0
assert data["items"] == []
assert data["generated_at"]
def test_owner_review_burndown_items_compute_chronological_delta(self):
older_snapshot = {
"stale_count": 119,