@@ -276,12 +276,12 @@ export function OverviewPanel({
276276
277277 const aiOverviewTriage = useCommitTriage ( failedOverviewRunIds ) ;
278278
279- // Auto-request triage when failures exist
279+ // Auto-request triage when failures exist (skip if already unavailable)
280280 useEffect ( ( ) => {
281- if ( failedOverviewRunIds . length > 0 && ! aiOverviewTriage . summary && ! aiOverviewTriage . loading ) {
281+ if ( failedOverviewRunIds . length > 0 && ! aiOverviewTriage . summary && ! aiOverviewTriage . loading && ! aiOverviewTriage . unavailable ) {
282282 aiOverviewTriage . requestTriage ( ) ;
283283 }
284- } , [ failedOverviewRunIds . length , aiOverviewTriage . summary , aiOverviewTriage . loading , aiOverviewTriage . requestTriage ] ) ;
284+ } , [ failedOverviewRunIds . length , aiOverviewTriage . summary , aiOverviewTriage . loading , aiOverviewTriage . unavailable , aiOverviewTriage . requestTriage ] ) ;
285285
286286 // Pre-compute inline failure tags for the failure feed
287287 // Tags suites with failed cases OR failed result
@@ -490,7 +490,7 @@ export function OverviewPanel({
490490 </ div >
491491
492492 { /* AI Overview Summary — only when failures exist and triage is active */ }
493- { failedOverviewRunIds . length > 0 && ( aiOverviewTriage . summary || aiOverviewTriage . loading || aiOverviewTriage . error ) && (
493+ { failedOverviewRunIds . length > 0 && ! aiOverviewTriage . unavailable && ( aiOverviewTriage . summary || aiOverviewTriage . loading || aiOverviewTriage . error ) && (
494494 < div className = "relative rounded-lg border border-orange-200/60 bg-orange-50/30 shadow-sm dark:border-orange-900/40 dark:bg-orange-950/10" >
495495 < div className = "absolute top-0 left-0 right-0 h-[3px] rounded-t-lg ai-shimmer-bar" />
496496 < div className = "px-4 py-3" >
@@ -626,108 +626,102 @@ export function OverviewPanel({
626626 </ div >
627627 ) }
628628
629- { /* Section C: Failure Feed (Needs Attention) */ }
630- < Collapsible open = { hasFailures && failureFeedOpen } onOpenChange = { setFailureFeedOpen } >
631- < div className = "rounded-xl border bg-card" >
632- < CollapsibleTrigger className = "w-full flex items-center justify-between px-4 py-3 hover: bg-muted/50 transition-colors rounded-xl " >
633- < div className = "flex items-center gap-2 " >
634- { failureFeedOpen && hasFailures ? (
635- < ChevronDown className = "h-4 w-4 text-muted-foreground" />
636- ) : (
637- < ChevronRight className = "h-4 w-4 text-muted-foreground" />
638- ) }
639- < span className = "text-sm font-semibold" > Needs Attention </ span >
640- { hasFailures && (
629+ { /* Section C: Failure Feed (Needs Attention) — hidden when nothing needs attention */ }
630+ { hasFailures && (
631+ < Collapsible open = { failureFeedOpen } onOpenChange = { setFailureFeedOpen } >
632+ < div className = "rounded-xl border bg-card " >
633+ < CollapsibleTrigger className = "w-full flex items-center justify-between px-4 py-3 hover:bg-muted/50 transition-colors rounded-xl " >
634+ < div className = "flex items-center gap-2" >
635+ { failureFeedOpen ? (
636+ < ChevronDown className = "h-4 w-4 text-muted-foreground" />
637+ ) : (
638+ < ChevronRight className = "h-4 w-4 text-muted-foreground" />
639+ ) }
640+ < span className = "text-sm font-semibold" > Needs Attention </ span >
641641 < span className = "text-xs text-muted-foreground" >
642642 ({ failureEntries . length } )
643643 </ span >
644- ) }
645- </ div >
646- { ! hasFailures && (
647- < div className = "flex items-center gap-1.5 text-xs text-emerald-500" >
648- < CheckCircle2 className = "h-3.5 w-3.5" />
649- All clear
650644 </ div >
651- ) }
652- </ CollapsibleTrigger >
653-
654- < CollapsibleContent >
655- < div className = "border-t divide-y" >
656- { failureEntries . map ( ( entry ) => {
657- const isFailed = entry . latestRun ?. result === "failed" ;
658- const isNeverRun = ! entry . latestRun ;
659-
660- return (
661- < button
662- key = { entry . suite . _id }
663- onClick = { ( ) => onSelectSuite ?.( entry . suite . _id ) }
664- className = "w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors"
665- >
666- < div className = "flex items-start gap-2.5" >
667- { isFailed ? (
668- < XCircle className = "h-4 w-4 text-destructive shrink-0 mt-0.5" />
669- ) : (
670- < MinusCircle className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
671- ) }
672- < div className = "min-w-0 flex-1" >
673- < div className = "flex items-center gap-1.5" >
674- < span className = "text-sm font-medium truncate" >
675- { entry . suite . name }
676- </ span >
677- { isFailed &&
678- failureTagMap . get ( entry . suite . _id ) ?. map ( ( tag ) => (
679- < InlineFailureTag key = { tag } tag = { tag } />
680- ) ) }
681- </ div >
682- { isFailed && entry . latestRun ?. summary && (
683- < div className = "text-xs text-muted-foreground mt-0.5" >
684- { entry . latestRun . summary . passed } /
685- { entry . latestRun . summary . total } tests passed
686- { entry . latestRun . summary . passRate !== undefined &&
687- ` (${ Math . round ( entry . latestRun . summary . passRate ) } %)` }
688- </ div >
689- ) }
690- { isFailed && entry . latestRun ?. ciMetadata && (
691- < div className = "text-[11px] text-muted-foreground mt-0.5" >
692- { entry . latestRun . ciMetadata . branch && (
693- < span > { entry . latestRun . ciMetadata . branch } </ span >
694- ) }
695- { entry . latestRun . ciMetadata . commitSha && (
696- < span >
697- { " " }
698- @ { entry . latestRun . ciMetadata . commitSha . slice ( 0 , 7 ) }
699- </ span >
700- ) }
701- { " · " }
702- { formatRelativeTime (
703- entry . latestRun . completedAt ??
704- entry . latestRun . createdAt ,
705- ) }
706- </ div >
707- ) }
708- { isFailed && ! entry . latestRun ?. ciMetadata && (
709- < div className = "text-[11px] text-muted-foreground mt-0.5" >
710- { formatRelativeTime (
711- entry . latestRun ?. completedAt ??
712- entry . latestRun ?. createdAt ,
713- ) }
714- </ div >
645+ </ CollapsibleTrigger >
646+
647+ < CollapsibleContent >
648+ < div className = "border-t divide-y" >
649+ { failureEntries . map ( ( entry ) => {
650+ const isFailed = entry . latestRun ?. result === "failed" ;
651+ const isNeverRun = ! entry . latestRun ;
652+
653+ return (
654+ < button
655+ key = { entry . suite . _id }
656+ onClick = { ( ) => onSelectSuite ?.( entry . suite . _id ) }
657+ className = "w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors"
658+ >
659+ < div className = "flex items-start gap-2.5" >
660+ { isFailed ? (
661+ < XCircle className = "h-4 w-4 text-destructive shrink-0 mt-0.5" />
662+ ) : (
663+ < MinusCircle className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
715664 ) }
716- { isNeverRun && (
717- < div className = "text-xs text-muted-foreground mt-0.5" >
718- Never run
665+ < div className = "min-w-0 flex-1" >
666+ < div className = "flex items-center gap-1.5" >
667+ < span className = "text-sm font-medium truncate" >
668+ { entry . suite . name }
669+ </ span >
670+ { isFailed &&
671+ failureTagMap . get ( entry . suite . _id ) ?. map ( ( tag ) => (
672+ < InlineFailureTag key = { tag } tag = { tag } />
673+ ) ) }
719674 </ div >
720- ) }
675+ { isFailed && entry . latestRun ?. summary && (
676+ < div className = "text-xs text-muted-foreground mt-0.5" >
677+ { entry . latestRun . summary . passed } /
678+ { entry . latestRun . summary . total } tests passed
679+ { entry . latestRun . summary . passRate !== undefined &&
680+ ` (${ Math . round ( entry . latestRun . summary . passRate ) } %)` }
681+ </ div >
682+ ) }
683+ { isFailed && entry . latestRun ?. ciMetadata && (
684+ < div className = "text-[11px] text-muted-foreground mt-0.5" >
685+ { entry . latestRun . ciMetadata . branch && (
686+ < span > { entry . latestRun . ciMetadata . branch } </ span >
687+ ) }
688+ { entry . latestRun . ciMetadata . commitSha && (
689+ < span >
690+ { " " }
691+ @ { entry . latestRun . ciMetadata . commitSha . slice ( 0 , 7 ) }
692+ </ span >
693+ ) }
694+ { " · " }
695+ { formatRelativeTime (
696+ entry . latestRun . completedAt ??
697+ entry . latestRun . createdAt ,
698+ ) }
699+ </ div >
700+ ) }
701+ { isFailed && ! entry . latestRun ?. ciMetadata && (
702+ < div className = "text-[11px] text-muted-foreground mt-0.5" >
703+ { formatRelativeTime (
704+ entry . latestRun ?. completedAt ??
705+ entry . latestRun ?. createdAt ,
706+ ) }
707+ </ div >
708+ ) }
709+ { isNeverRun && (
710+ < div className = "text-xs text-muted-foreground mt-0.5" >
711+ Never run
712+ </ div >
713+ ) }
714+ </ div >
715+ < ArrowRight className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
721716 </ div >
722- < ArrowRight className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
723- </ div >
724- </ button >
725- ) ;
726- } ) }
727- </ div >
728- </ CollapsibleContent >
729- </ div >
730- </ Collapsible >
717+ </ button >
718+ ) ;
719+ } ) }
720+ </ div >
721+ </ CollapsibleContent >
722+ </ div >
723+ </ Collapsible >
724+ ) }
731725
732726 { /* Section D: Suite Table */ }
733727 < div className = "rounded-xl border bg-card" >
0 commit comments