Explain alert acknowledgement and snooze without implying recovery

Document existing incident holds, hidden active alerts, bulk scope and the local-calendar snooze preset. Link delivery troubleshooting to the controls and verify the rendered help and acknowledgement readback.

Contract-Neutral: Existing help and verification only; no alert runtime, API, schema or frontend control changes.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-07 05:16:48 +01:00
parent 78057367d4
commit 6987eaddae
6 changed files with 201 additions and 5 deletions
+34
View File
@@ -549,6 +549,40 @@ This tolerance delays activation; it does not disable powered-off monitoring.
Use the existing guest offline-alert toggle when a guest should never produce
powered-off alerts.
### Acknowledge and snooze existing alerts
In **Alerts → Overview**, these actions apply to an existing incident, not its
threshold rule. Monitoring continues; neither action repairs the workload or
confirms recovery.
| Action | Effect |
| --- | --- |
| **Acknowledge** | Marks the incident as seen. While acknowledged, further firing notifications, escalation and its recovery notification are suppressed. The underlying alert can remain active. |
| **Unacknowledge** | Removes that acknowledgement. Normal notification policies apply again; this is not a guaranteed immediate resend. |
| **Snooze** | Pauses notifications and escalation for this incident until the selected time, including critical notifications. Monitoring can still detect recovery during the snooze. |
| **Resume** | Ends this incident's snooze early. It does not remove an acknowledgement or clear the underlying alert. |
Acknowledged alerts are hidden from the default active list and excluded from
its **Active** count. Use **Show acknowledged** to inspect them and
**Unacknowledge** only when they need attention again. **No unacknowledged
alerts** does not mean every workload recovered. Before **Acknowledge all**, read
the count and scope: the toolbar applies to all unacknowledged active alerts;
a group's button applies to that group, including its collapsed related alerts.
Use the individual action when only one incident has been reviewed.
Choose a **Snooze** duration on the incident's card and check its **Snoozed until**
time. **Until tomorrow at 9:00** uses the browser's local timezone, not the
[quiet-hours timezone](#quiet-hours-and-notification-holds). On expiry or
**Resume**, an acknowledged incident remains acknowledged; remove that hold
separately if intended. Other schedules, routing, cooldowns and destination
settings still apply, so ending a snooze is not proof of delivery or a promise
to replay missed notifications.
These incident actions are not **Dismiss retained failures** or **Retry retained
deliveries**. They do not retry a failed notification. Check fresh readings and
the workload for recovery, and [Recent delivery activity](TROUBLESHOOTING.md#test-succeeds-but-real-alerts-are-missing)
for delivery. Do not create an outage or lower thresholds to test an action.
### Quiet hours and notification holds
Open **Alerts → Schedule → Quiet hours** to enable a notification quiet period.
+4 -2
View File
@@ -623,8 +623,10 @@ alert was generated, routed or delivered to the intended recipient.
Turn delivery on there only when you intend to send alerts.
- Check the affected alert, the destination's **Enabled** state, minimum alert
severity and tag filters. Review the [quiet-hours schedule](CONFIGURATION.md#quiet-hours-and-notification-holds)
and any mute, acknowledgement or maintenance policy before treating an absent
attempt as a transport failure.
and [acknowledgement or snooze](CONFIGURATION.md#acknowledge-and-snooze-existing-alerts),
plus any mute or maintenance policy, before treating an absent attempt as a
transport failure. In **Alerts → Overview**, use **Show acknowledged** to find
hidden incidents; **Resume** ends a snooze but does not remove acknowledgement.
- Use **Recent delivery activity** to correlate the original alert, destination
and absolute timestamp, including held-notification reasons. An empty window
is not proof of healthy delivery; an **unavailable** read is not an empty log.
@@ -549,6 +549,40 @@ This tolerance delays activation; it does not disable powered-off monitoring.
Use the existing guest offline-alert toggle when a guest should never produce
powered-off alerts.
### Acknowledge and snooze existing alerts
In **Alerts → Overview**, these actions apply to an existing incident, not its
threshold rule. Monitoring continues; neither action repairs the workload or
confirms recovery.
| Action | Effect |
| --- | --- |
| **Acknowledge** | Marks the incident as seen. While acknowledged, further firing notifications, escalation and its recovery notification are suppressed. The underlying alert can remain active. |
| **Unacknowledge** | Removes that acknowledgement. Normal notification policies apply again; this is not a guaranteed immediate resend. |
| **Snooze** | Pauses notifications and escalation for this incident until the selected time, including critical notifications. Monitoring can still detect recovery during the snooze. |
| **Resume** | Ends this incident's snooze early. It does not remove an acknowledgement or clear the underlying alert. |
Acknowledged alerts are hidden from the default active list and excluded from
its **Active** count. Use **Show acknowledged** to inspect them and
**Unacknowledge** only when they need attention again. **No unacknowledged
alerts** does not mean every workload recovered. Before **Acknowledge all**, read
the count and scope: the toolbar applies to all unacknowledged active alerts;
a group's button applies to that group, including its collapsed related alerts.
Use the individual action when only one incident has been reviewed.
Choose a **Snooze** duration on the incident's card and check its **Snoozed until**
time. **Until tomorrow at 9:00** uses the browser's local timezone, not the
[quiet-hours timezone](#quiet-hours-and-notification-holds). On expiry or
**Resume**, an acknowledged incident remains acknowledged; remove that hold
separately if intended. Other schedules, routing, cooldowns and destination
settings still apply, so ending a snooze is not proof of delivery or a promise
to replay missed notifications.
These incident actions are not **Dismiss retained failures** or **Retry retained
deliveries**. They do not retry a failed notification. Check fresh readings and
the workload for recovery, and [Recent delivery activity](TROUBLESHOOTING.md#test-succeeds-but-real-alerts-are-missing)
for delivery. Do not create an outage or lower thresholds to test an action.
### Quiet hours and notification holds
Open **Alerts → Schedule → Quiet hours** to enable a notification quiet period.
@@ -623,8 +623,10 @@ alert was generated, routed or delivered to the intended recipient.
Turn delivery on there only when you intend to send alerts.
- Check the affected alert, the destination's **Enabled** state, minimum alert
severity and tag filters. Review the [quiet-hours schedule](CONFIGURATION.md#quiet-hours-and-notification-holds)
and any mute, acknowledgement or maintenance policy before treating an absent
attempt as a transport failure.
and [acknowledgement or snooze](CONFIGURATION.md#acknowledge-and-snooze-existing-alerts),
plus any mute or maintenance policy, before treating an absent attempt as a
transport failure. In **Alerts → Overview**, use **Show acknowledged** to find
hidden incidents; **Resume** ends a snooze but does not remove acknowledgement.
- Use **Recent delivery activity** to correlate the original alert, destination
and absolute timestamp, including held-notification reasons. An empty window
is not proof of healthy delivery; an **unavailable** read is not an empty log.
@@ -147,7 +147,7 @@ describe('useAlertOverviewState', () => {
acknowledged: makeAlert('acknowledged', new Date(now - 2 * 60_000).toISOString(), true),
old: makeAlert('old', new Date(now - 3 * 86_400_000).toISOString(), false),
});
const [showAcknowledged] = createSignal(false);
const [showAcknowledged, setShowAcknowledged] = createSignal(false);
const updateAlert = vi.fn();
vi.mocked(AlertsAPI.acknowledge).mockResolvedValue(undefined as any);
@@ -185,6 +185,17 @@ describe('useAlertOverviewState', () => {
);
expect(notificationStore.success).toHaveBeenCalledWith('Alert acknowledged');
expect(result.processingAlerts().has('warning')).toBe(true);
// Hiding/counting acknowledgement is not deletion or measured recovery.
expect(result.alertStats()).toMatchObject({ active: 1, acknowledged: 2 });
expect(result.filteredAlerts().map((alert) => alert.id)).toEqual(['old']);
expect(Object.keys(activeAlerts())).toHaveLength(3);
setShowAcknowledged(true);
expect(result.filteredAlerts().map((alert) => alert.id)).toEqual([
'old',
'warning',
'acknowledged',
]);
setShowAcknowledged(false);
vi.advanceTimersByTime(1500);
expect(result.processingAlerts().has('warning')).toBe(false);
@@ -0,0 +1,113 @@
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
import { renderDocMarkdown } from '@/features/docs/docMarkdown';
import { snoozePresetExpiry } from '@/features/alerts/AlertSnoozeAction';
import {
getAlertOverviewAcknowledgedToggleLabel,
getAlertOverviewPrimaryActionLabel,
getAlertOverviewResumeLabel,
getAlertOverviewSnoozeOptionLabel,
} from '@/utils/alertOverviewPresentation';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
const read = (name: string): string => readFileSync(path.join(root, name), 'utf8');
function article(name: string) {
const element = document.createElement('article');
element.innerHTML = renderDocMarkdown(read(`docs/${name}.md`), name);
return element;
}
function actions() {
const source = read('docs/CONFIGURATION.md')
.split('### Acknowledge and snooze existing alerts')[1]
?.split('### Quiet hours and notification holds')[0];
expect(source, 'existing incident controls need an explanation of their holds').toBeDefined();
const element = document.createElement('article');
element.innerHTML = renderDocMarkdown(source!, 'CONFIGURATION');
return element;
}
describe('existing alert incident actions help', () => {
it('renders the actual control names and keeps notification holds separate from recovery', () => {
const rows = [...actions().querySelectorAll('tbody tr')].map((row) =>
row.textContent?.replace(/\s+/g, ' '),
);
expect(rows).toHaveLength(4);
expect(rows[0]).toContain(
getAlertOverviewPrimaryActionLabel({ acknowledged: false, processing: false }),
);
expect(rows[0]).toContain('firing notifications, escalation and its recovery notification');
expect(rows[0]).toContain('underlying alert can remain active');
expect(rows[1]).toContain(
getAlertOverviewPrimaryActionLabel({ acknowledged: true, processing: false }),
);
expect(rows[1]).toContain('not a guaranteed immediate resend');
expect(rows[2]).toContain('including critical notifications');
expect(rows[3]).toContain(getAlertOverviewResumeLabel());
expect(rows[3]).toContain('does not remove an acknowledgement');
});
it('explains hidden incidents and bulk scope rather than an all-clear', () => {
const text = actions().textContent!.replace(/\s+/g, ' ');
expect(text).toContain('neither action repairs the workload or confirms recovery');
expect(text).toContain(
'hidden from the default active list and excluded from its Active count',
);
expect(text).toContain(getAlertOverviewAcknowledgedToggleLabel(false));
expect(text).toContain('does not mean every workload recovered');
expect(text).toContain('toolbar applies to all unacknowledged active alerts');
expect(text).toContain('including its collapsed related alerts');
expect(text).toContain('Use the individual action when only one incident has been reviewed');
});
it('binds tomorrow morning to the real local-calendar preset and preserves separate holds', () => {
const text = actions().textContent!.replace(/\s+/g, ' ');
expect(text).toContain(getAlertOverviewSnoozeOptionLabel('tomorrowMorning'));
expect(text).toContain("browser's local timezone, not the quiet-hours timezone");
// Deliberately use local constructors: the preset uses the browser's date,
// not a UTC duration or the alert schedule's independently configured zone.
const expiry = snoozePresetExpiry('tomorrowMorning', new Date(2026, 9, 7, 22, 30));
expect(expiry.getFullYear()).toBe(2026);
expect(expiry.getMonth()).toBe(9);
expect(expiry.getDate()).toBe(8);
expect(expiry.getHours()).toBe(9);
expect(expiry.getMinutes()).toBe(0);
expect(text).toContain('an acknowledged incident remains acknowledged');
expect(text).toContain('not proof of delivery or a promise to replay missed notifications');
expect(text).toContain('They do not retry a failed notification');
expect(text).toContain('Do not create an outage or lower thresholds');
});
it('ships exact guidance with working diagnosis and schedule links', () => {
for (const name of ['CONFIGURATION', 'TROUBLESHOOTING']) {
expect(read(`frontend-modern/public/docs/${name}.md`)).toBe(read(`docs/${name}.md`));
}
for (const [from, to, fragment, label] of [
[
'TROUBLESHOOTING',
'CONFIGURATION',
'acknowledge-and-snooze-existing-alerts',
'acknowledgement or snooze',
],
[
'CONFIGURATION',
'TROUBLESHOOTING',
'test-succeeds-but-real-alerts-are-missing',
'Recent delivery activity',
],
]) {
const link = article(from).querySelector(`a[href="/docs/${to}#${fragment}"]`);
expect(link?.textContent).toBe(label);
expect(link?.hasAttribute('data-doc-link')).toBe(true);
expect(article(to).querySelector(`[id="${fragment}"]`)).not.toBeNull();
}
expect(actions().querySelector('a[href="#quiet-hours-and-notification-holds"]')).not.toBeNull();
expect(
article('CONFIGURATION').querySelector('#quiet-hours-and-notification-holds'),
).not.toBeNull();
});
});