From bcabda085f2243d3695caf0743ce532698546ae3 Mon Sep 17 00:00:00 2001
From: rcourtman
Date: Thu, 13 Aug 2026 21:53:56 +0100
Subject: [PATCH] Point patrol attention lifecycle at the durable threshold and
finding controls
Two users independently concluded the permanent 'Remember as expected'
dismissals were removed (discussions #1623, #1699) because the Needs
attention workbench only offers acknowledge and temporary suppression,
while the finding-level Manage menu sits behind a collapsed disclosure
below it.
The attention detail's Lifecycle section now states that its controls
cover only the current occurrence and links the two durable paths: the
alert thresholds page, and the Patrol findings panel via a control that
expands the disclosure and scrolls to it. The disclosure summary also
carries an open-work count badge so it no longer reads as an empty
archive.
---
frontend-modern/browser-verification.json | 47 ++++++++---------
.../patrol/PatrolAttentionWorkbench.tsx | 33 +++++++++++-
.../patrol/PatrolIntelligenceSurface.tsx | 40 ++++++++++++--
.../PatrolAttentionWorkbench.test.tsx | 52 ++++++++++++++++++-
frontend-modern/src/routing/resourceLinks.ts | 1 +
5 files changed, 141 insertions(+), 32 deletions(-)
diff --git a/frontend-modern/browser-verification.json b/frontend-modern/browser-verification.json
index 09014865f..8278f7e48 100644
--- a/frontend-modern/browser-verification.json
+++ b/frontend-modern/browser-verification.json
@@ -1,25 +1,27 @@
{
"version": 1,
- "base_sha": "3adeb77d60620cdcc4c71431685725384bdc562a",
- "verified_at": "2026-08-12T08:19:01Z",
+ "base_sha": "929c03b4906590fa12b7d7ce1d63e81ab384854b",
+ "verified_at": "2026-08-13T20:53:32Z",
"result": "passed",
"changed_paths": [
- "frontend-modern/src/api/settings.ts",
- "frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts",
- "frontend-modern/src/components/Settings/useSystemSettingsState.ts"
+ "frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx",
+ "frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx",
+ "frontend-modern/src/features/patrol/__tests__/PatrolAttentionWorkbench.test.tsx",
+ "frontend-modern/src/routing/resourceLinks.ts"
],
"content_sha256": {
- "frontend-modern/src/api/settings.ts": "cb95289d5df7cd9332cc78573a5c6052f8968cc0fbb2d66c15c8ddc49f44ab62",
- "frontend-modern/src/components/Settings/useInfrastructureSettingsState.ts": "528b32af6b1c777ed87f5dd2d5b15b531f830c520b0f624acfc5a2d75ad3e314",
- "frontend-modern/src/components/Settings/useSystemSettingsState.ts": "acb66acc4a0254a6816564713f65a1ed5cf723eb526e4960076b4c84335e2c09"
+ "frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx": "34d7d90cfb0a0a6405346115393688a8db89be602e4112512a61fce7f17237f2",
+ "frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx": "396357b7930259e4be0947e97264d5311956c118295c10d0daaa2f59be89e44c",
+ "frontend-modern/src/features/patrol/__tests__/PatrolAttentionWorkbench.test.tsx": "d809cd5ee80973042e8c703e746a392ee0235d3868a0177ff8e874c87d7b811e",
+ "frontend-modern/src/routing/resourceLinks.ts": "9b44383c2061a6ec4f2e49a124be766a2d7294862dd32f3343bde1199e690030"
},
"routes": [
- "/settings/system-general"
+ "/patrol"
],
"viewports": [
{
- "width": 1280,
- "height": 720
+ "width": 1311,
+ "height": 1234
},
{
"width": 375,
@@ -27,21 +29,16 @@
}
],
"states": [
- "Admin session Monitoring Cadence card at the fresh-install Realtime (10s) default",
- "Admin card after selecting the Low (60s) preset and saving (GET /api/system/settings confirmed pvePollingInterval 60)",
- "Non-admin session (persisted session cookie whose user no longer matches the configured admin) with GET /api/system/settings returning 403 while GET /api/runtime/display returned pvePollingInterval 60: card showed Current cadence: 60 seconds with Low (60s) pressed instead of the old Realtime fallback",
- "Admin card after switching to Custom 45s and saving (GET /api/system/settings confirmed 45)",
- "Non-admin card after reload showing Current cadence: 45 seconds with Custom pressed and 45 in the custom input",
- "Same non-admin Custom 45s state re-exercised at the 375px mobile viewport",
- "Admin session reload showing Custom 45s restored through the refactored preset-resolution path"
+ "Needs attention list with 10 active mock items",
+ "Attention detail open with Lifecycle controls and the new durable-options guidance",
+ "Findings disclosure collapsed with '5 open' badge on the summary",
+ "Findings disclosure expanded showing the Open work findings list",
+ "Expanded finding Manage menu listing Remember as expected and dismiss actions"
],
"interactions": [
- "Built the sidecar backend and ran it as a scratch instance; logged in as admin on the pulse-1601-admin Vite frontend",
- "Selected Low (60s) on the Monitoring Cadence card and saved; verified the effective interval via the settings and runtime-display APIs",
- "Restarted the backend with a different configured admin username so the persisted browser session became a genuine non-admin session (settings 403, security-status capabilities all false)",
- "Reloaded /settings/system-general in that non-admin session and confirmed the cadence card rendered the server's real 60s interval from the runtime-display projection",
- "Logged in as the new admin in a separate cookie jar, set a non-preset Custom 45s cadence, and saved",
- "Reloaded the non-admin session and confirmed Custom 45s displayed, then repeated the check at the 375px mobile viewport",
- "Reloaded the admin session to confirm the shared preset-resolution refactor still renders the saved Custom 45s state"
+ "Clicked an attention item to open the detail panel (desktop and 375px mobile)",
+ "Verified 'adjust alert thresholds' renders as a link with href /alerts/thresholds",
+ "Clicked 'review Patrol findings' on both viewports: disclosure opened and scrolled into view",
+ "Expanded a finding row and opened its Manage menu to confirm Remember as expected is reachable"
]
}
diff --git a/frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx b/frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx
index 427108c02..da6787450 100644
--- a/frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx
+++ b/frontend-modern/src/features/patrol/PatrolAttentionWorkbench.tsx
@@ -1,4 +1,4 @@
-import { useLocation } from '@solidjs/router';
+import { A, useLocation } from '@solidjs/router';
import {
createEffect,
createMemo,
@@ -46,6 +46,7 @@ import {
import { aiChatStore } from '@/stores/aiChat';
import { patrolAttentionStore } from '@/stores/patrolAttention';
import {
+ ALERT_THRESHOLDS_PATH,
buildPatrolAttentionPath,
buildStandalonePath,
buildWorkloadsRouteSearch,
@@ -67,7 +68,7 @@ const FILTERS: Array<{ id: AttentionFilter; label: string }> = [
{ id: 'resolved', label: 'Recent resolved' },
];
-export function PatrolAttentionWorkbench() {
+export function PatrolAttentionWorkbench(props: { onOpenFindings?: () => void } = {}) {
const location = useLocation();
const [selectedItemId, setSelectedItemId] = createSignal('');
const [actionDetail, setActionDetail] = createSignal(null);
@@ -306,6 +307,7 @@ export function PatrolAttentionWorkbench() {
changeLifecycle(() => suppressPatrolAttention(itemId, reason, expiresAt))
}
onUnsuppress={(itemId) => changeLifecycle(() => unsuppressPatrolAttention(itemId))}
+ onOpenFindings={props.onOpenFindings}
/>
@@ -466,6 +468,7 @@ function AttentionDetail(props: {
onUnacknowledge: (itemId: string) => Promise;
onSuppress: (itemId: string, reason: string, expiresAt: string) => Promise;
onUnsuppress: (itemId: string) => Promise;
+ onOpenFindings?: () => void;
}) {
const detail = () => props.detail;
const item = () => detail()?.item;
@@ -597,6 +600,7 @@ function AttentionDetail(props: {
onUnacknowledge={props.onUnacknowledge}
onSuppress={props.onSuppress}
onUnsuppress={props.onUnsuppress}
+ onOpenFindings={props.onOpenFindings}
/>
@@ -810,6 +814,7 @@ function AttentionLifecycleControls(props: {
onUnacknowledge: (itemId: string) => Promise;
onSuppress: (itemId: string, reason: string, expiresAt: string) => Promise;
onUnsuppress: (itemId: string) => Promise;
+ onOpenFindings?: () => void;
}) {
const [showSuppression, setShowSuppression] = createSignal(false);
const [reason, setReason] = createSignal('');
@@ -975,6 +980,30 @@ function AttentionLifecycleControls(props: {
{props.error}
+
+
These controls cover only this occurrence, and suppression always expires.
+
+ For a permanent change,{' '}
+
+ adjust alert thresholds
+ {' '}
+ to change or turn off this alert for the affected resource
+
+ , or{' '}
+ {' '}
+ to mark a finding as expected so Patrol stops raising it.
+
+
+
);
diff --git a/frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx b/frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx
index dc68eed4b..7241a5b78 100644
--- a/frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx
+++ b/frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx
@@ -1,3 +1,6 @@
+import { createSignal, Show } from 'solid-js';
+import { MetadataBadge } from '@/components/shared/MetadataBadge';
+import { aiIntelligenceStore } from '@/stores/aiIntelligence';
import { usePatrolIntelligenceState } from './usePatrolIntelligenceState';
import { PatrolIntelligenceHeader } from './PatrolIntelligenceHeader';
import { PatrolIntelligenceBanners } from './PatrolIntelligenceBanners';
@@ -6,16 +9,45 @@ import { PatrolAttentionWorkbench } from './PatrolAttentionWorkbench';
export function PatrolIntelligenceSurface() {
const state = usePatrolIntelligenceState();
+ const [findingsOpen, setFindingsOpen] = createSignal(false);
+ let findingsSummary: HTMLElement | undefined;
+ const openWorkCount = () => aiIntelligenceStore.patrolOpenWorkCount;
+ const openFindings = () => {
+ setFindingsOpen(true);
+ queueMicrotask(() => {
+ findingsSummary?.scrollIntoView?.({ block: 'start' });
+ findingsSummary?.focus?.({ preventScroll: true });
+ });
+ };
return (
-
+
-
-
- Patrol checks, investigations, and run history
+ setFindingsOpen(event.currentTarget.open)}
+ >
+
+
+ Patrol checks, investigations, and run history
+ 0}>
+
+ {openWorkCount()} open
+
+
+