@@ -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
@@ -491,7 +491,7 @@ export function OverviewPanel({
491491 </ div >
492492
493493 { /* AI Overview Summary — only when failures exist and triage is active */ }
494- { failedOverviewRunIds . length > 0 && ( aiOverviewTriage . summary || aiOverviewTriage . loading || aiOverviewTriage . error ) && (
494+ { failedOverviewRunIds . length > 0 && ! aiOverviewTriage . unavailable && ( aiOverviewTriage . summary || aiOverviewTriage . loading || aiOverviewTriage . error ) && (
495495 < 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" >
496496 < div className = "absolute top-0 left-0 right-0 h-[3px] rounded-t-lg ai-shimmer-bar" />
497497 < div className = "px-4 py-3" >
@@ -627,115 +627,102 @@ export function OverviewPanel({
627627 </ div >
628628 ) }
629629
630- { /* Section C: Failure Feed (Needs Attention) */ }
631- < Collapsible
632- open = { hasFailures && failureFeedOpen }
633- onOpenChange = { setFailureFeedOpen }
634- >
635- < div className = "rounded-xl border bg-card" >
636- < CollapsibleTrigger className = "w-full flex items-center justify-between px-4 py-3 hover:bg-muted/50 transition-colors rounded-xl" >
637- < div className = "flex items-center gap-2" >
638- { failureFeedOpen && hasFailures ? (
639- < ChevronDown className = "h-4 w-4 text-muted-foreground" />
640- ) : (
641- < ChevronRight className = "h-4 w-4 text-muted-foreground" />
642- ) }
643- < span className = "text-sm font-semibold" > Needs Attention</ span >
644- { hasFailures && (
630+ { /* Section C: Failure Feed (Needs Attention) — hidden when nothing needs attention */ }
631+ { hasFailures && (
632+ < Collapsible open = { failureFeedOpen } onOpenChange = { setFailureFeedOpen } >
633+ < div className = "rounded-xl border bg-card" >
634+ < CollapsibleTrigger className = "w-full flex items-center justify-between px-4 py-3 hover:bg-muted/50 transition-colors rounded-xl" >
635+ < div className = "flex items-center gap-2" >
636+ { failureFeedOpen ? (
637+ < ChevronDown className = "h-4 w-4 text-muted-foreground" />
638+ ) : (
639+ < ChevronRight className = "h-4 w-4 text-muted-foreground" />
640+ ) }
641+ < span className = "text-sm font-semibold" > Needs Attention</ span >
645642 < span className = "text-xs text-muted-foreground" >
646643 ({ failureEntries . length } )
647644 </ span >
648- ) }
649- </ div >
650- { ! hasFailures && (
651- < div className = "flex items-center gap-1.5 text-xs text-emerald-500" >
652- < CheckCircle2 className = "h-3.5 w-3.5" />
653- All clear
654645 </ div >
655- ) }
656- </ CollapsibleTrigger >
657-
658- < CollapsibleContent >
659- < div className = "border-t divide-y" >
660- { failureEntries . map ( ( entry ) => {
661- const isFailed = entry . latestRun ?. result === "failed" ;
662- const isNeverRun = ! entry . latestRun ;
663-
664- return (
665- < button
666- key = { entry . suite . _id }
667- onClick = { ( ) => onSelectSuite ?.( entry . suite . _id ) }
668- className = "w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors"
669- >
670- < div className = "flex items-start gap-2.5" >
671- { isFailed ? (
672- < XCircle className = "h-4 w-4 text-destructive shrink-0 mt-0.5" />
673- ) : (
674- < MinusCircle className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
675- ) }
676- < div className = "min-w-0 flex-1" >
677- < div className = "flex items-center gap-1.5" >
678- < span className = "text-sm font-medium truncate" >
679- { entry . suite . name }
680- </ span >
681- { isFailed &&
682- failureTagMap . get ( entry . suite . _id ) ?. map ( ( tag ) => (
683- < InlineFailureTag key = { tag } tag = { tag } />
684- ) ) }
685- </ div >
686- { isFailed && entry . latestRun ?. summary && (
687- < div className = "text-xs text-muted-foreground mt-0.5" >
688- { entry . latestRun . summary . passed } /
689- { entry . latestRun . summary . total } tests passed
690- { entry . latestRun . summary . passRate !== undefined &&
691- ` (${ Math . round ( entry . latestRun . summary . passRate ) } %)` }
692- </ div >
646+ </ CollapsibleTrigger >
647+
648+ < CollapsibleContent >
649+ < div className = "border-t divide-y" >
650+ { failureEntries . map ( ( entry ) => {
651+ const isFailed = entry . latestRun ?. result === "failed" ;
652+ const isNeverRun = ! entry . latestRun ;
653+
654+ return (
655+ < button
656+ key = { entry . suite . _id }
657+ onClick = { ( ) => onSelectSuite ?.( entry . suite . _id ) }
658+ className = "w-full px-4 py-3 text-left hover:bg-muted/50 transition-colors"
659+ >
660+ < div className = "flex items-start gap-2.5" >
661+ { isFailed ? (
662+ < XCircle className = "h-4 w-4 text-destructive shrink-0 mt-0.5" />
663+ ) : (
664+ < MinusCircle className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
693665 ) }
694- { isFailed && entry . latestRun ?. ciMetadata && (
695- < div className = "text-[11px] text-muted-foreground mt-0.5" >
696- { entry . latestRun . ciMetadata . branch && (
697- < span > { entry . latestRun . ciMetadata . branch } </ span >
698- ) }
699- { entry . latestRun . ciMetadata . commitSha && (
700- < span >
701- { " " }
702- @{ " " }
703- { entry . latestRun . ciMetadata . commitSha . slice (
704- 0 ,
705- 7 ,
706- ) }
707- </ span >
708- ) }
709- { " · " }
710- { formatRelativeTime (
711- entry . latestRun . completedAt ??
712- entry . latestRun . createdAt ,
713- ) }
666+ < div className = "min-w-0 flex-1" >
667+ < div className = "flex items-center gap-1.5" >
668+ < span className = "text-sm font-medium truncate" >
669+ { entry . suite . name }
670+ </ span >
671+ { isFailed &&
672+ failureTagMap . get ( entry . suite . _id ) ?. map ( ( tag ) => (
673+ < InlineFailureTag key = { tag } tag = { tag } />
674+ ) ) }
714675 </ div >
715- ) }
716- { isFailed && ! entry . latestRun ?. ciMetadata && (
717- < div className = "text-[11px] text-muted-foreground mt-0.5" >
718- { formatRelativeTime (
719- entry . latestRun ?. completedAt ??
720- entry . latestRun ?. createdAt ,
721- ) }
722- </ div >
723- ) }
724- { isNeverRun && (
725- < div className = "text-xs text-muted-foreground mt-0.5" >
726- Never run
727- </ div >
728- ) }
676+ { isFailed && entry . latestRun ?. summary && (
677+ < div className = "text-xs text-muted-foreground mt-0.5" >
678+ { entry . latestRun . summary . passed } /
679+ { entry . latestRun . summary . total } tests passed
680+ { entry . latestRun . summary . passRate !== undefined &&
681+ ` (${ Math . round ( entry . latestRun . summary . passRate ) } %)` }
682+ </ div >
683+ ) }
684+ { isFailed && entry . latestRun ?. ciMetadata && (
685+ < div className = "text-[11px] text-muted-foreground mt-0.5" >
686+ { entry . latestRun . ciMetadata . branch && (
687+ < span > { entry . latestRun . ciMetadata . branch } </ span >
688+ ) }
689+ { entry . latestRun . ciMetadata . commitSha && (
690+ < span >
691+ { " " }
692+ @ { entry . latestRun . ciMetadata . commitSha . slice ( 0 , 7 ) }
693+ </ span >
694+ ) }
695+ { " · " }
696+ { formatRelativeTime (
697+ entry . latestRun . completedAt ??
698+ entry . latestRun . createdAt ,
699+ ) }
700+ </ div >
701+ ) }
702+ { isFailed && ! entry . latestRun ?. ciMetadata && (
703+ < div className = "text-[11px] text-muted-foreground mt-0.5" >
704+ { formatRelativeTime (
705+ entry . latestRun ?. completedAt ??
706+ entry . latestRun ?. createdAt ,
707+ ) }
708+ </ div >
709+ ) }
710+ { isNeverRun && (
711+ < div className = "text-xs text-muted-foreground mt-0.5" >
712+ Never run
713+ </ div >
714+ ) }
715+ </ div >
716+ < ArrowRight className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
729717 </ div >
730- < ArrowRight className = "h-4 w-4 text-muted-foreground shrink-0 mt-0.5" />
731- </ div >
732- </ button >
733- ) ;
734- } ) }
735- </ div >
736- </ CollapsibleContent >
737- </ div >
738- </ Collapsible >
718+ </ button >
719+ ) ;
720+ } ) }
721+ </ div >
722+ </ CollapsibleContent >
723+ </ div >
724+ </ Collapsible >
725+ ) }
739726
740727 { /* Section D: Suite Table */ }
741728 < div className = "rounded-xl border bg-card" >
0 commit comments