feat(governance): explain verifier failures
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user