feat(governance): explain verifier failures
All checks were successful
Code Review / ai-code-review (push) Successful in 14s
CD Pipeline / tests (push) Successful in 1m21s
CD Pipeline / build-and-deploy (push) Successful in 3m23s
CD Pipeline / post-deploy-checks (push) Successful in 1m18s

This commit is contained in:
Your Name
2026-05-14 20:49:20 +08:00
parent dd269b195c
commit bad48dee04
5 changed files with 364 additions and 1 deletions

View File

@@ -84,6 +84,30 @@ interface Adr100VerificationCoverage {
success: boolean
created_at?: string | null
}>
recent_non_success?: Array<{
auto_repair_id: string
incident_id: string
incident_status: string
incident_severity: string
alert_category?: string | null
alertname?: string | null
auto_success: boolean
playbook_id?: string | null
playbook_name?: string | null
triggered_by?: string | null
risk_level?: string | null
verification_result: string
failure_class: string
next_step: string
auto_error_excerpt?: string | null
evidence_excerpt?: string | null
auto_created_at?: string | null
verification_collected_at?: string | null
}>
non_success_breakdown?: {
by_verification_result?: Array<{ name: string; count: number }>
by_failure_class?: Array<{ name: string; count: number }>
}
}
interface SummaryApiResponse {
@@ -123,6 +147,32 @@ function formatPercent(value?: number | null): string {
return value == null ? '--' : `${(value * 100).toFixed(1)}%`
}
function failureClassKey(value?: string | null): string {
if (value === 'unsupported_action_scheme') return value
if (value === 'verifier_missing_promql') return value
if (value === 'verifier_target_missing_pod') return value
if (value === 'auto_repair_execution_failed') return value
if (value === 'verification_failed') return value
if (value === 'verification_timeout') return value
if (value === 'verification_degraded') return value
return 'unknown'
}
function nextStepKey(value?: string | null): string {
if (value === 'normalize_playbook_executor') return value
if (value === 'add_verifier_query_template') return value
if (value === 'map_verifier_target') return value
if (value === 'review_auto_repair_execution') return value
if (value === 'escalate_verification_failure') return value
if (value === 'review_degraded_verification') return value
return 'review_degraded_verification'
}
function compactLabel(value?: string | null, fallback = '--'): string {
if (!value) return fallback
return value.length > 54 ? `${value.slice(0, 54)}...` : value
}
function buildMetrics(api: SloApiResponse): SloMetric[] {
const adr100Metrics = api.adr100?.metrics
if (adr100Metrics?.length) {
@@ -186,6 +236,8 @@ function VerificationCoveragePanel({ coverage }: { coverage?: Adr100Verification
{ label: t('unverifiedAuto'), value: String(coverage?.unverified_auto ?? '--') },
{ label: t('coverageRate'), value: formatPercent(coverage?.coverage_rate) },
]
const failureBreakdown = coverage?.non_success_breakdown?.by_failure_class ?? []
const recentFindings = coverage?.recent_non_success ?? []
return (
<GlassCard variant="subtle" padding="md">
@@ -234,6 +286,78 @@ function VerificationCoveragePanel({ coverage }: { coverage?: Adr100Verification
<span>{t('successRate')} {formatPercent(coverage?.verification_success_rate)}</span>
<span>{t('lastVerified')} {coverage?.last_verified_auto_at ?? '--'}</span>
</div>
{failureBreakdown.length > 0 && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
<div style={{ fontFamily: 'Syne, sans-serif', fontSize: 11, fontWeight: 700, color: '#141413' }}>
{t('failureBreakdown')}
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>
{failureBreakdown.map(item => (
<span key={item.name} style={{
display: 'inline-flex',
alignItems: 'center',
minHeight: 24,
padding: '3px 7px',
borderRadius: 6,
border: '0.5px solid #F59E0B40',
background: 'rgba(245,158,11,0.08)',
fontFamily: "'DM Mono', monospace",
fontSize: 10,
color: '#7c5a10',
}}>
{t(`failureClass.${failureClassKey(item.name)}`)} · {item.count}
</span>
))}
</div>
</div>
)}
{recentFindings.length > 0 && (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<div style={{ fontFamily: 'Syne, sans-serif', fontSize: 11, fontWeight: 700, color: '#141413' }}>
{t('recentFindings')}
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
{recentFindings.slice(0, 5).map(item => (
<div key={`${item.auto_repair_id}-${item.incident_id}`} style={{
display: 'grid',
gridTemplateColumns: 'minmax(120px, 0.8fr) minmax(180px, 1.4fr) minmax(160px, 1fr)',
gap: 10,
alignItems: 'center',
minWidth: 0,
paddingTop: 7,
borderTop: '0.5px solid rgba(20,20,19,0.08)',
}} className="slo-finding-row">
<div style={{ minWidth: 0 }}>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#141413' }}>
{item.incident_id}
</div>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 9, color: '#87867f', marginTop: 2 }}>
{item.incident_status} · {item.verification_result}
</div>
</div>
<div style={{ minWidth: 0 }}>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#7c5a10' }}>
{t(`failureClass.${failureClassKey(item.failure_class)}`)}
</div>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 9, color: '#87867f', marginTop: 2, overflowWrap: 'anywhere' }}>
{compactLabel(item.alertname)} · {compactLabel(item.playbook_name)}
</div>
</div>
<div style={{ minWidth: 0 }}>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 10, color: '#141413' }}>
{t(`nextStep.${nextStepKey(item.next_step)}`)}
</div>
<div style={{ fontFamily: "'DM Mono', monospace", fontSize: 9, color: '#87867f', marginTop: 2, overflowWrap: 'anywhere' }}>
{compactLabel(item.auto_error_excerpt ?? item.evidence_excerpt)}
</div>
</div>
</div>
))}
</div>
</div>
)}
</div>
</GlassCard>
)
@@ -358,6 +482,7 @@ export function SloTab() {
@media (max-width: 640px) {
.slo-kpi-grid > * { flex: 0 0 100%; min-width: 0; }
.slo-coverage-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
.slo-finding-row { grid-template-columns: 1fr !important; }
}
`}</style>
</div>