mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 21:20:00 +00:00
Make guest-read safety explanations readable without hover (#2874)
## What changed and why Make the Workloads guest drawer's existing read-state explanations visible without hover, so phone and keyboard users can read what “Reported available” and cached metadata actually mean. Deferred incidents retain their prominent safety warning without duplicate text. This improves the existing guest-safety surface; it changes no collector, metric, permission or guest command. Related to #2439, #2619, #2762 and #2822. Independent thaw, covered-filesystem writes/liveness, restart recovery and installed acceptance remain separate; this change claims no report completion. ## Included source Complete reviewed publication range: `81abd9a1d980be2bc41623d0cd981463d5ffcf7e..9a9006121c81f511e7c8bc426f0d6e9634d7b954`. - Candidate `67104c830d39d2b798a45122d7df1dcad2844067`, integrated at `31aba74aac9f63a28ff9953ae657dcd0c32db7da`. - Existing upstream compositions `38a436c110a2a1949d2bc94d6e79148883cea205` and `827993c0f38781ace19bcf1a7a30e0c35aebf49f` preserve already-published maintenance and Patrol restoration. - Additive final composition `9a9006121c81f511e7c8bc426f0d6e9634d7b954` incorporates current published responsive alert editing at `81abd9a1d980be2bc41623d0cd981463d5ffcf7e`. The bounded refresh completed at that identity with source unchanged. Every accepted identity and ancestry remains intact. ## Validation and limits - Provenance, whitespace and complete-range browser coverage pass. The candidate's recorded rendered-text-only `Contract-Neutral` declaration passes the established completion guard. An initial diagnostic invocation omitted that declaration and reported the expected shape block; it was not a source defect or a new waiver. - Retained `source-70izx4xr` passes 288 source tests, types/lint, production/embed build and bundle budgets: client/guest zero, complete 83,475-byte output and guest stop, with its explicit reporting line-bound flag retained. All seven new visibility assertions fail against the original component. - Retained browser evidence records 22 desktop Chromium/phone WebKit checks on Playwright 1.56.1, four natively inspected captures and 164 loaded production input hashes. The final component hash matches the receipt; its production Workloads dependencies remain unchanged. No sufficient browser proof was replayed just for batching. Earlier failed browser attempts and loopback WebSocket refusal remain distinct from that passing synthetic result. - The coordinator's optional supplemental proof on `31aba74a` ended with client 143 and no observed result/cleanup; its existing Delivery readback request remains open. It is not a pass and was not replayed. - Exact final-source `source-lefs45kv` on `9a9006121c81f511e7c8bc426f0d6e9634d7b954` passes offline dependency installation, full types, the combined guest-drawer/model/presentation and responsive alert-editor boundary tests, production/embed build and every bundle budget. Client/guest exit zero, complete 61,816-byte output and completed guest stop are recorded; its reporting `line_bound_exceeded: true` is explicit, not lost raw output. This validates the upstream composition rather than repeating a browser qualification. These are source and synthetic UI checks, not native guest recovery or release qualification. Administrative review and all required exact-head hosted checks still govern landing; advisory failures require an evidenced disposition. No release or deployment was performed. [How Pulse handles triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": 1,
|
||||
"base_sha": "020a7be6bf2a2923c291395a8409ed79ddcde336",
|
||||
"verified_at": "2026-10-12T13:19:44.508Z",
|
||||
"result": "passed",
|
||||
"changed_paths": [
|
||||
"frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx"
|
||||
],
|
||||
"content_sha256": {
|
||||
"frontend-modern/src/components/Workloads/GuestDrawerOverview.tsx": "af9cd697e786db4ead5a3ea02b923c4d95bbfbd32c22457db11c9ee0a1f74371"
|
||||
},
|
||||
"routes": [
|
||||
"/proxmox (production GuestDrawer in a local synthetic read-state fixture)"
|
||||
],
|
||||
"viewports": [
|
||||
{
|
||||
"width": 1440,
|
||||
"height": 900
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"height": 844
|
||||
}
|
||||
],
|
||||
"states": [
|
||||
"Available, expected-unreachable, not-running, disabled, unrecognised and cached-version-only: original fixed explanations are visible without hover; unknown raw values are not rendered.",
|
||||
"Backup lock and timed-out read retain the original prominent safety warning/link without duplicate row text; available state returns on the same guest after clearance. Clearance is not native thaw/recovery.",
|
||||
"LXC and vSphere remain without a QEMU read-state row.",
|
||||
"22 source-bound desktop Chromium 141.0.7390.37 / phone WebKit 26.0 checks on Playwright 1.56.1; four final captures inspected with native view_image. Source imports: 164 verified loaded files.",
|
||||
"Explicit empty GET /api/resources and GET /api/alerts/active fixtures only; no API writes or external service. Loopback WebSocket proxy ECONNREFUSED remains in raw browser log; no streaming/native acceptance.",
|
||||
"Prior clients 1/1/1 retained: navigation readiness failures and the zero-read fixture oracle failure are not passes. Final client 0, browsers/contexts/server closed."
|
||||
],
|
||||
"interactions": [
|
||||
"Changed provider-owned state for the same canonical guest without remount, checking visible state/detail, geometry and precautions.",
|
||||
"Focused the production collapse button and pressed Enter, then reopened the fixture drawer and read the current explanation.",
|
||||
"Changed fixture platform to LXC/vSphere and confirmed no misleading QEMU explanation."
|
||||
],
|
||||
"evidence": "Lane outcome RECEIPTS.md links complete results/scripts/captures/native observations and retains adverse attempts; not a real guest, current-source backend or released proof."
|
||||
}
|
||||
@@ -79,6 +79,30 @@ export function GuestDrawerOverview(props: GuestDrawerOverviewProps) {
|
||||
ageClass: props.backupPresentation?.ageClass,
|
||||
});
|
||||
const guestReadPresentation = () => getGuestDrawerGuestReadPresentation(props.guest);
|
||||
const guestReadRow = () => {
|
||||
const read = guestReadPresentation();
|
||||
if (!read) return null;
|
||||
return makeDetailRow('Guest-agent reads', read.label, {
|
||||
title: read.detail,
|
||||
tone: read.tone,
|
||||
wrap: true,
|
||||
// A title alone is not available to touch users. Deferred reads already
|
||||
// have a visible safety notice above the drawer; do not repeat it here.
|
||||
layout: read.precaution ? undefined : 'stacked',
|
||||
valueContent: (
|
||||
<>
|
||||
<span class="block whitespace-normal wrap-break-word text-left leading-snug">
|
||||
{read.label}
|
||||
</span>
|
||||
<Show when={!read.precaution}>
|
||||
<span class="mt-1 block whitespace-normal text-left font-normal leading-snug text-muted wrap-break-word">
|
||||
{read.detail}
|
||||
</span>
|
||||
</Show>
|
||||
</>
|
||||
),
|
||||
});
|
||||
};
|
||||
const memoryReading = () => getGuestDrawerMemoryReading(props.guest);
|
||||
// A linked Pulse agent past its reporting lease leaves its last report on
|
||||
// the guest, so its RAID, network and filesystem rows read as last known.
|
||||
@@ -104,11 +128,7 @@ export function GuestDrawerOverview(props: GuestDrawerOverviewProps) {
|
||||
: undefined,
|
||||
wrap: true,
|
||||
}),
|
||||
makeDetailRow('Guest-agent reads', guestReadPresentation()?.label, {
|
||||
title: guestReadPresentation()?.detail,
|
||||
tone: guestReadPresentation()?.tone,
|
||||
wrap: true,
|
||||
}),
|
||||
guestReadRow(),
|
||||
makeDetailRow('Memory reading', memoryReading()?.summary, {
|
||||
wrap: true,
|
||||
}),
|
||||
|
||||
+34
@@ -63,6 +63,40 @@ const readRow = () => screen.getByText('Guest-agent reads').closest('tr')!;
|
||||
afterEach(cleanup);
|
||||
|
||||
describe('guest agent coverage requires current state, not cached metadata', () => {
|
||||
it.each([
|
||||
'available',
|
||||
'expected-unreachable',
|
||||
'not-running',
|
||||
'disabled',
|
||||
'future-state-private-detail',
|
||||
'',
|
||||
])('makes the %s explanation visible without hovering', (state) => {
|
||||
const value = guest({ guestAgentStatus: state, agentVersion: '9.2' });
|
||||
render(() => <GuestDrawerOverview {...overviewProps(value)} />);
|
||||
const presentation = getGuestDrawerGuestReadPresentation(value)!;
|
||||
expect(within(readRow()).getByText(presentation.detail)).toBeVisible();
|
||||
expect(readRow()).not.toHaveTextContent('future-state-private-detail');
|
||||
expect(readRow().querySelector('[tabindex]')).toBeNull();
|
||||
});
|
||||
|
||||
it('updates the visible explanation without repeating the deferred safety notice', () => {
|
||||
const [value, setValue] = createSignal(guest({ guestAgentStatus: 'available' }));
|
||||
render(() => <GuestDrawerOverview {...overviewProps(value())} />);
|
||||
const availableDetail = getGuestDrawerGuestReadPresentation(value())!.detail;
|
||||
expect(within(readRow()).getByText(availableDetail)).toBeVisible();
|
||||
setValue({ ...value(), lock: 'backup', diskStatusReason: 'prev-vm-locked' });
|
||||
expect(within(readRow()).getByText('Deferred')).toBeVisible();
|
||||
expect(within(readRow()).queryByText(availableDetail)).not.toBeInTheDocument();
|
||||
expect(
|
||||
within(readRow()).queryByText(getGuestDrawerGuestReadPresentation(value())!.detail),
|
||||
).not.toBeInTheDocument();
|
||||
setValue({ ...value(), lock: '', diskStatusReason: '', guestAgentStatus: 'not-running' });
|
||||
expect(
|
||||
within(readRow()).getByText(getGuestDrawerGuestReadPresentation(value())!.detail),
|
||||
).toBeVisible();
|
||||
expect(within(readRow()).queryByText(availableDetail)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
['deferred', 'Deferred'],
|
||||
['expected-unreachable', 'Unreachable'],
|
||||
|
||||
Reference in New Issue
Block a user