feat(compose-doctor): show All Clear when findings are acknowledged (#1721)

When every finding is acknowledged, the Doctor summary uses the success All Clear banner with distinct copy instead of the muted acknowledged chip, so operators can see at a glance that no active findings remain.
This commit is contained in:
Anso
2026-07-28 14:26:16 -04:00
committed by GitHub
parent 78475d96ef
commit 543e4ef256
3 changed files with 73 additions and 7 deletions
+2 -2
View File
@@ -58,9 +58,9 @@ The summary card at the top of the Doctor tab reflects the overall outcome of th
| **High risk** | Highest active finding is high risk | Amber card with a count of active findings by severity |
| **Warning** | Highest active finding is a warning | Blue card with a count of active findings by severity |
| **Info** | All active findings are informational | Muted card with a count of active findings by severity |
| **Acknowledged** | Every finding from the last run has been acknowledged | Muted card noting all findings are acknowledged |
| **All clear · findings acknowledged** | Model rendered; every finding from the last run has been acknowledged | Green card with "No active findings remain. One or more detected issues were reviewed and acknowledged by an authorized operator." |
The summary card also shows when the run happened and who triggered it: "ran 5 minutes ago by admin". If any findings are acknowledged, the line adds a count: "3 active (1 blocker · 2 warning) · 2 acknowledged".
The summary card also shows when the run happened and who triggered it: "ran 5 minutes ago by admin". When active findings remain alongside acknowledgements, the line adds a count: "3 active (1 blocker · 2 warning) · 2 acknowledged".
A small colored dot appears on the **Doctor** tab label when the last run's active findings include a blocker (red) or a high-risk finding (amber). Click the **X** on the summary card to dismiss it; this also clears the tab dot. The dismissal is remembered per stack and node, and clears itself automatically the moment the finding set changes (a rule clearing, a new rule firing, or a severity change), so a stale dismissal can never hide a genuinely new problem. It never clears just by opening the tab or re-running preflight to the same result, and it does not sync across browsers or teammates: it is stored in your browser only.
@@ -81,7 +81,65 @@ describe('PreflightPanel', () => {
render(<PreflightPanel stackName="web" />);
const status = await screen.findByTestId('preflight-status');
expect(status).toHaveAttribute('data-status', 'pass');
expect(status).toHaveTextContent(/all clear/i);
expect(status).toHaveTextContent('all clear');
expect(status).not.toHaveTextContent(/findings acknowledged/i);
expect(status).toHaveTextContent('No issues found in the effective model.');
});
it('renders all-clear · findings acknowledged when every finding is acknowledged', async () => {
vi.mocked(apiFetch).mockResolvedValue(jsonRes(report({
status: 'pass',
acknowledgedCount: 1,
findings: [{
ruleId: 'privileged',
severity: 'high',
title: 'Privileged container',
message: 'runs privileged',
service: 'web',
acknowledged: true,
}],
})));
render(<PreflightPanel stackName="web" />);
const status = await screen.findByTestId('preflight-status');
expect(status).toHaveAttribute('data-status', 'pass');
expect(status).toHaveTextContent('all clear · findings acknowledged');
expect(status).toHaveTextContent(
'No active findings remain. One or more detected issues were reviewed and acknowledged by an authorized operator.',
);
expect(status.className).toContain('border-success/40');
expect(status.className).toContain('bg-success/[0.06]');
expect(status.className).toContain('text-success');
expect(status.querySelector('svg.lucide-check')).not.toBeNull();
expect(status.querySelector('svg.lucide-shield-check')).toBeNull();
expect(screen.getByTestId('preflight-acknowledged-section')).toBeInTheDocument();
});
it('keeps the severity summary when active findings remain alongside acknowledgements', async () => {
vi.mocked(apiFetch).mockResolvedValue(jsonRes(report({
status: 'high',
highestSeverity: 'high',
acknowledgedCount: 1,
findings: [
{ ruleId: 'privileged', severity: 'high', title: 'Privileged container', message: 'runs privileged', service: 'web' },
{
ruleId: 'image-latest',
severity: 'warning',
title: 'Image uses a moving tag',
message: 'latest tag',
service: 'web',
acknowledged: true,
},
],
})));
render(<PreflightPanel stackName="web" />);
const status = await screen.findByTestId('preflight-status');
expect(status).toHaveAttribute('data-status', 'high');
expect(status).toHaveTextContent('high risk');
expect(status).not.toHaveTextContent(/all clear/i);
expect(status).toHaveTextContent('1 active');
expect(status).toHaveTextContent('1 acknowledged');
expect(screen.getByText('Privileged container')).toBeInTheDocument();
expect(screen.getByTestId('preflight-acknowledged-section')).toBeInTheDocument();
});
it('keeps All Clear when only inherited-healthcheck notes remain', async () => {
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useState } from 'react';
import {
Check, TriangleAlert, ShieldAlert, Info, RefreshCw, Stethoscope, X,
ChevronDown, ChevronRight, ShieldCheck, type LucideIcon,
ChevronDown, ChevronRight, type LucideIcon,
} from 'lucide-react';
import { apiFetch } from '@/lib/api';
import { cn } from '@/lib/utils';
@@ -83,8 +83,16 @@ function summaryMeta(
line: report.renderError ?? 'Sencho could not render the effective Compose model.',
};
}
if (report.activeCount === 0 && report.acknowledgedCount === 0) {
return { label: 'all clear', icon: Check, tone: 'border-success/40 bg-success/[0.06] text-success', line: 'No issues found in the effective model.' };
if (report.activeCount === 0) {
const acknowledgedOnly = report.acknowledgedCount > 0;
return {
label: acknowledgedOnly ? 'all clear · findings acknowledged' : 'all clear',
icon: Check,
tone: 'border-success/40 bg-success/[0.06] text-success',
line: acknowledgedOnly
? 'No active findings remain. One or more detected issues were reviewed and acknowledged by an authorized operator.'
: 'No issues found in the effective model.',
};
}
const meta = SEVERITY_META[report.activeHighestSeverity ?? 'info'];
const activeParts = GROUP_ORDER
@@ -95,7 +103,7 @@ function summaryMeta(
const line = report.acknowledgedCount > 0
? `${report.activeCount} active${activeParts ? ` (${activeParts})` : ''} · ${report.acknowledgedCount} acknowledged`
: (activeParts || `${report.activeCount} active`);
return { label: report.activeCount === 0 ? 'acknowledged' : meta.label, icon: report.activeCount === 0 ? ShieldCheck : meta.icon, tone: report.activeCount === 0 ? 'border-muted bg-card/40 text-stat-subtitle' : meta.tone, line };
return { label: meta.label, icon: meta.icon, tone: meta.tone, line };
}
function expiryComboboxOptions(finding: PreflightFinding): ComboboxOption[] {