feat(mcp): run governed version lifecycle receipts
This commit is contained in:
@@ -77,6 +77,39 @@ interface RuntimeReadback {
|
||||
total_chunks: number
|
||||
sources: number
|
||||
}
|
||||
version_lifecycle?: {
|
||||
status: string
|
||||
controller_configured: boolean
|
||||
controller_owner: string
|
||||
lock_backend?: string
|
||||
interval_seconds: number
|
||||
latest_run?: {
|
||||
run_id: string
|
||||
trace_id: string
|
||||
work_item_id: string
|
||||
status: string
|
||||
candidate_count: number
|
||||
observed_count: number
|
||||
baseline_count: number
|
||||
changed_count: number
|
||||
policy_blocked_count: number
|
||||
error_count: number
|
||||
started_at: string
|
||||
ended_at?: string | null
|
||||
fresh: boolean
|
||||
} | null
|
||||
observations?: Array<{
|
||||
candidate_id: string
|
||||
catalog_source: string
|
||||
observed_version?: string | null
|
||||
discovery_status: string
|
||||
change_state: string
|
||||
lifecycle_terminal: string
|
||||
error_class?: string | null
|
||||
observed_at: string
|
||||
}>
|
||||
blockers?: string[]
|
||||
}
|
||||
federated_product_runtime_receipt_count?: number
|
||||
federated_product_runtime_receipt_expected?: number
|
||||
}
|
||||
@@ -226,6 +259,13 @@ export default function McpControlPlanePage({ params }: { params: { locale: stri
|
||||
const runtime = overview?.runtime
|
||||
const gateway = runtime?.gateway_audit_24h
|
||||
const rag = runtime?.rag
|
||||
const lifecycleRuntime = runtime?.version_lifecycle
|
||||
const lifecycleObservationByCandidate = useMemo(
|
||||
() => new Map(
|
||||
(lifecycleRuntime?.observations ?? []).map(row => [row.candidate_id, row]),
|
||||
),
|
||||
[lifecycleRuntime?.observations],
|
||||
)
|
||||
|
||||
return (
|
||||
<AppLayout locale={params.locale}>
|
||||
@@ -440,7 +480,14 @@ export default function McpControlPlanePage({ params }: { params: { locale: stri
|
||||
<td className="px-3 py-3 text-[#5f5b54]">{item.catalog_source}</td>
|
||||
<td className="px-3 py-3"><StatusBadge value={item.decision} /></td>
|
||||
<td className="px-3 py-3 text-[#5f5b54]">{item.scope}</td>
|
||||
<td className="px-3 py-3 text-[#5f5b54]">{item.version_state}</td>
|
||||
<td className="px-3 py-3 text-[#5f5b54]">
|
||||
<div>{lifecycleObservationByCandidate.get(item.capability_id)?.observed_version ?? item.version_state}</div>
|
||||
{lifecycleObservationByCandidate.has(item.capability_id) ? (
|
||||
<div className="mt-1 text-[10px] text-[#8a867e]">
|
||||
{lifecycleObservationByCandidate.get(item.capability_id)?.change_state.replaceAll('_', ' ')}
|
||||
</div>
|
||||
) : null}
|
||||
</td>
|
||||
<td className="px-3 py-3 text-[#5f5b54]">{item.digest_state} / {item.sbom_state} / {item.signature_state}</td>
|
||||
<td className="px-3 py-3">
|
||||
{item.deployment_allowed ? <CheckCircle2 className="h-4 w-4 text-[#2f7d54]" aria-label={t('external.allowed')} /> : <XCircle className="h-4 w-4 text-[#b2432d]" aria-label={t('external.blocked')} />}
|
||||
@@ -470,6 +517,25 @@ export default function McpControlPlanePage({ params }: { params: { locale: stri
|
||||
<div role="status" className="mt-4 rounded-xl border border-[#e8c77a] bg-[#fff9e9] p-3 text-xs leading-5 text-[#705219]">
|
||||
{t('lifecycle.runtimeState', { value: overview.version_lifecycle.runtime_state })}
|
||||
</div>
|
||||
{lifecycleRuntime?.latest_run ? (
|
||||
<div className="mt-3 rounded-xl border border-[#c8dfcf] bg-[#f2faf4] p-3 text-xs text-[#285a37]">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<span className="font-bold">{t('lifecycle.latestReceipt')}</span>
|
||||
<StatusBadge value={lifecycleRuntime.latest_run.status} />
|
||||
</div>
|
||||
<dl className="mt-3 grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<div><dt className="text-[#66816d]">{t('lifecycle.observed')}</dt><dd className="mt-1 text-lg font-bold">{lifecycleRuntime.latest_run.observed_count}/{lifecycleRuntime.latest_run.candidate_count}</dd></div>
|
||||
<div><dt className="text-[#66816d]">{t('lifecycle.changed')}</dt><dd className="mt-1 text-lg font-bold">{lifecycleRuntime.latest_run.changed_count}</dd></div>
|
||||
<div><dt className="text-[#66816d]">{t('lifecycle.blocked')}</dt><dd className="mt-1 text-lg font-bold">{lifecycleRuntime.latest_run.policy_blocked_count}</dd></div>
|
||||
<div><dt className="text-[#66816d]">{t('lifecycle.errors')}</dt><dd className="mt-1 text-lg font-bold">{lifecycleRuntime.latest_run.error_count}</dd></div>
|
||||
</dl>
|
||||
<p className="mt-3 break-all font-mono text-[10px] text-[#66816d]">{lifecycleRuntime.latest_run.work_item_id} · {lifecycleRuntime.latest_run.run_id}</p>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mt-3 rounded-xl border border-[#dedbd1] bg-[#fbfaf6] p-3 text-xs text-[#77736a]">
|
||||
{t('lifecycle.pendingReceipt')}
|
||||
</p>
|
||||
)}
|
||||
</article>
|
||||
|
||||
<article className="rounded-2xl border border-[#dedbd1] bg-white p-5" aria-labelledby="security-title">
|
||||
|
||||
Reference in New Issue
Block a user