mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-12 06:19:11 +00:00
Canonicalize dashboard attention ordering
Move Patrol attention ordering into aiIntelligenceStore so dashboard hooks consume a canonical queue instead of adding their own ordering logic downstream.
This commit is contained in:
@@ -31,7 +31,6 @@ vi.mock('@/utils/logger', () => ({
|
||||
|
||||
import { AIAPI } from '@/api/ai';
|
||||
import { useDashboardActions } from '@/hooks/useDashboardActions';
|
||||
import { sortFindingsForAttentionQueue } from '@/utils/aiFindingPresentation';
|
||||
|
||||
describe('useDashboardActions', () => {
|
||||
beforeEach(() => {
|
||||
@@ -109,29 +108,60 @@ describe('useDashboardActions', () => {
|
||||
dispose();
|
||||
});
|
||||
|
||||
it('prioritizes Patrol runtime findings in the dashboard attention queue', () => {
|
||||
expect(
|
||||
sortFindingsForAttentionQueue([
|
||||
it('surfaces the store-owned attention ordering unchanged', async () => {
|
||||
vi.mocked(AIAPI.getUnifiedFindings).mockResolvedValue({
|
||||
findings: [
|
||||
{
|
||||
id: 'infra-warning',
|
||||
status: 'active',
|
||||
source: 'ai-patrol',
|
||||
severity: 'warning',
|
||||
resourceId: 'vm-101',
|
||||
resourceName: 'db-01',
|
||||
category: 'infrastructure',
|
||||
resource_id: 'instance:node:101',
|
||||
resource_name: 'db-01',
|
||||
resource_type: 'host',
|
||||
title: 'Disk nearly full',
|
||||
detectedAt: '2026-03-01T00:00:00Z',
|
||||
} as never,
|
||||
description: 'Storage usage is high.',
|
||||
detected_at: '2026-03-01T00:00:00Z',
|
||||
status: 'active',
|
||||
investigation_outcome: 'fix_verification_unknown',
|
||||
},
|
||||
{
|
||||
id: 'runtime-warning',
|
||||
status: 'active',
|
||||
source: 'ai-patrol',
|
||||
severity: 'warning',
|
||||
resourceId: 'ai-service',
|
||||
resourceName: 'Pulse Patrol Service',
|
||||
category: 'service',
|
||||
resource_id: 'ai-service',
|
||||
resource_name: 'Pulse Patrol Service',
|
||||
resource_type: 'service',
|
||||
title: 'Pulse Patrol: Insufficient API credits',
|
||||
lastSeenAt: '2026-03-01T00:05:00Z',
|
||||
detectedAt: '2026-03-01T00:01:00Z',
|
||||
} as never,
|
||||
]).map((finding) => finding.id),
|
||||
).toEqual(['runtime-warning', 'infra-warning']);
|
||||
description: 'Provider credits are exhausted.',
|
||||
detected_at: '2026-03-01T00:01:00Z',
|
||||
last_seen_at: '2026-03-01T00:05:00Z',
|
||||
status: 'active',
|
||||
investigation_outcome: 'fix_failed',
|
||||
},
|
||||
],
|
||||
count: 2,
|
||||
} as never);
|
||||
vi.mocked(AIAPI.getPendingApprovals).mockResolvedValue([] as never);
|
||||
|
||||
let dispose!: () => void;
|
||||
let actions!: ReturnType<typeof useDashboardActions>;
|
||||
|
||||
createRoot((d) => {
|
||||
dispose = d;
|
||||
const [alertsList] = createSignal<Alert[]>([]);
|
||||
actions = useDashboardActions(alertsList);
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
|
||||
expect(actions.findingsNeedingAttention().map((finding) => finding.id)).toEqual([
|
||||
'runtime-warning',
|
||||
'infra-warning',
|
||||
]);
|
||||
|
||||
dispose();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import type { UnifiedFinding } from '@/stores/aiIntelligence';
|
||||
import { aiIntelligenceStore } from '@/stores/aiIntelligence';
|
||||
import type { ApprovalRequest } from '@/api/ai';
|
||||
import { hasFeature } from '@/stores/license';
|
||||
import { sortFindingsForAttentionQueue } from '@/utils/aiFindingPresentation';
|
||||
|
||||
export interface DashboardActions {
|
||||
pendingApprovals: Accessor<ApprovalRequest[]>;
|
||||
@@ -55,7 +54,7 @@ export function useDashboardActions(alertsList: Accessor<Alert[]>): DashboardAct
|
||||
|
||||
const findingsNeedingAttention = createMemo(() => {
|
||||
if (!hasPatrol()) return [];
|
||||
return sortFindingsForAttentionQueue(aiIntelligenceStore.findingsNeedingAttention);
|
||||
return aiIntelligenceStore.findingsNeedingAttention;
|
||||
});
|
||||
|
||||
const totalActionCount = createMemo(
|
||||
|
||||
@@ -427,4 +427,51 @@ describe('aiIntelligenceStore', () => {
|
||||
await aiIntelligenceStore.loadPendingApprovals();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('sorts Patrol runtime findings to the top of the shared needs-attention queue', async () => {
|
||||
vi.mocked(AIAPI.getUnifiedFindings).mockResolvedValueOnce({
|
||||
findings: [
|
||||
{
|
||||
id: 'infra-warning',
|
||||
source: 'ai-patrol',
|
||||
severity: 'warning',
|
||||
category: 'infrastructure',
|
||||
resource_id: 'instance:node:101',
|
||||
resource_name: 'db-01',
|
||||
resource_type: 'host',
|
||||
title: 'Disk nearly full',
|
||||
description: 'Storage usage is high.',
|
||||
detected_at: '2026-03-01T00:00:00Z',
|
||||
status: 'active',
|
||||
investigation_outcome: 'fix_verification_unknown',
|
||||
},
|
||||
{
|
||||
id: 'runtime-warning',
|
||||
source: 'ai-patrol',
|
||||
severity: 'warning',
|
||||
category: 'service',
|
||||
resource_id: 'ai-service',
|
||||
resource_name: 'Pulse Patrol Service',
|
||||
resource_type: 'service',
|
||||
title: 'Pulse Patrol: Insufficient API credits',
|
||||
description: 'Provider credits are exhausted.',
|
||||
detected_at: '2026-03-01T00:01:00Z',
|
||||
last_seen_at: '2026-03-01T00:05:00Z',
|
||||
status: 'active',
|
||||
investigation_outcome: 'fix_failed',
|
||||
},
|
||||
],
|
||||
count: 2,
|
||||
});
|
||||
|
||||
vi.mocked(AIAPI.getPendingApprovals).mockResolvedValueOnce([]);
|
||||
|
||||
await aiIntelligenceStore.loadFindings();
|
||||
await aiIntelligenceStore.loadPendingApprovals();
|
||||
|
||||
expect(aiIntelligenceStore.findingsNeedingAttention.map((finding) => finding.id)).toEqual([
|
||||
'runtime-warning',
|
||||
'infra-warning',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
doesFindingNeedAttention,
|
||||
hasPendingInvestigationFixApproval,
|
||||
isPatrolInvestigationFixApproval,
|
||||
sortFindingsForAttentionQueue,
|
||||
} from '@/utils/aiFindingPresentation';
|
||||
import { getApprovalExpiryTime, isLivePendingApproval } from '@/utils/approvalState';
|
||||
import { logger } from '@/utils/logger';
|
||||
@@ -453,7 +454,9 @@ export const aiIntelligenceStore = {
|
||||
|
||||
get findingsNeedingAttention() {
|
||||
const approvals = getLivePendingApprovals();
|
||||
return unifiedFindings().filter((finding) => doesFindingNeedAttention(finding, approvals));
|
||||
return sortFindingsForAttentionQueue(
|
||||
unifiedFindings().filter((finding) => doesFindingNeedAttention(finding, approvals)),
|
||||
);
|
||||
},
|
||||
|
||||
get needsAttentionCount() {
|
||||
|
||||
Reference in New Issue
Block a user