feat(web): enrich Runs apply gate handoff
Some checks failed
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / tests (push) Successful in 1m37s
CD Pipeline / build-and-deploy (push) Successful in 4m35s
CD Pipeline / post-deploy-checks (push) Successful in 1m43s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
Some checks failed
Code Review / ai-code-review (push) Successful in 15s
CD Pipeline / tests (push) Successful in 1m37s
CD Pipeline / build-and-deploy (push) Successful in 4m35s
CD Pipeline / post-deploy-checks (push) Successful in 1m43s
Ansible / Reboot Recovery Contract / validate (push) Has been cancelled
This commit is contained in:
@@ -419,6 +419,10 @@ export function AwoooPStatusChainPanel({
|
||||
const mcpGatewayTotal = mcpGateway.total ?? 0;
|
||||
const mcpGatewayProblemTotal = (mcpGateway.failed ?? 0) + (mcpGateway.blocked ?? 0);
|
||||
const executionTotal = execution.operation_total ?? 0;
|
||||
const handoffCandidate = automationHandoff?.candidate;
|
||||
const handoffAssets = automationHandoff?.asset_ids;
|
||||
const ownerReviewChecklist = automationHandoff?.owner_review_checklist ?? [];
|
||||
const forbiddenActions = automationHandoff?.forbidden_actions ?? [];
|
||||
const sourceToolchainTone: SourceFlowTone = sourceCorrelation
|
||||
? (sourceLinkVerified ? "success" : (sourceVerificationBlocked ? "blocked" : "warning"))
|
||||
: "neutral";
|
||||
@@ -516,6 +520,18 @@ export function AwoooPStatusChainPanel({
|
||||
},
|
||||
];
|
||||
const handoffGates = automationHandoff?.gates ?? [];
|
||||
const handoffCandidateCards = [
|
||||
{ key: "catalog", label: t("applyGate.catalog"), value: handoffCandidate?.catalog_id },
|
||||
{ key: "risk", label: t("applyGate.risk"), value: handoffCandidate?.risk_level },
|
||||
{ key: "matchScore", label: t("applyGate.matchScore"), value: handoffCandidate?.match_score },
|
||||
{ key: "checkPlaybook", label: t("applyGate.checkPlaybook"), value: handoffCandidate?.check_mode_playbook_path },
|
||||
{ key: "applyPlaybook", label: t("applyGate.applyPlaybook"), value: handoffCandidate?.apply_playbook_path },
|
||||
];
|
||||
const handoffAssetCards = [
|
||||
{ key: "dryRunAsset", label: t("applyGate.dryRunAsset"), value: handoffAssets?.dry_run },
|
||||
{ key: "applyAsset", label: t("applyGate.applyAsset"), value: handoffAssets?.apply_candidate },
|
||||
{ key: "verifierAsset", label: t("applyGate.verifierAsset"), value: handoffAssets?.verifier },
|
||||
];
|
||||
const handoffGateLabel = (key: string | null | undefined) => {
|
||||
const labels: Record<string, string> = {
|
||||
dry_run: t("applyGate.gates.dryRun"),
|
||||
@@ -775,23 +791,77 @@ export function AwoooPStatusChainPanel({
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.workItem")}</p>
|
||||
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}>
|
||||
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}>
|
||||
{valueOrEmpty(automationHandoff.work_item_id, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.applyCandidate")}</p>
|
||||
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.candidate?.apply_playbook_path, emptyLabel)}>
|
||||
{valueOrEmpty(automationHandoff.candidate?.apply_playbook_path, emptyLabel)}
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.ownerGate")}</p>
|
||||
<p className="mt-2 break-words font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.owner_review_gate, emptyLabel)}>
|
||||
{valueOrEmpty(automationHandoff.owner_review_gate, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.verifier")}</p>
|
||||
<p className="mt-2 truncate font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.asset_ids?.verifier, emptyLabel)}>
|
||||
{valueOrEmpty(automationHandoff.asset_ids?.verifier, emptyLabel)}
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.nextAction")}</p>
|
||||
<p className="mt-2 break-words font-mono text-sm text-[#141413]" title={valueOrEmpty(automationHandoff.next_action, emptyLabel)}>
|
||||
{valueOrEmpty(automationHandoff.next_action, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-5">
|
||||
{handoffCandidateCards.map((item) => (
|
||||
<div key={item.key} className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
|
||||
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(item.value, emptyLabel)}>
|
||||
{valueOrEmpty(item.value, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] md:grid-cols-3">
|
||||
{handoffAssetCards.map((item) => (
|
||||
<div key={item.key} className="min-w-0 bg-white px-4 py-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{item.label}</p>
|
||||
<p className="mt-2 break-all font-mono text-sm text-[#141413]" title={valueOrEmpty(item.value, emptyLabel)}>
|
||||
{valueOrEmpty(item.value, emptyLabel)}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid gap-px bg-[#e0ddd4] lg:grid-cols-2">
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.checklistTitle")}</p>
|
||||
<span className="border border-[#d8d3c7] bg-[#faf9f3] px-2 py-0.5 font-mono text-[11px] text-[#5f5b52]">
|
||||
{ownerReviewChecklist.length}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="mt-3 space-y-2">
|
||||
{(ownerReviewChecklist.length ? ownerReviewChecklist : [emptyLabel]).map((item, index) => (
|
||||
<li key={`${item}-${index}`} className="flex min-w-0 items-start gap-2 text-xs leading-5 text-[#141413]">
|
||||
<CheckCircle2 className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#17602a]" aria-hidden="true" />
|
||||
<span className="min-w-0 break-words font-mono">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="min-w-0 bg-white px-4 py-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-xs font-semibold text-[#77736a]">{t("applyGate.forbiddenTitle")}</p>
|
||||
<span className="border border-[#e2a29b] bg-[#fff0ef] px-2 py-0.5 font-mono text-[11px] text-[#9f2f25]">
|
||||
{forbiddenActions.length}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="mt-3 space-y-2">
|
||||
{(forbiddenActions.length ? forbiddenActions : [emptyLabel]).map((item, index) => (
|
||||
<li key={`${item}-${index}`} className="flex min-w-0 items-start gap-2 text-xs leading-5 text-[#141413]">
|
||||
<TriangleAlert className="mt-0.5 h-3.5 w-3.5 shrink-0 text-[#9f2f25]" aria-hidden="true" />
|
||||
<span className="min-w-0 break-words font-mono">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user