feat(awooop): apply source correlation links
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
CD Pipeline / tests (push) Successful in 4m1s
CD Pipeline / build-and-deploy (push) Successful in 4m8s
CD Pipeline / post-deploy-checks (push) Successful in 2m2s

This commit is contained in:
Your Name
2026-05-21 10:23:29 +08:00
parent d25237a31f
commit fe3bf5dc18
8 changed files with 1011 additions and 7 deletions

View File

@@ -140,6 +140,14 @@ type RecurrenceItem = {
reviewer_id?: string | null;
recorded_at?: string | null;
} | null;
source_correlation_apply?: {
apply_id?: string | null;
apply_status?: string | null;
target_incident_id?: string | null;
source_event_id?: string | null;
source_event_provider_event_id?: string | null;
recorded_at?: string | null;
} | null;
};
type RecurrenceResponse = {
@@ -158,6 +166,7 @@ type RecurrenceResponse = {
failed_repair_group_total?: number;
source_correlation_review_group_total?: number;
source_correlation_decision_recorded_group_total?: number;
source_correlation_applied_group_total?: number;
};
items: RecurrenceItem[];
};
@@ -174,8 +183,11 @@ type RecurrenceWorkItemActionResult = {
decision?: string | null;
review_status?: string | null;
review_record_status?: string | null;
apply_status?: string | null;
target_incident_id?: string | null;
latest_provider_event_id?: string | null;
source_event_id?: string | null;
source_event_provider_event_id?: string | null;
allowed?: boolean | null;
executed?: boolean | null;
safety_level?: string | null;
@@ -223,7 +235,14 @@ type RecurrenceWorkItemActionResult = {
};
type RecurrenceWorkItemActionState = {
loading?: "preview" | "dryRun" | "handoff" | "acceptSource" | "rejectSource" | null;
loading?:
| "preview"
| "dryRun"
| "handoff"
| "acceptSource"
| "rejectSource"
| "applySource"
| null;
result?: RecurrenceWorkItemActionResult | null;
error?: string | null;
};
@@ -904,6 +923,19 @@ function staleRatioRecheckStatusKey(status?: string | null) {
return "unknown";
}
function sourceApplyStatusKey(status?: string | null) {
if (
status === "ready_to_apply" ||
status === "applied" ||
status === "partial" ||
status === "record_failed" ||
status === "blocked"
) {
return status;
}
return "unknown";
}
function formatStaleRatio(value: number) {
return `${(value * 100).toFixed(1)}%`;
}
@@ -948,6 +980,8 @@ function buildWorkItems(
recurrenceSummary?.source_correlation_review_group_total ?? 0;
const recurrenceSourceReviewRecorded =
recurrenceSummary?.source_correlation_decision_recorded_group_total ?? 0;
const recurrenceSourceApplied =
recurrenceSummary?.source_correlation_applied_group_total ?? 0;
const latestRecurrenceOpenItem = recurrenceOpenItems(telemetry.eventRecurrence)[0] ?? null;
const driftState = telemetry.driftFingerprintState;
const driftFsmKey = driftFsmStateKey(driftState?.fsm_state);
@@ -1023,12 +1057,18 @@ function buildWorkItems(
t("evidence.recurrenceSourceReviewRecorded", {
count: recurrenceSourceReviewRecorded,
}),
t("evidence.recurrenceSourceApplied", {
count: recurrenceSourceApplied,
}),
]
: [
t("evidence.recurrenceEmpty"),
t("evidence.recurrenceSourceReviewRecorded", {
count: recurrenceSourceReviewRecorded,
}),
t("evidence.recurrenceSourceApplied", {
count: recurrenceSourceApplied,
}),
],
href: latestRecurrenceOpenItem?.work_item?.work_item_id
? `/awooop/work-items?project_id=${encodeURIComponent(telemetry.eventRecurrence?.project_id ?? "awoooi")}&work_item_id=${encodeURIComponent(latestRecurrenceOpenItem.work_item.work_item_id)}${latestRecurrenceOpenItem.work_item.incident_id ? `&incident_id=${encodeURIComponent(latestRecurrenceOpenItem.work_item.incident_id)}` : ""}`
@@ -1342,7 +1382,13 @@ function RecurrenceWorkQueuePanel({
const summary = recurrence?.summary;
const runWorkItemAction = useCallback(async (
workItemId: string,
action: "preview" | "dryRun" | "handoff" | "acceptSource" | "rejectSource",
action:
| "preview"
| "dryRun"
| "handoff"
| "acceptSource"
| "rejectSource"
| "applySource",
targetIncidentId?: string | null
) => {
setActionState((current) => ({
@@ -1381,6 +1427,18 @@ function RecurrenceWorkQueuePanel({
},
15000
);
} else if (action === "applySource") {
result = await postJson<RecurrenceWorkItemActionResult>(
`${API_BASE}/api/v1/platform/events/dossier/recurrence/source-correlation/apply`,
{
project_id: projectId,
work_item_id: workItemId,
reviewer_id: "operator_console",
operator_note: "operator_console_apply_source_match",
limit: 300,
},
15000
);
} else {
result = await postJson<RecurrenceWorkItemActionResult>(
`${API_BASE}/api/v1/platform/events/dossier/recurrence/source-correlation/review`,
@@ -1407,7 +1465,12 @@ function RecurrenceWorkQueuePanel({
error: result ? null : t("actions.failed"),
},
}));
if (result?.history?.recorded || result?.review_record_status === "recorded") {
if (
result?.history?.recorded ||
result?.review_record_status === "recorded" ||
result?.apply_status === "applied" ||
result?.apply_status === "partial"
) {
onRecorded();
}
}, [onRecorded, projectId, t]);
@@ -1437,6 +1500,11 @@ function RecurrenceWorkQueuePanel({
count: summary?.source_correlation_review_group_total ?? 0,
})}
</span>
<span className="border border-[#9bc7a4] bg-[#f0faf2] px-2 py-0.5 text-[#17602a]">
{t("sourceApplied", {
count: summary?.source_correlation_applied_group_total ?? 0,
})}
</span>
</div>
</div>
@@ -1468,6 +1536,7 @@ function RecurrenceWorkQueuePanel({
const handoffStatusKey = recurrenceHandoffStatusKey(actionResult?.handoff_status);
const handoffKindKey = recurrenceHandoffKindKey(actionResult?.handoff_kind);
const sourceReview = item.source_correlation_review;
const sourceApply = item.source_correlation_apply;
const isSourceReview = workItem?.kind === "source_correlation_review";
const workItemOpen = workItem?.status === "open";
const targetIncidentId = firstIncidentId(
@@ -1485,6 +1554,13 @@ function RecurrenceWorkQueuePanel({
actionResult?.review_status ??
sourceReview?.review_status
);
const sourceApplyStatus = actionResult?.apply_status ?? sourceApply?.apply_status;
const sourceApplyStatusKeyValue = sourceApplyStatusKey(sourceApplyStatus);
const canApplySource =
isSourceReview &&
sourceReview?.decision === "accepted" &&
Boolean(targetIncidentId) &&
sourceApplyStatusKeyValue !== "applied";
return (
<article
@@ -1555,6 +1631,19 @@ function RecurrenceWorkQueuePanel({
})}
</p>
) : null}
{sourceApply || actionResult?.apply_status ? (
<p>
{t("sourceApplyStatus", {
status: t(
`actions.sourceApplyStatuses.${sourceApplyStatusKeyValue}` as never
),
event:
actionResult?.source_event_provider_event_id ??
sourceApply?.source_event_provider_event_id ??
"--",
})}
</p>
) : null}
</div>
<div className="mt-3 flex flex-wrap items-center gap-2">
{workItemId ? (
@@ -1627,6 +1716,20 @@ function RecurrenceWorkQueuePanel({
? t("actions.sourceRejecting")
: t("actions.sourceReject")}
</button>
<button
type="button"
onClick={() => runWorkItemAction(workItemId, "applySource")}
disabled={
currentAction?.loading === "applySource" ||
!canApplySource
}
className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2 py-1 text-xs font-semibold text-[#2e2b26] hover:border-[#1f6feb] hover:bg-[#edf4ff] hover:text-[#0f4fa8] disabled:cursor-not-allowed disabled:opacity-60"
>
<GitBranch className="h-3.5 w-3.5" aria-hidden="true" />
{currentAction?.loading === "applySource"
? t("actions.sourceApplying")
: t("actions.sourceApply")}
</button>
</>
) : null}
</>
@@ -1708,6 +1811,16 @@ function RecurrenceWorkQueuePanel({
})}
</p>
) : null}
{actionResult.apply_status ? (
<p>
{t("actions.sourceApplyResult", {
status: t(
`actions.sourceApplyStatuses.${sourceApplyStatusKeyValue}` as never
),
event: actionResult.source_event_provider_event_id ?? "--",
})}
</p>
) : null}
{actionResult.handoff_status ? (
<p>
{t("actions.handoffStatus", {