/** * The traceable-chain view: a vertical evidence flow from raw failure to * human decision, each stage expandable into tier-color-coded items. */ import { useState } from 'react'; import Accordion from '@mui/material/Accordion'; import AccordionDetails from '@mui/material/AccordionDetails'; import AccordionSummary from '@mui/material/AccordionSummary'; import Box from '@mui/material/Box'; import Chip from '@mui/material/Chip'; import Typography from '@mui/material/Typography'; import { ChevronDown } from 'lucide-react'; import type { EvidenceItem, RetrospectiveScorecard } from '../../types/retro'; import { DETERMINATION_COLOR, LAUNCH_COLORS, SEVERITY_COLORS, TIER_META } from './tierTheme'; interface StageDef { id: string; title: string; color: string; badge: string; items: { primary: string; secondary: string; color: string }[]; } function evidenceRow(e: EvidenceItem) { const meta = TIER_META[e.tier]; return { primary: `${e.key}${e.value !== null ? ` = ${e.value}` : ''}`, secondary: `${e.statement} — ${e.provenance}`, color: meta.color, }; } function buildStages(sc: RetrospectiveScorecard): StageDef[] { const byTier = (tier: EvidenceItem['tier']) => sc.evidence.filter((e) => e.tier === tier && !e.key.startsWith('policy_') && e.key !== 'severity_divergence'); const observed = byTier('TIER1_OBSERVED'); const derived = byTier('TIER2_DERIVED').filter((e) => e.key !== 'behavioral_impact'); const behavioral = sc.evidence.filter((e) => e.key === 'behavioral_impact'); const retrieved = byTier('TIER3_RETRIEVED'); const policyItems = sc.evidence.filter( (e) => e.key === 'policy_severity' || e.key === 'severity_divergence' ); return [ { id: 'raw', title: '1 · Raw failure artifact', color: '#8a949e', badge: sc.failure_type, items: [ { primary: sc.evaluation_id, secondary: `${String(sc.scenario?.description ?? 'evaluation log')} (weather: ${String( sc.scenario?.weather ?? '?' )}, ODD: ${String(sc.scenario?.odd_status ?? '?')})`, color: '#8a949e', }, ], }, { id: 'observed', title: `2 · Observed evidence — FACT (${observed.length})`, color: TIER_META.TIER1_OBSERVED.color, badge: 'TIER 1', items: observed.map(evidenceRow), }, { id: 'derived', title: `3 · Derived metrics — DERIVED FACT (${derived.length})`, color: TIER_META.TIER2_DERIVED.color, badge: 'TIER 2', items: derived.map(evidenceRow), }, { id: 'retrieved', title: `4 · Retrieved engineering evidence (${retrieved.length})`, color: TIER_META.TIER3_RETRIEVED.color, badge: 'TIER 3', items: retrieved.map(evidenceRow), }, { id: 'hypothesis', title: `5 · Agent hypotheses — INFERENCE (${sc.root_cause_hypotheses.length})`, color: TIER_META.TIER4_AI_HYPOTHESIS.color, badge: 'TIER 4', items: sc.root_cause_hypotheses.map((h) => ({ primary: `${Math.round(h.confidence * 100)}% confidence`, secondary: `${h.hypothesis} [supported by: ${h.supporting_evidence_keys.join(', ') || 'n/a'}; missing: ${ h.missing_evidence.join(', ') || 'none' }]`, color: TIER_META.TIER4_AI_HYPOTHESIS.color, })), }, { id: 'behavioral', title: '6 · Behavioral analysis', color: TIER_META.TIER2_DERIVED.color, badge: 'DERIVED', items: behavioral.length ? behavioral.map(evidenceRow) : [{ primary: 'behavioral consequence', secondary: sc.behavioral_consequence, color: TIER_META.TIER2_DERIVED.color }], }, { id: 'policy', title: '7 · Safety policy (deterministic)', color: DETERMINATION_COLOR, badge: sc.policy_version, items: [ ...policyItems.map(evidenceRow), ...sc.launch_rationale.map((r) => ({ primary: 'launch gate', secondary: r, color: DETERMINATION_COLOR, })), ], }, { id: 'scorecard', title: '8 · Retrospective scorecard → human decision', color: LAUNCH_COLORS[sc.launch_recommendation], badge: sc.launch_recommendation, items: [ { primary: `severity ${sc.severity} · launch ${sc.launch_recommendation}`, secondary: sc.human_review_required ? `HUMAN REVIEW REQUIRED: ${sc.human_review_reasons.join('; ')}` : 'no human review trigger fired — decision still rests with a human', color: SEVERITY_COLORS[sc.severity], }, ], }, ]; } export default function EvidenceChain({ scorecard }: { scorecard: RetrospectiveScorecard }) { const [expanded, setExpanded] = useState(['observed', 'derived', 'policy', 'scorecard']); const stages = buildStages(scorecard); const toggle = (id: string) => setExpanded((cur) => (cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id])); return ( EVIDENCE TIER LEGEND: {Object.values(TIER_META).map((m) => ( ))} {stages.map((stage, i) => ( {i < stages.length - 1 ? ( ) : null} toggle(stage.id)} sx={{ flex: 1, mb: 1, bgcolor: '#161c23', border: '1px solid #232a31', '&:before': { display: 'none' } }} > } sx={{ minHeight: 40 }}> {stage.title} {stage.items.length === 0 ? ( no items ) : ( stage.items.map((item, j) => ( {item.primary} {item.secondary} )) )} ))} ); }