diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index a02fd65ed..fd6318983 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -1108,3 +1108,10 @@ and must NOT appear for `will_fix_later` itself or for findings with no prior regression. This is the operator-facing half of "Pulse learns from operator dismissal patterns" — a recurring issue should not be silently buried just because the operator is moving fast on triage. +The collapsed finding row must also surface `regressionCount` as a small +pill next to the investigation-confidence badge whenever +`regressionCount > 0`, so a triaging operator scanning the list can spot +"this is not a one-off" without expanding each card. The pill stays +absent on fresh detections (count == 0) so the row stays clean for +ordinary findings, and the styling (amber tone) reads as a recurrence +signal rather than a generic muted note. diff --git a/frontend-modern/src/components/AI/FindingsPanel.tsx b/frontend-modern/src/components/AI/FindingsPanel.tsx index be64ce722..b3d12d46e 100644 --- a/frontend-modern/src/components/AI/FindingsPanel.tsx +++ b/frontend-modern/src/components/AI/FindingsPanel.tsx @@ -745,6 +745,19 @@ export const FindingsPanel: Component = (props) => { {finding.investigationRecord!.confidence!} confidence + {/* Regression pill — Pulse's "Learn" signal on the collapsed row. + A finding that has regressed before is not a one-off; it + needs to be triaged differently from a fresh detection. + Sits next to the confidence badge so trust + recurrence + can be scanned together without expanding the card. */} + 0}> + + regressed {finding.regressionCount}× + + {/* Title */} { expect(findingsPanelSource).toContain('Pulse will permanently suppress'); }); + it('surfaces regressionCount as a pill on the collapsed finding row', () => { + // regressionCount is the strongest "this is not a one-off" signal Pulse + // can give an operator scanning a list. The pill must appear in the + // collapsed row (alongside the confidence badge) so triage decisions + // can be made without expanding each card. The pill must only appear + // when regressionCount > 0 — fresh detections must stay clean. + expect(findingsPanelSource).toContain('(finding.regressionCount || 0) > 0'); + expect(findingsPanelSource).toContain('regressed {finding.regressionCount}×'); + expect(findingsPanelSource).toContain('text-amber-700 dark:text-amber-300'); + // The regression pill must sit next to the confidence badge in source order + // so the row reads as one trust-related cluster. + const confidenceIndex = findingsPanelSource.indexOf('confidence'); + const regressionIndex = findingsPanelSource.indexOf('regressed {finding.regressionCount}×'); + expect(confidenceIndex).toBeGreaterThan(0); + expect(regressionIndex).toBeGreaterThan(0); + expect(regressionIndex).toBeGreaterThan(confidenceIndex); + }); + it('warns the operator before dismissing a recurrent finding as not_an_issue or expected_behavior', () => { // not_an_issue and expected_behavior both stay quiet forever after dismiss. // If the finding has already regressed before, the operator may be