mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Merge candidate 20260908T115002Z-web-product
Change-source: pulse-maintainer
This commit is contained in:
@@ -2813,3 +2813,18 @@ and fixture reset, overlap and unmount controls. It is component lifecycle
|
||||
acceptance, not an installed full-page or notification-delivery receipt.
|
||||
PR1973's proposed resourceIncidentError accessor is absent here; if introduced,
|
||||
its writes must obey the same owner check and gain a stale-error regression.
|
||||
|
||||
### Destination server-error presentation
|
||||
|
||||
Delivery activity preserves the queue's `server_error` classification as
|
||||
“Destination server error”, rather than treating it as unclassified. When that
|
||||
class dominates retained terminal failures, the full health description directs
|
||||
operators to destination service status and server logs, then to retry retained
|
||||
deliveries once the service is available. Unknown classes still use the generic
|
||||
fallback; this presentation does not change retry scheduling or imply receipt.
|
||||
|
||||
The presentation unit tests cover the label and terminal guidance.
|
||||
`scripts/check-delivery-server-error-copy.mjs` renders the real health and log
|
||||
cards in Chromium at desktop, tablet and phone widths, asserting the copy,
|
||||
absence of page errors and horizontal overflow. Scripted props are component
|
||||
evidence only, not installed notification delivery.
|
||||
|
||||
@@ -1,67 +1,24 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "701362ac25ef28dfa253f952820eed7c5a90db12",
|
||||
"verified_at": "2026-09-08T02:20:17Z",
|
||||
"base_sha": "550b2d80a175cc66ab4e7ac269b7ec350e101611",
|
||||
"verified_at": "2026-09-08T11:54:21Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/components/AI/Chat/index.tsx",
|
||||
"frontend-modern/src/components/Alerts/IncidentAssistantHandoffButton.tsx",
|
||||
"frontend-modern/src/components/Alerts/IncidentTimelineEventCard.tsx",
|
||||
"frontend-modern/src/components/Alerts/IncidentTimelinePanel.tsx",
|
||||
"frontend-modern/src/components/Alerts/incidentAssistantHandoffModel.ts",
|
||||
"frontend-modern/src/features/alerts/AlertHistoryItemActions.tsx",
|
||||
"frontend-modern/src/features/alerts/AlertHistoryMobileList.tsx",
|
||||
"frontend-modern/src/features/alerts/AlertResourceIncidentsPanel.tsx",
|
||||
"frontend-modern/src/features/alerts/MobileAlertHistoryInvestigationDialog.tsx",
|
||||
"frontend-modern/src/features/alerts/useAlertHistoryState.ts",
|
||||
"frontend-modern/src/features/alerts/useAlertResourceIncidentsState.ts",
|
||||
"frontend-modern/src/hooks/useTypeToSearch.ts",
|
||||
"frontend-modern/src/types/api.ts",
|
||||
"frontend-modern/src/utils/alertIncidentPresentation.ts"
|
||||
"frontend-modern/src/utils/alertDestinationsPresentation.ts"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/components/AI/Chat/index.tsx": "ca9e630294db426df591c3386d4fec161f34ade0bb6a8c4ea769130545a12341",
|
||||
"frontend-modern/src/components/Alerts/IncidentAssistantHandoffButton.tsx": "b7a6ea6676ed9834d8254bb4ddab1301ec3b0d2368a96fa3e91e9ab5d2cefb4f",
|
||||
"frontend-modern/src/components/Alerts/IncidentTimelineEventCard.tsx": "21e90602de00aa4765b1c40b5295a618a237501389d50ca9dbe36454abd11f32",
|
||||
"frontend-modern/src/components/Alerts/IncidentTimelinePanel.tsx": "7298d4184c243325dbd671777257c7efd66c86091eb4b83663315e2bdc131fcb",
|
||||
"frontend-modern/src/components/Alerts/incidentAssistantHandoffModel.ts": "9e777eee1be46f33ac146770fac6193cf6cd1c8b33ab0d0cd72fe57b98907a37",
|
||||
"frontend-modern/src/features/alerts/AlertHistoryItemActions.tsx": "06e75d16ccad383b7c89d109baaee71852482f49b41ef3d0f5cc99768dd35101",
|
||||
"frontend-modern/src/features/alerts/AlertHistoryMobileList.tsx": "851a2c1dc11ccbc827a6a50dfe06088b13a7307ce489b0767804daf6f33d023c",
|
||||
"frontend-modern/src/features/alerts/AlertResourceIncidentsPanel.tsx": "c6f89c4991c9cfc2b1c8c82f7fed35af93b3b0b29b16b2c4bdc61a51005bec4c",
|
||||
"frontend-modern/src/features/alerts/MobileAlertHistoryInvestigationDialog.tsx": "55af36a7194c2dbe9d944ca08f52e794f2b5e2ccbe79427bc55abe66aaa8a522",
|
||||
"frontend-modern/src/features/alerts/useAlertHistoryState.ts": "652a09b02321ad1cfd087aacc7937a6afb5cfb7da0e0d6ca997b30d5c9ac53bd",
|
||||
"frontend-modern/src/features/alerts/useAlertResourceIncidentsState.ts": "7a4de5d23f84bffed0665edbc4c9bd7cf33120d81fc82f7e9f5d3df56cffeb45",
|
||||
"frontend-modern/src/hooks/useTypeToSearch.ts": "b3f5244fd951baedc33fc8efee32c28077d94c07e5fce958eb6611bf50992a20",
|
||||
"frontend-modern/src/types/api.ts": "82177cc67fc449a28d2d3cc226605ae091bd1f83fbdb001d15b3b430e5cd4100",
|
||||
"frontend-modern/src/utils/alertIncidentPresentation.ts": "2e098b24b04c047a1b024a93608c5133720660cafc54c40408972413786a0a07"
|
||||
"frontend-modern/src/utils/alertDestinationsPresentation.ts": "67f6f9a1c98345949e162bf06a38760e61457aa1e8a2130913646647a4f29e88"
|
||||
},
|
||||
"routes": [
|
||||
"/alerts (retained upstream exact-content Chromium receipt for 13 unchanged runtime files)",
|
||||
"/qualification (fresh combined hook and AlertResourceIncidentsPanel with scripted API)"
|
||||
"/qualification (real health and delivery-log cards, scripted props)"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 1000
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 900,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"width": 767,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
@@ -70,27 +27,11 @@
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Canonical merged occurrence and source evidence, resolved alert with independently unknown current health.",
|
||||
"Loading and disabled Refresh, initial/cached read failures, Retry/Refresh, successful empty, partial history and unknown times.",
|
||||
"Saved operator note and unsaved draft across desktop/phone layout changes, long-note wrapping and persisted reload.",
|
||||
"Timeline/resource handoff into Assistant, reopened composer focus, saved funded history-and-note response across widths.",
|
||||
"Request reset, reverse completion, superseded success/failure, current failure, and success/failure after disposal.",
|
||||
"Latest per-resource owner controls incident content, failed-read state, notifications, and loading completion.",
|
||||
"Retry clears the current failed-read state while retaining cached history until the owning response succeeds.",
|
||||
"Obsolete and disposed failures do not set the resource error accessor."
|
||||
"Retained terminal server_error health diagnosis and failed delivery log entry"
|
||||
],
|
||||
"interactions": [
|
||||
"Expand/collapse native Evidence details by keyboard, All/None event filters and note draft enable/clear.",
|
||||
"Expand resource Events, inject read errors, retry and reload.",
|
||||
"Resize source and open Assistant destination across 390/900/767/768/1440 widths.",
|
||||
"Type in a modal without clearing background search, Escape and return focus to the selected card.",
|
||||
"Resume the exact saved upstream session, scroll through its note explanation and inspect actual pixels.",
|
||||
"Open a resource row, overlap refresh, reset or unmount, and resolve or reject deferred reads in controlled order."
|
||||
"Load both cards at desktop, tablet and phone widths; assert label, guidance, no page errors or horizontal overflow."
|
||||
],
|
||||
"notes": "The protected upstream receipt remains exact for 13 unchanged runtime files. The sole combined-content change, useAlertResourceIncidentsState.ts, was freshly exercised with its real panel in 16 Chromium cases at desktop and phone widths after adding ownership to the upstream failed-read signal. Screenshots are retained under /tmp/pulse-incident-ownership. This is component lifecycle evidence, not an installed Alerts route, provider delivery, touch-device, production rollout, backup/restore, or population-reliability receipt.",
|
||||
"command": "pulse-heavy-run -- node scripts/check-incident-request-ownership.mjs",
|
||||
"limitations": [
|
||||
"The 16-case qualification route uses fixture lifecycle controls and a scripted API; the supplied panel does not expose the error accessor directly, so that state is asserted through the fixture snapshot.",
|
||||
"This is not installed acceptance, provider delivery, touch-device, production rollout, backup/restore, or population-reliability proof."
|
||||
]
|
||||
"notes": "Phone screenshot visually inspected; screenshots retained at /tmp/pulse-server-error-copy. Initial fixture omitted alertIds and timed out; corrected fixture passed all three widths. No installed delivery or receipt claim.",
|
||||
"command": "pulse-heavy-run -- node scripts/check-delivery-server-error-copy.mjs"
|
||||
}
|
||||
|
||||
@@ -236,9 +236,28 @@ describe('alert destinations delivery log copy', () => {
|
||||
it('labels failure classes and falls back to unclassified for unknown values', () => {
|
||||
expect(getAlertDeliveryLogFailureClassLabel('authentication')).toBe('Authentication failure');
|
||||
expect(getAlertDeliveryLogFailureClassLabel('rate_limited')).toBe('Rate limited');
|
||||
expect(getAlertDeliveryLogFailureClassLabel('server_error')).toBe('Destination server error');
|
||||
expect(getAlertDeliveryLogFailureClassLabel('made-up-class')).toBe('Unclassified failure');
|
||||
});
|
||||
|
||||
it('gives destination availability guidance for terminal server errors', () => {
|
||||
const description = getAlertDestinationsDeliveryHealthDescription({
|
||||
status: 'degraded',
|
||||
failed: 1,
|
||||
deadLetter: 0,
|
||||
completedRetentionDays: 7,
|
||||
deadLetterRetentionDays: 30,
|
||||
failureClasses7d: { server_error: 1 },
|
||||
failureClassesAvailable: true,
|
||||
});
|
||||
expect(description).toContain('classified as server error (1)');
|
||||
expect(description).toContain(
|
||||
'Check the destination service status and server logs. Retry retained deliveries once the service is available.',
|
||||
);
|
||||
expect(description).not.toContain('Review the local notification audit details');
|
||||
expect(description).toContain('Recoverable retry attempts do not trigger this warning');
|
||||
});
|
||||
|
||||
it('names the retention window and the test-send caveat so absence is not read as failure', () => {
|
||||
expect(getAlertDestinationsDeliveryLogTitle()).toBe('Recent delivery activity');
|
||||
const description = getAlertDestinationsDeliveryLogDescription(7, 30);
|
||||
|
||||
@@ -229,6 +229,7 @@ export function getAlertDestinationsDeliveryHealthDescription(
|
||||
tls: 'Check certificate trust, hostname matching, and TLS settings.',
|
||||
configuration: 'Review the enabled destination configuration and required fields.',
|
||||
rejected: 'Check the destination endpoint and payload requirements.',
|
||||
server_error: 'Check the destination service status and server logs. Retry retained deliveries once the service is available.',
|
||||
unknown: 'Review the local notification audit details for the terminal error.',
|
||||
};
|
||||
let diagnostic = 'Check each enabled destination and send a test.';
|
||||
@@ -329,6 +330,7 @@ export function getAlertDeliveryLogFailureClassLabel(failureClass: string) {
|
||||
tls: 'TLS failure',
|
||||
configuration: 'Configuration problem',
|
||||
rejected: 'Rejected by destination',
|
||||
server_error: 'Destination server error',
|
||||
unknown: 'Unclassified failure',
|
||||
};
|
||||
return labels[failureClass] ?? labels.unknown;
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// 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 { render } from 'solid-js/web';
|
||||
import { AlertDeliveryHealthCard } from '/src/features/alerts/AlertDeliveryHealthCard';
|
||||
import { AlertDeliveryLogCard } from '/src/features/alerts/AlertDeliveryLogCard';
|
||||
import '/src/index.css';
|
||||
const noop = () => {};
|
||||
render(() => <main class="p-4">
|
||||
<AlertDeliveryHealthCard health={{status:'degraded',failed:1,deadLetter:0,failureClasses7d:{server_error:1},failureClassesAvailable:true}} unavailable={false} refreshing={false} onRefresh={noop}/>
|
||||
<AlertDeliveryLogCard log={{entries:[{id:1,alertIds:['fixture-alert'],success:false,type:'webhook',outcome:'failed',failureClass:'server_error',timestamp:new Date().toISOString(),attempts:3}],completedRetentionDays:7,deadLetterRetentionDays:30}} unavailable={false} refreshing={false} onRefresh={noop} webhooks={[]}/>
|
||||
</main>, document.getElementById('root'));
|
||||
`;
|
||||
const server = await createServer({
|
||||
root,
|
||||
configFile: false,
|
||||
optimizeDeps: {
|
||||
noDiscovery: true,
|
||||
entries: [],
|
||||
esbuildOptions: { target: "esnext" },
|
||||
},
|
||||
esbuild: { target: "esnext" },
|
||||
plugins: [
|
||||
solid(),
|
||||
{
|
||||
name: "ordering-fixture",
|
||||
configureServer(s) {
|
||||
s.middlewares.use((req, res, next) => {
|
||||
if (req.url === "/qualification") {
|
||||
res.setHeader("Content-Type", "text/html");
|
||||
res.end(
|
||||
'<div id="root"></div><script type="module" src="/ordering-fixture.tsx"></script>',
|
||||
);
|
||||
} else next();
|
||||
});
|
||||
},
|
||||
resolveId(id) {
|
||||
if (id === "/ordering-fixture.tsx") return id;
|
||||
},
|
||||
load(id) {
|
||||
if (id === "/ordering-fixture.tsx") return fixture;
|
||||
},
|
||||
},
|
||||
],
|
||||
resolve: { alias: { "@": resolve(root, "src") } },
|
||||
server: { host: "127.0.0.1", port: 5197, strictPort: true },
|
||||
});
|
||||
let browser;
|
||||
try {
|
||||
await server.listen();
|
||||
browser = await chromium.launch({ headless: true });
|
||||
mkdirSync("/tmp/pulse-server-error-copy", { recursive: true });
|
||||
for (const width of [1440, 900, 390]) {
|
||||
const page = await browser.newPage({ viewport: { width, height: 900 } });
|
||||
const errors = [];
|
||||
page.on('pageerror', error => errors.push(error.message));
|
||||
await page.goto("http://127.0.0.1:5197/qualification");
|
||||
await page.getByText('Destination server error', {exact:true}).waitFor();
|
||||
assert.match(await page.getByRole('alert').innerText(), /Check the destination service status and server logs/);
|
||||
assert.equal(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth), true);
|
||||
assert.deepEqual(errors, []);
|
||||
await page.screenshot({path: `/tmp/pulse-server-error-copy/${width}.png`, fullPage:true});
|
||||
await page.close();
|
||||
}
|
||||
console.log(JSON.stringify({result:'passed',viewports:[1440,900,390],scope:'Real health/log cards, Chromium, scripted props; no installed delivery claim'}));
|
||||
|
||||
} finally {
|
||||
await browser?.close();
|
||||
await server.close();
|
||||
}
|
||||
Reference in New Issue
Block a user