diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/1440.png b/docs/qualification/release-v6.4-diagnosis-ordering/1440.png
new file mode 100644
index 000000000..11ef19b88
Binary files /dev/null and b/docs/qualification/release-v6.4-diagnosis-ordering/1440.png differ
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/390.png b/docs/qualification/release-v6.4-diagnosis-ordering/390.png
new file mode 100644
index 000000000..7619f4904
Binary files /dev/null and b/docs/qualification/release-v6.4-diagnosis-ordering/390.png differ
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/900.png b/docs/qualification/release-v6.4-diagnosis-ordering/900.png
new file mode 100644
index 000000000..ba9a1e0e4
Binary files /dev/null and b/docs/qualification/release-v6.4-diagnosis-ordering/900.png differ
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/README.md b/docs/qualification/release-v6.4-diagnosis-ordering/README.md
new file mode 100644
index 000000000..394c92c6a
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/README.md
@@ -0,0 +1,43 @@
+# Release-line Overview diagnosis ordering regression
+
+Exact runtime parent: `101bae33963ee65a5a83bc229d081b43825de889`.
+Backport source: `495562ef663e473889490a774a80cf8d04405732` (main).
+Regression provenance: `32e620a6c36b4ce08729de747cb232dfabf5c1ba`
+introduced the asynchronous delivery diagnosis projection on 26 August 2026;
+it is an ancestor of this parent and v6.4.1. The unversioned completion path
+is also present in v6.4.3-rc.1. This repairs an existing candidate surface,
+not new product scope (RELEASE_PROMOTION_POLICY, Release Train rules 2/4).
+
+The main runtime patch and its two regression tests apply without adaptation.
+With only those tests and the browser fixture added to the exact parent,
+2 tests fail and 11 pass: overlapping reads replace the current disabled
+warning, and an obsolete response repopulates an empty active set. Chromium
+also fails the retained-warning assertion (0 versus 1) at its first viewport.
+These are local release-line reproductions, not inherited main receipts.
+
+With the six-line runtime patch, all 13 tests pass. Another 25 adjacent
+presentation and delivery-action tests pass. TypeScript (`tsc --noEmit`) exits
+0. No full repository suite was run for this bounded UI backport.
+
+Commands:
+
+- `npm --prefix frontend-modern test -- src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx src/features/alerts/__tests__/useAlertOverviewState.test.tsx`
+- `npm --prefix frontend-modern test -- src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx src/features/alerts/__tests__/deliveryDiagnosisPresentation.test.ts`
+- `npm --prefix frontend-modern run type-check`
+- `pulse-heavy-run -- node scripts/check-alert-diagnosis-ordering.mjs`
+
+The fixture uses the real Overview component and synthetic API responses on
+loopback only. It does not exercise an installed backend, actual delivery,
+recipient receipt, or production routing. Latest-started ownership prevents
+superseded successful reads from updating state, including empty-set
+invalidation. Failed refreshes still retain the previous snapshot; no new
+freshness claim or indicator is added.
+
+This does not resolve warning-projection ordering, SQLite crashes, paid
+convergence, recipient qualification or the release HOLD. No public release
+or issue-resolution claim follows from this component evidence.
+
+Patched Chromium passes at 1440, 900 and 390 pixels: warning retained, stale
+dispatch absent, new alert present, text within viewport and no page errors.
+Desktop and phone screenshots were visually inspected: warning and controls
+remain visible. Receipt binds the runtime file hash to the exact parent.
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/adjacent-tests.txt b/docs/qualification/release-v6.4-diagnosis-ordering/adjacent-tests.txt
new file mode 100644
index 000000000..d08483421
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/adjacent-tests.txt
@@ -0,0 +1,14 @@
+
+> pulse-modern@1.0.0 test
+> vitest run src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx src/features/alerts/__tests__/deliveryDiagnosisPresentation.test.ts
+
+
+ RUN v3.2.7 /var/lib/pulse-maintainer/team-worktrees/20260906T163509Z-release-line/pulse@release_v6.4/frontend-modern
+
+ ✓ src/features/alerts/__tests__/deliveryDiagnosisPresentation.test.ts (16 tests) 137ms
+ ✓ src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx (9 tests) 358ms
+
+ Test Files 2 passed (2)
+ Tests 25 passed (25)
+ Start at 17:38:35
+ Duration 2.35s (transform 1.13s, setup 212ms, collect 1.21s, tests 495ms, environment 727ms, prepare 184ms)
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/fixed-browser.txt b/docs/qualification/release-v6.4-diagnosis-ordering/fixed-browser.txt
new file mode 100644
index 000000000..c47cb4689
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/fixed-browser.txt
@@ -0,0 +1 @@
+{"result":"passed","viewports":[1440,900,390],"scope":"Real Overview and Chromium; scripted diagnoses, not installed delivery or receipt"}
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/fixed-tests.txt b/docs/qualification/release-v6.4-diagnosis-ordering/fixed-tests.txt
new file mode 100644
index 000000000..cd55867b9
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/fixed-tests.txt
@@ -0,0 +1,14 @@
+
+> pulse-modern@1.0.0 test
+> vitest run src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx src/features/alerts/__tests__/useAlertOverviewState.test.tsx
+
+
+ RUN v3.2.7 /var/lib/pulse-maintainer/team-worktrees/20260906T163509Z-release-line/pulse@release_v6.4/frontend-modern
+
+ ✓ src/features/alerts/__tests__/useAlertOverviewState.test.tsx (8 tests) 41ms
+ ✓ src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx (5 tests) 156ms
+
+ Test Files 2 passed (2)
+ Tests 13 passed (13)
+ Start at 17:38:05
+ Duration 1.84s (transform 964ms, setup 109ms, collect 1.18s, tests 197ms, environment 658ms, prepare 123ms)
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/parent-browser.txt b/docs/qualification/release-v6.4-diagnosis-ordering/parent-browser.txt
new file mode 100644
index 000000000..695ccd2e2
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/parent-browser.txt
@@ -0,0 +1,18 @@
+node:internal/modules/run_main:107
+ triggerUncaughtException(
+ ^
+
+AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
+
+0 !== 1
+
+ at file:///var/lib/pulse-maintainer/team-worktrees/20260906T163509Z-release-line/pulse@release_v6.4/scripts/check-alert-diagnosis-ordering.mjs:103:12 {
+ generatedMessage: true,
+ code: 'ERR_ASSERTION',
+ actual: 0,
+ expected: 1,
+ operator: 'strictEqual',
+ diff: 'simple'
+}
+
+Node.js v24.20.0
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/parent-tests.txt b/docs/qualification/release-v6.4-diagnosis-ordering/parent-tests.txt
new file mode 100644
index 000000000..eddb46317
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/parent-tests.txt
@@ -0,0 +1,80 @@
+
+> pulse-modern@1.0.0 test
+> vitest run src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx src/features/alerts/__tests__/useAlertOverviewState.test.tsx
+
+
+ RUN v3.2.7 /var/lib/pulse-maintainer/team-worktrees/20260906T163509Z-release-line/pulse@release_v6.4/frontend-modern
+
+ ❯ src/features/alerts/__tests__/useAlertOverviewState.test.tsx (8 tests | 1 failed) 47ms
+ × useAlertOverviewState > invalidates pending diagnosis reads when the active set becomes empty 13ms
+ → expected { a1: { alertIdentifier: 'a1', …(1) } } to deeply equal {}
+ ✓ useAlertOverviewState > owns overview stats, filtering, and acknowledge flows outside the tab shell 4ms
+ ✓ useAlertOverviewState > keeps a stable id order for alerts sharing a startTime (#1218) 1ms
+ ✓ useAlertOverviewState > counts alerts with missing startTime in total24h 1ms
+ ✓ useAlertOverviewState > groups only explicit shared-system correlations across resources 1ms
+ ✓ useAlertOverviewState > groups explicit infrastructure synthesis without hiding the supporting detector 1ms
+ ✓ useAlertOverviewState > does not infer correlation by truncating resource paths 1ms
+ ✓ useAlertOverviewState > still groups multiple detector signals for the exact same resource 1ms
+ ❯ src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx (5 tests | 1 failed) 167ms
+ ✓ OverviewTab delivery status line > renders held-notification status from the bulk diagnosis endpoint 95ms
+ ✓ OverviewTab delivery status line > does not turn dispatch evidence into receipt for 'ready' 16ms
+ ✓ OverviewTab delivery status line > does not turn dispatch evidence into receipt for 'cooldown' 17ms
+ × OverviewTab delivery status line > ignores an older diagnosis response after the active alert set changes 24ms
+ → expected to be null
+ ✓ OverviewTab delivery status line > renders no delivery line when the diagnosis fetch fails 10ms
+
+⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
+
+ FAIL src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx > OverviewTab delivery status line > ignores an older diagnosis response after the active alert set changes
+AssertionError: expected to be null
+
+- Expected:
+null
+
++ Received:
+
+ Dispatch requested 11:15 AM
+
+
+ ❯ src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx:145:56
+ 143| finishOlder([makeDiagnosis('a1', { lastNotified: '2026-08-26T10:15…
+ 144| await Promise.resolve();
+ 145| expect(screen.queryByText(/^Dispatch requested /)).toBeNull();
+ | ^
+ 146| expect(screen.getByText('Notifications are turned off')).toBeTruth…
+ 147| });
+
+⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
+
+ FAIL src/features/alerts/__tests__/useAlertOverviewState.test.tsx > useAlertOverviewState > invalidates pending diagnosis reads when the active set becomes empty
+AssertionError: expected { a1: { alertIdentifier: 'a1', …(1) } } to deeply equal {}
+
+- Expected
++ Received
+
+- {}
++ {
++ "a1": {
++ "alertIdentifier": "a1",
++ "reason": "ready",
++ },
++ }
+
+ ❯ src/features/alerts/__tests__/useAlertOverviewState.test.tsx:85:40
+ 83| finish([{ alertIdentifier: 'a1', reason: 'ready' } as AlertDeliver…
+ 84| await Promise.resolve();
+ 85| expect(result.deliveryDiagnoses()).toEqual({});
+ | ^
+ 86| });
+ 87|
+
+⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
+
+
+ Test Files 2 failed (2)
+ Tests 2 failed | 11 passed (13)
+ Start at 17:37:40
+ Duration 1.88s (transform 1.03s, setup 143ms, collect 1.24s, tests 214ms, environment 607ms, prepare 329ms)
diff --git a/docs/qualification/release-v6.4-diagnosis-ordering/typecheck.txt b/docs/qualification/release-v6.4-diagnosis-ordering/typecheck.txt
new file mode 100644
index 000000000..4b83befb6
--- /dev/null
+++ b/docs/qualification/release-v6.4-diagnosis-ordering/typecheck.txt
@@ -0,0 +1,3 @@
+
+> pulse-modern@1.0.0 type-check
+> tsc --noEmit
diff --git a/docs/release-control/v6/internal/subsystems/alerts.md b/docs/release-control/v6/internal/subsystems/alerts.md
index 3a9a796dc..17c361dd9 100644
--- a/docs/release-control/v6/internal/subsystems/alerts.md
+++ b/docs/release-control/v6/internal/subsystems/alerts.md
@@ -15,6 +15,15 @@
## Purpose
+Overview delivery diagnoses use latest-started refresh ownership. Older bulk
+responses cannot overwrite newer card notification status, and an empty active
+alert set invalidates outstanding reads. Disposal also prevents updates. Failed
+refreshes retain the existing snapshot; this ordering repair does not add a
+freshness indicator or establish recipient receipt. Verify response overlap in
+`OverviewTab.deliverystatus.test.tsx`, empty-set invalidation in
+`useAlertOverviewState.test.tsx`, and rendered ordering at three widths using
+`scripts/check-alert-diagnosis-ordering.mjs`.
+
Confirmed canonical metric recovery publishes the clearing evaluation's value,
observation time, and resolved metric wording in the snapshot consumed by
recent-resolution reads and notification callbacks. Clone that snapshot before
diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
index de5dd45da..f721eebeb 100644
--- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md
+++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
@@ -20,6 +20,15 @@
## Purpose
+Overview delivery diagnoses use latest-started refresh ownership. Older bulk
+responses cannot overwrite newer card notification status, and an empty active
+alert set invalidates outstanding reads. Disposal also prevents updates. Failed
+refreshes retain the existing snapshot; this ordering repair does not add a
+freshness indicator or establish recipient receipt. Verify response overlap in
+`OverviewTab.deliverystatus.test.tsx`, empty-set invalidation in
+`useAlertOverviewState.test.tsx`, and rendered ordering at three widths using
+`scripts/check-alert-diagnosis-ordering.mjs`.
+
Proxmox backup presentation treats every manifestless PBS artifact as
non-recoverable. It renders the artifact as `Running` when current writer
visibility is absent or a matching writer is active, and as danger-tone
diff --git a/frontend-modern/browser-verification.json b/frontend-modern/browser-verification.json
index f36da173a..11dde8f7a 100644
--- a/frontend-modern/browser-verification.json
+++ b/frontend-modern/browser-verification.json
@@ -1,13 +1,13 @@
{
"version": 1,
- "base_sha": "2d550da942c6f0a7e7896e7aca86493983f90b84",
- "verified_at": "2026-09-06T15:54:59.817913Z",
+ "base_sha": "101bae33963ee65a5a83bc229d081b43825de889",
+ "verified_at": "2026-09-06T16:42:39.585210Z",
"result": "passed",
"changed_paths": [
- "frontend-modern/src/features/alerts/deliveryDiagnosisPresentation.ts"
+ "frontend-modern/src/features/alerts/useAlertOverviewState.ts"
],
"content_sha256": {
- "frontend-modern/src/features/alerts/deliveryDiagnosisPresentation.ts": "bc3eadf8f790517b377430a42eb67e8fdfcadb53bb13b87cd971d6a9ab91d607"
+ "frontend-modern/src/features/alerts/useAlertOverviewState.ts": "64d0b891e7ad228e8590da859dc25e825b6164c8cf76a01983a219d6cd079b23"
},
"routes": [
"/qualification (loopback synthetic Overview, not production routing)"
@@ -27,11 +27,11 @@
}
],
"states": [
- "Ready and cooldown show Dispatch requested; cooldown says next eligible; missing timestamp shows Notification pending. Scripted diagnoses, not recipient receipt.",
- "390px screenshot visually inspected: longer status wraps within card and controls remain visible."
+ "Newer notifications-disabled response remains visible after older dispatch response completes. Synthetic diagnoses, not recipient receipt.",
+ "Desktop and phone screenshots visually inspected: current warning and card controls remain visible within viewport."
],
"interactions": [
- "pulse-heavy-run -- node scripts/check-alert-dispatch-copy.mjs passed at all three widths; two dispatch labels, no Notified labels, one pending label, text fits viewport, no page errors.",
- "Evidence: /var/lib/pulse-maintainer/queue/staging/20260906T155036Z-release-line/browser-dispatch.log and browser-dispatch/*.png."
+ "pulse-heavy-run -- node scripts/check-alert-diagnosis-ordering.mjs passed at 1440, 900 and 390 pixels: retained warning, absent stale dispatch, new alert present, text within viewport, no page errors.",
+ "Exact-parent failing and patched passing evidence: docs/qualification/release-v6.4-diagnosis-ordering/."
]
}
diff --git a/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx b/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx
index 631ae1590..5dcaaaeb7 100644
--- a/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx
+++ b/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliverystatus.test.tsx
@@ -1,4 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
+import { createSignal } from 'solid-js';
import { cleanup, render, screen, waitFor } from '@solidjs/testing-library';
import { DEFAULT_LOCALE, setActiveLocale } from '@/i18n';
import type { Alert, AlertDeliveryDiagnosis } from '@/types/api';
@@ -124,6 +125,27 @@ describe('OverviewTab delivery status line', () => {
if (state.reason === 'cooldown') expect(screen.getByText(/next eligible/)).toBeTruthy();
});
+ it('ignores an older diagnosis response after the active alert set changes', async () => {
+ let finishOlder!: (value: AlertDeliveryDiagnosis[]) => void;
+ getDeliveryDiagnoses.mockReturnValueOnce(
+ new Promise((resolve) => {
+ finishOlder = resolve;
+ }),
+ );
+ getDeliveryDiagnoses.mockResolvedValueOnce([
+ makeDiagnosis('a1', { status: 'suppressed', reason: 'notifications_disabled' }),
+ ]);
+ const [alerts, setAlerts] = createSignal>({ a1: makeAlert('a1') });
+ render(() => );
+ await waitFor(() => expect(getDeliveryDiagnoses).toHaveBeenCalledTimes(1));
+ setAlerts({ a1: makeAlert('a1'), a2: makeAlert('a2') });
+ await waitFor(() => expect(screen.getByText('Notifications are turned off')).toBeTruthy());
+ finishOlder([makeDiagnosis('a1', { lastNotified: '2026-08-26T10:15:00Z' })]);
+ await Promise.resolve();
+ expect(screen.queryByText(/^Dispatch requested /)).toBeNull();
+ expect(screen.getByText('Notifications are turned off')).toBeTruthy();
+ });
+
it('renders no delivery line when the diagnosis fetch fails', async () => {
const activeAlerts: Record = { a1: makeAlert('a1') };
getDeliveryDiagnoses.mockRejectedValue(new Error('boom'));
diff --git a/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx b/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx
index edc4f4569..2b3a96ddc 100644
--- a/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx
+++ b/frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx
@@ -4,12 +4,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { AlertsAPI } from '@/api/alerts';
import { notificationStore } from '@/stores/notifications';
-import type { Alert } from '@/types/api';
+import type { Alert, AlertDeliveryDiagnosis } from '@/types/api';
import { useAlertOverviewState } from '../useAlertOverviewState';
vi.mock('@/api/alerts', () => ({
AlertsAPI: {
+ getDeliveryDiagnoses: vi.fn(),
acknowledge: vi.fn(),
bulkAcknowledge: vi.fn(),
unacknowledge: vi.fn(),
@@ -47,6 +48,7 @@ describe('useAlertOverviewState', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-03-22T12:00:00Z'));
+ vi.mocked(AlertsAPI.getDeliveryDiagnoses).mockReset().mockResolvedValue([]);
vi.mocked(AlertsAPI.acknowledge).mockReset();
vi.mocked(AlertsAPI.unacknowledge).mockReset();
vi.mocked(AlertsAPI.bulkAcknowledge).mockReset();
@@ -58,6 +60,31 @@ describe('useAlertOverviewState', () => {
vi.useRealTimers();
});
+ it('invalidates pending diagnosis reads when the active set becomes empty', async () => {
+ let finish!: (value: AlertDeliveryDiagnosis[]) => void;
+ vi.mocked(AlertsAPI.getDeliveryDiagnoses).mockReturnValueOnce(
+ new Promise((resolve) => {
+ finish = resolve;
+ }),
+ );
+ const [activeAlerts, setActiveAlerts] = createSignal>({
+ a1: makeAlert('a1', new Date().toISOString()),
+ });
+ const { result } = renderHook(() =>
+ useAlertOverviewState({
+ activeAlerts,
+ overrides: () => [],
+ showAcknowledged: () => true,
+ updateAlert: vi.fn(),
+ }),
+ );
+ expect(AlertsAPI.getDeliveryDiagnoses).toHaveBeenCalledOnce();
+ setActiveAlerts({});
+ finish([{ alertIdentifier: 'a1', reason: 'ready' } as AlertDeliveryDiagnosis]);
+ await Promise.resolve();
+ expect(result.deliveryDiagnoses()).toEqual({});
+ });
+
it('owns overview stats, filtering, and acknowledge flows outside the tab shell', async () => {
const now = Date.now();
const [activeAlerts] = createSignal>({
diff --git a/frontend-modern/src/features/alerts/useAlertOverviewState.ts b/frontend-modern/src/features/alerts/useAlertOverviewState.ts
index 650a92063..377bb479e 100644
--- a/frontend-modern/src/features/alerts/useAlertOverviewState.ts
+++ b/frontend-modern/src/features/alerts/useAlertOverviewState.ts
@@ -68,17 +68,21 @@ export function useAlertOverviewState(props: UseAlertOverviewStateProps) {
Record
>({});
let diagnosisStateDisposed = false;
+ let diagnosisRequestVersion = 0;
onCleanup(() => {
diagnosisStateDisposed = true;
});
const refreshDeliveryDiagnoses = async () => {
+ // A slower previous refresh must not replace a newer notification state.
+ // Increment even for an empty alert set to invalidate outstanding requests.
+ const requestVersion = ++diagnosisRequestVersion;
if (activeAlerts().length === 0) {
setDeliveryDiagnoses({});
return;
}
try {
const list = await AlertsAPI.getDeliveryDiagnoses();
- if (diagnosisStateDisposed) return;
+ if (diagnosisStateDisposed || requestVersion !== diagnosisRequestVersion) return;
const next: Record = {};
for (const diagnosis of list) {
next[diagnosis.alertIdentifier || diagnosis.alertId] = diagnosis;
diff --git a/scripts/check-alert-diagnosis-ordering.mjs b/scripts/check-alert-diagnosis-ordering.mjs
new file mode 100644
index 000000000..45b4e30f4
--- /dev/null
+++ b/scripts/check-alert-diagnosis-ordering.mjs
@@ -0,0 +1,146 @@
+// Isolated real-browser component qualification; no installed backend or delivery claim.
+import { createServer } from "../frontend-modern/node_modules/vite/dist/node/index.js";
+import solid from "../frontend-modern/node_modules/vite-plugin-solid/dist/esm/index.mjs";
+import { chromium } from "@playwright/test";
+import { resolve } from "node:path";
+import { mkdirSync } from "node:fs";
+import assert from "node:assert/strict";
+const root = resolve("frontend-modern");
+process.chdir(root);
+const fixture = `
+import { createSignal } from 'solid-js';
+import { render } from 'solid-js/web';
+import { Router, Route } from '@solidjs/router';
+import { AlertsAPI } from '/src/api/alerts';
+import { NotificationsAPI } from '/src/api/notifications';
+import { OverviewTab } from '/src/features/alerts/OverviewTab';
+import '/src/index.css';
+let finishOlder;
+let requests = 0;
+AlertsAPI.getDeliveryDiagnoses = () => {
+ requests++;
+ if (requests === 1) return new Promise(resolve => { finishOlder = resolve; });
+ return Promise.resolve([{alertIdentifier:'a1', alertId:'a1', status:'suppressed',
+ reason:'notifications_disabled', message:'Notifications disabled by current configuration'}]);
+};
+window.finishOlder = () => finishOlder([{alertIdentifier:'a1', alertId:'a1',
+ status:'would_send',reason:'ready',lastNotified:'2026-08-26T10:15:00Z'}]);
+window.requestCount = () => requests;
+AlertsAPI.getEvents = async () => [];
+NotificationsAPI.getHealth = async () => ({queue:{status:'healthy'}});
+const alert = id => ({id,resourceId:id,resourceName:'VM '+id,type:'cpu',level:'warning',
+message:'High CPU on '+id,startTime:new Date().toISOString(),acknowledged:false,node:'node1'});
+function Fixture() {
+const [alerts, setAlerts] = createSignal({a1:alert('a1')});
+return
+{}} showQuickTip={()=>false} dismissQuickTip={()=>{}} showAcknowledged={()=>true}
+setShowAcknowledged={()=>{}} alertsDisabled={()=>false}/>; }
+render(()=>,document.getElementById('root'));
+`;
+const server = await createServer({
+ root,
+ configFile: false,
+ optimizeDeps: {
+ noDiscovery: true,
+ entries: [],
+ esbuildOptions: { target: "esnext" },
+ },
+ esbuild: { target: "esnext" },
+ plugins: [
+ solid(),
+ {
+ name: "dispatch-fixture",
+ configureServer(s) {
+ s.middlewares.use((req, res, next) => {
+ if (req.url === "/qualification") {
+ res.setHeader("Content-Type", "text/html");
+ res.end(
+ '',
+ );
+ } else next();
+ });
+ },
+ resolveId(id) {
+ if (id === "/dispatch-fixture.tsx") return id;
+ },
+ load(id) {
+ if (id === "/dispatch-fixture.tsx") return fixture;
+ },
+ },
+ ],
+ resolve: { alias: { "@": resolve(root, "src") } },
+ server: { host: "127.0.0.1", port: 5199, strictPort: true },
+});
+let browser;
+try {
+ await server.listen();
+ browser = await chromium.launch({ headless: true });
+ mkdirSync("/tmp/pulse-alert-diagnosis-ordering", { recursive: true });
+ for (const width of [1440, 900, 390]) {
+ const page = await browser.newPage({ viewport: { width, height: 1000 } });
+ const errors = [];
+ page.on("pageerror", (e) => {
+ errors.push(e.message);
+ console.error(e.message);
+ });
+ page.on("console", (m) => {
+ if (m.type() === "error") console.error(m.text());
+ });
+ await page.route("http://127.0.0.1:5199/api/**", (route) =>
+ route.fulfill({ json: [] }),
+ );
+ await page.goto("http://127.0.0.1:5199/qualification");
+ await page.waitForFunction(() => window.requestCount?.() === 1);
+ await page.getByRole("button", { name: "Add alert", exact: true }).click();
+ await page
+ .getByText("Notifications are turned off", { exact: true })
+ .waitFor();
+ await page.evaluate(async () => {
+ window.finishOlder();
+ await Promise.resolve();
+ });
+ assert.equal(
+ await page
+ .getByText("Notifications are turned off", { exact: true })
+ .count(),
+ 1,
+ );
+ assert.equal(await page.getByText(/^Dispatch requested /).count(), 0);
+ assert.equal(
+ await page.getByText("High CPU on a2", { exact: true }).count(),
+ 1,
+ );
+ const label = page.getByText("Notifications are turned off", {
+ exact: true,
+ });
+ assert.equal(
+ await label.evaluate((el) => {
+ const range = document.createRange();
+ range.selectNodeContents(el);
+ return [...range.getClientRects()].every(
+ (b) => b.left >= 0 && b.right <= innerWidth,
+ );
+ }),
+ true,
+ "current status must fit viewport",
+ );
+ assert.deepEqual(errors, []);
+ await page.screenshot({
+ path: "/tmp/pulse-alert-diagnosis-ordering/" + width + ".png",
+ fullPage: true,
+ });
+ await page.close();
+ }
+ console.log(
+ JSON.stringify({
+ result: "passed",
+ viewports: [1440, 900, 390],
+ scope:
+ "Real Overview and Chromium; scripted diagnoses, not installed delivery or receipt",
+ }),
+ );
+} finally {
+ await browser?.close();
+ await server.close();
+}