feat(governance): surface callback owner review work items
All checks were successful
CD Pipeline / tests (push) Successful in 1m18s
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / build-and-deploy (push) Successful in 4m36s
CD Pipeline / post-deploy-checks (push) Successful in 2m9s

This commit is contained in:
Your Name
2026-05-25 08:14:01 +08:00
parent 2c2446e56e
commit 156660929e
6 changed files with 256 additions and 0 deletions

View File

@@ -16,6 +16,7 @@ from collections import defaultdict
from collections.abc import Mapping
from datetime import UTC, datetime, timedelta
from typing import Any, get_args
from urllib.parse import urlencode
from uuid import UUID
import httpx
@@ -1087,6 +1088,12 @@ def _empty_km_stale_completion_summary(
"items_truncated": False,
"related_total": 0,
"related_items": [],
"work_item": _km_stale_callback_owner_review_work_item(
project_id=project_id,
incident_id=incident_id,
status_value=status_value,
reason=reason,
),
}
@@ -1157,6 +1164,52 @@ def _build_km_stale_completion_summary(
"items_truncated": total > returned,
"related_total": len(related_items),
"related_items": related_items[:3],
"work_item": _km_stale_callback_owner_review_work_item(
project_id=project_id,
incident_id=incident_id,
status_value=(
"matched_owner_review"
if related_items
else "no_related_owner_review"
),
reason=None if related_items else "no_matching_completion_item",
),
}
def _km_stale_callback_owner_review_work_item(
*,
project_id: str,
incident_id: str | None,
status_value: str,
reason: str | None,
) -> dict[str, Any] | None:
"""Generate a read-only Work Items link for callback evidence gaps."""
if not incident_id or status_value != "no_related_owner_review":
return None
work_item_id = f"km-callback-owner-review:{project_id}:{incident_id}"
target_query = urlencode(
{
"project_id": project_id,
"incident_id": incident_id,
"callback_reply_status": "sent",
}
)
return {
"schema_version": "km_stale_callback_owner_review_work_item_v1",
"work_item_id": work_item_id,
"kind": "km_stale_callback_owner_review",
"status": "open",
"project_id": project_id,
"incident_id": incident_id,
"reason": reason or "no_matching_completion_item",
"title": "Telegram callback incident has no matching KM owner-review item",
"next_step": "review_or_queue_km_owner_review",
"target_surface": "awooop_runs_callback_evidence",
"target_href": f"/awooop/runs?{target_query}",
"writes_on_read": False,
"manual_review_required": True,
"batch_writes_allowed": False,
}

View File

@@ -435,6 +435,7 @@ def test_list_callback_replies_response_preserves_callback_evidence() -> None:
"batch_writes_allowed": False,
"manual_review_required": True,
"related_total": 1,
"work_item": None,
"related_items": [
{
"entry_id": "km-1",
@@ -529,6 +530,53 @@ async def test_km_stale_completion_summary_matches_callback_incident(
assert summary["batch_writes_allowed"] is False
assert summary["related_items"][0]["entry_id"] == "km-1"
assert summary["related_items"][0]["can_preview"] is True
assert summary["work_item"] is None
def test_km_stale_completion_summary_generates_owner_review_work_item() -> None:
summary = platform_operator_service._build_km_stale_completion_summary(
queue=SimpleNamespace(
total=10,
returned=10,
pending_count=10,
ready_count=10,
blocked_count=0,
completed_count=1,
failed_count=0,
writes_on_read=False,
manual_review_required=True,
batch_writes_allowed=False,
items=[],
),
project_id="awoooi",
incident_id="INC-20260524-16109D",
)
assert summary["status"] == "no_related_owner_review"
assert summary["work_item"] == {
"schema_version": "km_stale_callback_owner_review_work_item_v1",
"work_item_id": (
"km-callback-owner-review:awoooi:INC-20260524-16109D"
),
"kind": "km_stale_callback_owner_review",
"status": "open",
"project_id": "awoooi",
"incident_id": "INC-20260524-16109D",
"reason": "no_matching_completion_item",
"title": (
"Telegram callback incident has no matching KM owner-review item"
),
"next_step": "review_or_queue_km_owner_review",
"target_surface": "awooop_runs_callback_evidence",
"target_href": (
"/awooop/runs?project_id=awoooi"
"&incident_id=INC-20260524-16109D"
"&callback_reply_status=sent"
),
"writes_on_read": False,
"manual_review_required": True,
"batch_writes_allowed": False,
}
def test_list_approvals_response_preserves_status_chain() -> None:

View File

@@ -1961,6 +1961,9 @@
"telegramCallbacks": {
"title": "Telegram detail / history as DB truth-first"
},
"callbackOwnerReview": {
"title": "Callback missing KM owner-review work item"
},
"ciSecretHygiene": {
"title": "CI/CD secret log exposure hardening"
},
@@ -1987,6 +1990,7 @@
"configDriftFsm": "The same drift fingerprint must expose recurrence, PR, zero diff, handoff, and next step",
"remediationQueue": "Every degraded / failed / timeout row must map to replay, reverify, ticket, or manual review",
"telegramCallbacks": "Detail and history buttons cannot depend only on Redis TTL or stale snapshots",
"callbackOwnerReview": "Telegram detail/history callbacks without a KM owner-review link must become trackable work items",
"ciSecretHygiene": "Workflows must not mount secrets in step env / action inputs; historical logs still need rotation and retention governance",
"governanceDispatch": "Governance alerts must enter dispatch and expose skipped / pending / repaired",
"knowledgeHealthcheck": "knowledge_degradation must show Hermes / OpenClaw / ElephantAlpha ownership, current stage, and owner review point",
@@ -2013,6 +2017,11 @@
"remediationQueue": "Remediation work: {total}; AI-ready: {ready}; human: {human}",
"telegramCallbacks": "Telegram callback lookup and history summary are being repaired",
"telegramCallbacksLive": "Read-only callback toast 400 is nonfatal; detail / history replies now use DB truth-chain",
"callbackOwnerReview": "Callback owner-review gaps: {open} open; callback evidence: {total}",
"callbackOwnerReviewLatest": "Latest: {incident} / {action}",
"callbackOwnerReviewQueue": "Completion queue: ready {ready}; blocked {blocked}; completed {completed}; failed {failed}",
"callbackOwnerReviewNext": "Next: {next}",
"callbackOwnerReviewEmpty": "Recent callback evidence is matched or no data is available yet",
"ciSecretHygiene": "Repo-controlled step env / action input exposure is guarded; key rotation and log retention remain",
"governance": "Unresolved governance alerts: {unresolved}; pending dispatch: {queued}",
"governanceUnavailable": "Governance events API is not responding; pending dispatch: {queued}",
@@ -2791,6 +2800,7 @@
"related": "{entryId} · {readiness} · {nextAction}",
"noRelated": "This incident has no matching owner-review completion item yet.",
"fetchFailed": "KM owner-review summary failed to load: {reason}",
"openWorkItem": "Open work item",
"statuses": {
"matched_owner_review": "Matched owner review",
"no_related_owner_review": "No matched owner review",

View File

@@ -1962,6 +1962,9 @@
"telegramCallbacks": {
"title": "Telegram 詳情 / 歷史改為 DB truth-first"
},
"callbackOwnerReview": {
"title": "Callback 未匹配 KM Owner Review 工作項"
},
"ciSecretHygiene": {
"title": "CI/CD secret log 泄漏面收斂"
},
@@ -1988,6 +1991,7 @@
"configDriftFsm": "同一 drift fingerprint 必須顯示重複、PR、零 diff、交接與下一步",
"remediationQueue": "每筆 degraded / failed / timeout 都必須映射到重跑、重驗、Ticket 或人工檢查",
"telegramCallbacks": "按下詳情與歷史不能再只依賴 Redis TTL 或舊快照",
"callbackOwnerReview": "Telegram 詳情 / 歷史若未連到 KM owner-review必須變成可追蹤工作項",
"ciSecretHygiene": "workflow 不可再把 secrets 掛在 step env / action input歷史 log 需另做輪換與保留期治理",
"governanceDispatch": "治理告警必須進 dispatch並標示 skipped / pending / repaired",
"knowledgeHealthcheck": "knowledge_degradation 必須顯示 Hermes / OpenClaw / ElephantAlpha 分工、目前階段與 owner 審核點",
@@ -2014,6 +2018,11 @@
"remediationQueue": "補救工作:{total}AI 可接手:{ready};人工:{human}",
"telegramCallbacks": "目前修補 Telegram callback 查詢鏈與歷史摘要",
"telegramCallbacksLive": "read-only callback toast 400 已非致命;詳情 / 歷史改由 DB truth-chain 回覆",
"callbackOwnerReview": "Callback owner-review 缺口:{open} 個 opencallback evidence{total}",
"callbackOwnerReviewLatest": "最新:{incident} / {action}",
"callbackOwnerReviewQueue": "Completion queue可處理 {ready};卡住 {blocked};完成 {completed};失敗 {failed}",
"callbackOwnerReviewNext": "下一步:{next}",
"callbackOwnerReviewEmpty": "近期 callback evidence 均已匹配或尚無資料",
"ciSecretHygiene": "repo 可控 step env / action input 泄漏面已加 guard仍需 key rotation 與 log retention 收斂",
"governance": "未解治理告警:{unresolved}pending dispatch{queued}",
"governanceUnavailable": "治理事件 API 目前無法回應pending dispatch{queued}",
@@ -2792,6 +2801,7 @@
"related": "{entryId} · {readiness} · {nextAction}",
"noRelated": "本 Incident 尚未對到 owner-review completion item。",
"fetchFailed": "KM owner-review 摘要讀取失敗:{reason}",
"openWorkItem": "開啟工作項",
"statuses": {
"matched_owner_review": "已匹配 owner review",
"no_related_owner_review": "未匹配 owner review",

View File

@@ -294,6 +294,23 @@ interface KmStaleCompletionSummaryItem {
can_preview?: boolean | null;
}
interface KmStaleCallbackOwnerReviewWorkItem {
schema_version?: string;
work_item_id?: string | null;
kind?: string | null;
status?: string | null;
project_id?: string | null;
incident_id?: string | null;
reason?: string | null;
title?: string | null;
next_step?: string | null;
target_surface?: string | null;
target_href?: string | null;
writes_on_read?: boolean | null;
manual_review_required?: boolean | null;
batch_writes_allowed?: boolean | null;
}
interface KmStaleCompletionSummary {
schema_version?: string;
project_id?: string | null;
@@ -313,6 +330,7 @@ interface KmStaleCompletionSummary {
items_truncated?: boolean | null;
related_total?: number;
related_items?: KmStaleCompletionSummaryItem[];
work_item?: KmStaleCallbackOwnerReviewWorkItem | null;
}
interface CallbackReplyEvent {
@@ -1468,6 +1486,7 @@ function CallbackKmCompletionSummary({
const related = Array.isArray(summary.related_items)
? summary.related_items[0]
: null;
const workItem = summary.work_item?.status === "open" ? summary.work_item : null;
return (
<div className="mt-3 border-t border-[#e0ddd4] pt-3">
@@ -1511,6 +1530,15 @@ function CallbackKmCompletionSummary({
) : (
<p className="text-[#77736a]">{t("noRelated")}</p>
)}
{workItem?.target_href ? (
<Link
href={workItem.target_href as never}
className="mt-1 inline-flex w-fit items-center gap-1.5 border border-[#d8d3c7] bg-[#faf9f3] px-2 py-1 text-xs font-semibold text-[#2e2b26] hover:border-[#d97757] hover:text-[#8a4a2f]"
>
<SearchCheck className="h-3.5 w-3.5" aria-hidden="true" />
{t("openWorkItem")}
</Link>
) : null}
</div>
</div>
);

View File

@@ -771,6 +771,54 @@ type DriftFingerprintHandoffResult = DriftFingerprintState & {
} | null;
};
type KmStaleCallbackOwnerReviewWorkItem = {
schema_version?: string;
work_item_id?: string | null;
kind?: string | null;
status?: string | null;
project_id?: string | null;
incident_id?: string | null;
reason?: string | null;
title?: string | null;
next_step?: string | null;
target_surface?: string | null;
target_href?: string | null;
writes_on_read?: boolean | null;
manual_review_required?: boolean | null;
batch_writes_allowed?: boolean | null;
};
type KmStaleCallbackCompletionSummary = {
schema_version?: string;
status?: string | null;
project_id?: string | null;
incident_id?: string | null;
reason?: string | null;
pending_count?: number;
ready_count?: number;
blocked_count?: number;
completed_count?: number;
failed_count?: number;
work_item?: KmStaleCallbackOwnerReviewWorkItem | null;
};
type CallbackReplyWorkItemEvent = {
message_id: string;
project_id: string;
status: string;
action?: string | null;
incident_id?: string | null;
event_at?: string | null;
km_stale_completion_summary?: KmStaleCallbackCompletionSummary | null;
};
type CallbackRepliesWorkItemResponse = {
items?: CallbackReplyWorkItemEvent[];
total: number;
page: number;
per_page: number;
};
type Telemetry = {
quality: AutomationQualitySummary | null;
governanceEvents: GovernanceEventsResponse | null;
@@ -787,6 +835,7 @@ type Telemetry = {
slo: SloResponse | null;
remediationHistory: RemediationHistoryResponse | null;
driftFingerprintState: DriftFingerprintState | null;
callbackReplies: CallbackRepliesWorkItemResponse | null;
statusChain: AwoooPStatusChain | null;
};
@@ -1490,6 +1539,18 @@ function governanceEventHistoryHref(eventId: string) {
return `/governance?tab=events&event_id=${encodeURIComponent(eventId)}`;
}
function callbackOwnerReviewOpenEvents(
response: CallbackRepliesWorkItemResponse | null
): CallbackReplyWorkItemEvent[] {
return (response?.items ?? []).filter((item) => {
const summary = item.km_stale_completion_summary;
return (
summary?.status === "no_related_owner_review" &&
summary.work_item?.status === "open"
);
});
}
function buildWorkItems(
telemetry: Telemetry,
t: ReturnType<typeof useTranslations>
@@ -1515,6 +1576,13 @@ function buildWorkItems(
const topKnowledgeStaleCandidate = telemetry.knowledgeStaleCandidates?.items?.[0] ?? null;
const knowledgeCompletionQueue = telemetry.knowledgeStaleOwnerReviewCompletionQueue;
const latestKnowledgeCompletion = knowledgeCompletionQueue?.items?.[0] ?? null;
const callbackOwnerReviewEvents = callbackOwnerReviewOpenEvents(
telemetry.callbackReplies
);
const latestCallbackOwnerReview = callbackOwnerReviewEvents[0] ?? null;
const latestCallbackSummary =
latestCallbackOwnerReview?.km_stale_completion_summary ?? null;
const latestCallbackWorkItem = latestCallbackSummary?.work_item ?? null;
const remediationQueue = telemetry.slo?.adr100?.verification_coverage?.remediation_queue;
const remediationTotal = remediationQueue?.total ?? 0;
const remediationReadyForAi = remediationQueue?.ready_for_ai ?? 0;
@@ -1704,6 +1772,40 @@ function buildWorkItems(
evidence: t("evidence.telegramCallbacksLive"),
href: "/awooop/runs",
},
{
id: "callbackOwnerReview",
phase: "T164",
status: callbackOwnerReviewEvents.length > 0
? "in_progress"
: telemetry.callbackReplies
? "watching"
: "blocked",
surfaceKey: "workItems",
source: "/api/v1/platform/runs/callback-replies + km_stale_completion_summary",
gateKey: "callbackOwnerReview",
evidence: t("evidence.callbackOwnerReview", {
open: callbackOwnerReviewEvents.length,
total: telemetry.callbackReplies?.total ?? 0,
}),
evidenceDetails: latestCallbackOwnerReview
? [
t("evidence.callbackOwnerReviewLatest", {
incident: latestCallbackOwnerReview.incident_id ?? "--",
action: latestCallbackOwnerReview.action ?? "--",
}),
t("evidence.callbackOwnerReviewQueue", {
ready: latestCallbackSummary?.ready_count ?? 0,
blocked: latestCallbackSummary?.blocked_count ?? 0,
completed: latestCallbackSummary?.completed_count ?? 0,
failed: latestCallbackSummary?.failed_count ?? 0,
}),
t("evidence.callbackOwnerReviewNext", {
next: latestCallbackWorkItem?.next_step ?? "--",
}),
]
: [t("evidence.callbackOwnerReviewEmpty")],
href: latestCallbackWorkItem?.target_href ?? "/awooop/runs",
},
{
id: "ciSecretHygiene",
phase: "T44",
@@ -4459,6 +4561,7 @@ export default function AwoooPWorkItemsPage() {
slo: null,
remediationHistory: null,
driftFingerprintState: null,
callbackReplies: null,
statusChain: null,
});
const [loading, setLoading] = useState(true);
@@ -4482,6 +4585,7 @@ export default function AwoooPWorkItemsPage() {
const sloUrl = `${API_BASE}/api/v1/ai/slo`;
const remediationHistoryUrl = `${API_BASE}/api/v1/ai/slo/remediation/history?limit=80`;
const driftFingerprintUrl = `${API_BASE}/api/v1/drift/fingerprints/state?namespace=awoooi-prod`;
const callbackRepliesUrl = `${API_BASE}/api/v1/platform/runs/callback-replies?project_id=${encodedProjectId}&per_page=100`;
const [
quality,
@@ -4499,6 +4603,7 @@ export default function AwoooPWorkItemsPage() {
slo,
remediationHistory,
driftFingerprintState,
callbackReplies,
] = await Promise.all([
fetchJson<AutomationQualitySummary>(qualityUrl, 15000),
fetchJson<GovernanceEventsResponse>(governanceEventsUrl),
@@ -4515,6 +4620,7 @@ export default function AwoooPWorkItemsPage() {
fetchJson<SloResponse>(sloUrl),
fetchJson<RemediationHistoryResponse>(remediationHistoryUrl),
fetchJson<DriftFingerprintState>(driftFingerprintUrl, 12000),
fetchJson<CallbackRepliesWorkItemResponse>(callbackRepliesUrl, 12000),
]);
const statusChainIncidentId = selectStatusChainIncidentId(
@@ -4548,6 +4654,7 @@ export default function AwoooPWorkItemsPage() {
slo,
remediationHistory,
driftFingerprintState,
callbackReplies,
statusChain,
});
setLastUpdated(new Date());