Keep Patrol findings visible during refresh

This commit is contained in:
rcourtman 2026-05-12 22:13:19 +01:00
parent 5465852be0
commit bb04bf9042
4 changed files with 22 additions and 3 deletions

View file

@ -83,7 +83,9 @@ Patrol-specific presentation helpers.
The same source boundary owns loading and error state: Patrol findings must
render from `patrolFindings`, `patrolFindingsLoading`, and
`patrolFindingsError` instead of letting a pending unified findings request
mask direct Patrol evidence that has already loaded.
mask direct Patrol evidence that has already loaded. Background Patrol
refreshes must preserve already-loaded findings instead of replacing the
workspace with a blocking loading state.
2. Keep Patrol-specific copy and badge logic inside the governed Patrol presentation helpers instead of page-local branches
Patrol assessment copy must not present an all-clear health prediction while
active Patrol findings or Patrol runtime issues are still present. The

View file

@ -165,11 +165,15 @@ export const FindingsPanel: Component<FindingsPanelProps> = (props) => {
const sourceFindings = createMemo(() =>
isPatrolFindingsSource() ? aiIntelligenceStore.patrolFindings : aiIntelligenceStore.findings,
);
const sourceHasFindings = createMemo(() => sourceFindings().length > 0);
const sourceFindingsLoading = createMemo(() =>
isPatrolFindingsSource()
? aiIntelligenceStore.patrolFindingsLoading
: aiIntelligenceStore.findingsLoading,
);
const shouldShowLoadingState = createMemo(
() => sourceFindingsLoading() && !sourceHasFindings(),
);
const sourceFindingsError = createMemo(() =>
isPatrolFindingsSource()
? aiIntelligenceStore.patrolFindingsError
@ -1806,7 +1810,7 @@ export const FindingsPanel: Component<FindingsPanelProps> = (props) => {
</Show>
{/* Loading/Error states */}
<Show when={sourceFindingsLoading()}>
<Show when={shouldShowLoadingState()}>
<div class="p-4 text-sm text-muted flex items-center gap-2">
<span class="h-4 w-4 border-2 border-current border-t-transparent rounded-full animate-spin" />
Loading findings...
@ -1819,7 +1823,7 @@ export const FindingsPanel: Component<FindingsPanelProps> = (props) => {
</div>
</Show>
<Show when={!sourceFindingsLoading()}>
<Show when={!shouldShowLoadingState()}>
<Card padding="none" class="overflow-hidden">
{/* Content */}
<div class="divide-y divide-border-subtle">

View file

@ -237,4 +237,15 @@ describe('FindingsPanel resource links', () => {
expect(screen.queryByText('Loading findings...')).not.toBeInTheDocument();
expect(screen.getByText('Provider connection issue')).toBeInTheDocument();
});
it('keeps loaded Patrol findings visible during a Patrol refresh', async () => {
mockState.patrolFindingsLoading = true;
render(() => <FindingsPanel findingsSource="patrol" />);
await waitFor(() => expect(mockState.loadPatrolFindings).toHaveBeenCalled());
expect(screen.queryByText('Loading findings...')).not.toBeInTheDocument();
expect(screen.getByText('Provider connection issue')).toBeInTheDocument();
});
});

View file

@ -675,6 +675,8 @@ describe('aiFindingPresentation', () => {
expect(findingsPanelSource).toContain("findingsSource?: 'unified' | 'patrol'");
expect(findingsPanelSource).toContain('aiIntelligenceStore.loadPatrolFindings()');
expect(findingsPanelSource).toContain('aiIntelligenceStore.patrolFindings');
expect(findingsPanelSource).toContain('const sourceHasFindings = createMemo(');
expect(findingsPanelSource).toContain('const shouldShowLoadingState = createMemo(');
expect(findingsPanelSource).toContain('aiIntelligenceStore.patrolFindingsLoading');
expect(findingsPanelSource).toContain('aiIntelligenceStore.patrolFindingsError');
expect(findingsPanelSource).toContain('aiIntelligenceStore.patrolFindingsSignal()');