feat(adr100): bridge playbook authoring approvals
This commit is contained in:
@@ -196,7 +196,10 @@ type RecurrenceWorkItemActionResult = {
|
||||
writes_source_event?: boolean | null;
|
||||
writes_auto_repair_result?: boolean | null;
|
||||
writes_ticket?: boolean | null;
|
||||
writes_approval_record?: boolean | null;
|
||||
creates_external_ticket?: boolean | null;
|
||||
deduplicated?: boolean | null;
|
||||
fingerprint?: string | null;
|
||||
verification_result_preview?: string | null;
|
||||
next_step?: string | null;
|
||||
checks?: Array<{ name?: string | null; passed?: boolean | null; detail?: string | null }>;
|
||||
@@ -214,12 +217,24 @@ type RecurrenceWorkItemActionResult = {
|
||||
labels?: string[] | null;
|
||||
body_preview?: string | null;
|
||||
} | null;
|
||||
approval_id?: string | null;
|
||||
approval?: {
|
||||
id?: string | null;
|
||||
status?: string | null;
|
||||
risk_level?: string | null;
|
||||
required_signatures?: number | null;
|
||||
current_signatures?: number | null;
|
||||
requested_by?: string | null;
|
||||
incident_id?: string | null;
|
||||
matched_playbook_id?: string | null;
|
||||
} | null;
|
||||
plan?: {
|
||||
step?: string | null;
|
||||
flywheel_node?: string | null;
|
||||
agent_id?: string | null;
|
||||
required_scope?: string | null;
|
||||
target_action?: string | null;
|
||||
writes?: string[] | null;
|
||||
} | null;
|
||||
read_model_route?: {
|
||||
agent_id?: string | null;
|
||||
@@ -239,6 +254,7 @@ type RecurrenceWorkItemActionState = {
|
||||
loading?:
|
||||
| "preview"
|
||||
| "dryRun"
|
||||
| "approval"
|
||||
| "handoff"
|
||||
| "acceptSource"
|
||||
| "rejectSource"
|
||||
@@ -2661,7 +2677,7 @@ function Adr100RemediationQueuePanel({
|
||||
|
||||
const runAction = useCallback(async (
|
||||
item: RemediationQueueItem,
|
||||
action: "preview" | "dryRun"
|
||||
action: "preview" | "dryRun" | "approval"
|
||||
) => {
|
||||
const workItemId = item.work_item_id ?? "";
|
||||
if (!workItemId) return;
|
||||
@@ -2677,7 +2693,13 @@ function Adr100RemediationQueuePanel({
|
||||
`${API_BASE}/api/v1/ai/slo/remediation/preview?work_item_id=${encodeURIComponent(workItemId)}&mode=${encodeURIComponent(mode)}`,
|
||||
12000
|
||||
)
|
||||
: await postJson<RecurrenceWorkItemActionResult>(
|
||||
: action === "approval"
|
||||
? await postJson<RecurrenceWorkItemActionResult>(
|
||||
`${API_BASE}/api/v1/ai/slo/remediation/approval-request`,
|
||||
{ work_item_id: workItemId, mode: "approval" },
|
||||
15000
|
||||
)
|
||||
: await postJson<RecurrenceWorkItemActionResult>(
|
||||
`${API_BASE}/api/v1/ai/slo/remediation/dry-run`,
|
||||
{ work_item_id: workItemId, mode },
|
||||
15000
|
||||
@@ -2742,6 +2764,9 @@ function Adr100RemediationQueuePanel({
|
||||
const state = workItemId ? actionState[workItemId] : undefined;
|
||||
const result = state?.result ?? null;
|
||||
const ticketPreview = result?.ticket_preview ?? null;
|
||||
const approval = result?.approval ?? null;
|
||||
const canCreateApproval = item.remediation_status === "needs_playbook_ticket"
|
||||
|| item.remediation_action === "promote_diagnostic_to_repair_playbook";
|
||||
return (
|
||||
<article key={workItemId || item.incident_id || item.auto_repair_id} className="min-w-0 bg-white p-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
@@ -2799,6 +2824,17 @@ function Adr100RemediationQueuePanel({
|
||||
<FileText className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
{state?.loading === "dryRun" ? t("actions.loading") : t("actions.dryRun")}
|
||||
</button>
|
||||
{canCreateApproval ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => runAction(item, "approval")}
|
||||
disabled={!workItemId || state?.loading === "approval"}
|
||||
className="inline-flex items-center gap-1.5 border border-[#d8d3c7] bg-white px-2.5 py-1 text-xs font-semibold text-[#141413] hover:border-[#d97757] disabled:opacity-50"
|
||||
>
|
||||
<ShieldCheck className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
{state?.loading === "approval" ? t("actions.loading") : t("actions.approval")}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{state?.error ? (
|
||||
@@ -2816,7 +2852,22 @@ function Adr100RemediationQueuePanel({
|
||||
incident: String(result.writes_incident_state ?? false),
|
||||
autoRepair: String(result.writes_auto_repair_result ?? false),
|
||||
})}</span>
|
||||
{result.writes_approval_record ? (
|
||||
<span className="font-mono">{t("result.approvalWrite")}</span>
|
||||
) : null}
|
||||
{result.deduplicated ? (
|
||||
<span className="font-mono">{t("result.deduplicated")}</span>
|
||||
) : null}
|
||||
</div>
|
||||
{approval ? (
|
||||
<p className="mt-2 font-mono text-[11px] text-[#5f5b52]">
|
||||
{t("result.approval", {
|
||||
id: approval.id ?? result.approval_id ?? "--",
|
||||
status: approval.status ?? "--",
|
||||
risk: approval.risk_level ?? "--",
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
{ticketPreview ? (
|
||||
<div className="mt-2 border-t border-[#e0ddd4] pt-2">
|
||||
<p className="font-semibold text-[#141413]">{ticketPreview.title ?? t("ticketFallback")}</p>
|
||||
|
||||
Reference in New Issue
Block a user