diff --git a/docs/METRICS_HISTORY.md b/docs/METRICS_HISTORY.md index 1555f2b2fb..c64478b480 100644 --- a/docs/METRICS_HISTORY.md +++ b/docs/METRICS_HISTORY.md @@ -4,6 +4,19 @@ Pulse persists metrics history to disk so trend views and sparklines survive res ## Read a History panel +In current drawers, open the affected resource's **Overview** and use **History +range** beside its charts. Proxmox guests and nodes, Docker hosts and resource +drawers (including PBS) show stored charts there instead of a separate History +tab. If your installed version still has a **History** tab, use that tab; do not +upgrade or restart just to follow this guide. A resource without a supported +history target may have no chart. + +These charts are separate from **Alerts → History**, table sparklines and +current summary cards. A current card or a short trend preview does not +establish the observations in the selected stored range. For a PBS host, use +the [Backup Server chart](PBS.md#pbs-is-connected-but-history-stays-empty), +not its datastore or the VM that happens to host it. + Start with the existing chart, not a restart or a storage change. A current reading does not prove that it was stored, and a selected time range does not prove continuous coverage. **Window start** and **Window end** bound the @@ -20,12 +33,21 @@ than borrowing one from the current overview. | **current**, **last known** or **freshness unknown** with no stored history | A separately qualified fallback reading. Keep its label and explanation. | Not proof that the value was persisted or belongs to a past incident. | | **Single observation. No trend yet.** | One stored time in that panel. | Not a flat trend or sustained recovery. | | **Missing observations** | Some series lack readings at other stored times in that panel; lines stop across those gaps. | Not the cause or duration of a monitoring outage. Missing is not measured zero. | +| **Shaded low–high band** | The recorded minimum and maximum within an aggregated bucket, when supplied; the line shows its average. | Not continuous coverage, every original sample, or the duration of a spike. | +| **peak** while inspecting | That metric's maximum in the inspected bucket, when the panel shows it beside the average. | Not necessarily the highest value in the whole selected range or the value at an alert's exact time. | For example, CPU **22% at 12:02** and memory **95% at 12:01** are not one observation. At **Latest stored · 12:02**, memory is a dash if there is no memory point at 12:02; the earlier 95% remains inspectable. Network and disk I/O panels have their own times, not a shared snapshot of the guest. +An aggregated CPU bucket can show **22% average, 10% to 95%**. The line at 22% +does not rule out the recorded 95% peak, and the band does not say how long +that peak lasted. No visible band may mean a single reading, equal bounds or +unavailable bounds; it does not prove that usage stayed constant. Older panels +may not show these bounds. Keep that limitation rather than inferring a peak +from the line alone. + 1. Keep the same resource, metric and range when comparing existing readings. **Inspect history**, when offered, steps through stored times: focus the slider and use the arrow keys, **Home** or **End**, or move it by touch. diff --git a/docs/PBS.md b/docs/PBS.md index a426c45575..0539fc2e2e 100644 --- a/docs/PBS.md +++ b/docs/PBS.md @@ -429,12 +429,19 @@ the pause. Monitoring and alerts are unavailable while Pulse is stopped. ### PBS is connected but History stays empty -Open **Proxmox → Backups → Backup Server → History**, not a datastore or the -PBS virtual machine's row in **Overview**. Record which route fails. A working -VM chart can use a different metrics target from the PBS host chart; it does -not establish that the PBS chart can read the same series. Current CPU and -memory values, a green connection badge, and an **API+Agent** label also do not -prove that stored host history is available. +Open **Proxmox → Backups**, choose the **Backup Server** row, then use the +drawer's **Overview** charts and **History range** selector. If your installed +version still has a separate **History** tab, use that tab instead; no upgrade +or restart is needed just to find the chart. This is not a datastore chart, +**Alerts → History**, or the PBS virtual machine's row in Proxmox **Overview**. +Record which route fails. A working VM chart can use a different metrics target +from the PBS host chart; it does not establish that the PBS chart can read the +same series. Current CPU and memory values, a green connection badge, and an +**API+Agent** label also do not prove that stored host history is available. + +Use the chart's selected stored range, not a current summary card or table +sparkline. For observation times, missing samples and aggregated low–high bands, +see [reading a History panel](METRICS_HISTORY.md#read-a-history-panel). Start with one affected PBS and a recent, unlocked time range: @@ -447,7 +454,8 @@ Start with one affected PBS and a recent, unlocked time range: 2. If the target is already known, inspect the chart's request instead of collecting the identities again. In your signed-in browser, open developer tools → **Network**, filter for `metrics-store/history`, then open the - affected **History** tab. Do not refresh or restart the server. Inspect one + affected drawer's **Overview** charts (or **History** tab on older versions). + Do not refresh or restart the server. Inspect one completed request for the drawer's `resourceType`, `resourceId` and `range`. The drawer normally fetches all chart metrics together: a request **without `metric`** is expected, so do not wait for a separate `metric=cpu` request. diff --git a/frontend-modern/browser-verification/2026-10-14-history-guide.json b/frontend-modern/browser-verification/2026-10-14-history-guide.json new file mode 100644 index 0000000000..004f2d69c2 --- /dev/null +++ b/frontend-modern/browser-verification/2026-10-14-history-guide.json @@ -0,0 +1,68 @@ +{ + "version": 1, + "result": "passed", + "base_sha": "c670f6b6666e6f91241e6ba2e20f77b768c3bc1e", + "changed_paths": [ + "frontend-modern/public/docs/PBS.md", + "frontend-modern/public/docs/METRICS_HISTORY.md" + ], + "content_sha256": { + "frontend-modern/public/docs/PBS.md": "a4e7aa00bf32f8269a20f8f1c3609214d8dadb1da79a3ed0e7b4b980789741cf", + "frontend-modern/public/docs/METRICS_HISTORY.md": "24d00753afb3bc8a15f079958d6fcb964cecfbfcf14f2f9e42e8916be5fd8250" + }, + "routes": [ + "/docs/PBS#pbs-is-connected-but-history-stays-empty", + "/docs/METRICS_HISTORY#read-a-history-panel" + ], + "states": [ + "Actual canonical/shipped Markdown through production Docs and sanitised renderer in a Vite source preview; only local security/API GET fixtures.", + "PBS host Overview chart/range versus old installed History tab, VM/datastore and Alerts History.", + "Stored observation/window semantics and aggregated averages/minimum/maximum/local peaks without continuous-coverage or incident-time claims." + ], + "interactions": [ + "Desktop keyboard Enter and390px WebKit touch follow both cross-guide links to existing bookmarks.", + "Scroll to bounds/peak rows and worked example in the real article. Actual served Markdown hashes match their committed producers." + ], + "viewports": [ + { + "width": 1440, + "height": 1000 + }, + { + "width": 390, + "height": 844 + } + ], + "verified_at": "2026-10-14T00:23:19.647Z", + "runtime": { + "playwright": "1.56.1", + "chromium": "141.0.7390.37", + "webkit": "26.0" + }, + "pixel_review": { + "tool": "native view_image", + "opened": [ + "chromium-pbs-navigation.jpg", + "chromium-history-navigation.jpg", + "chromium-bucket-bounds.jpg", + "chromium-peak-example.jpg", + "webkit-pbs-navigation.jpg", + "webkit-history-navigation.jpg", + "webkit-bucket-bounds.jpg", + "webkit-peak-example.jpg" + ], + "observations": [ + "Desktop: revised PBS host route, old-tab compatibility, Overview/range guidance and both cross-guide links are readable; the complete bounds/peak table fits the page.", + "390 px WebKit: new navigation and no-upgrade/no-restart caveat wrap within the page. All three columns of the bounds/peak rows remain readable without page overflow, and the22% average/95% peak example and its duration/constant-usage limits are visible.", + "Desktop bucket-bounds capture is byte-identical to the already presented history-navigation image; that same presented pixel content covers both cases.", + "Keyboard Enter and phone touch followed both preserved bookmarks in both directions. No API, guest, backup, credential or installed monitoring operation was performed." + ], + "remaining": null + }, + "browser_cleanup": { + "closed": true, + "preview_exit": 143, + "preview_signal": null + }, + "evidence": "PI OUTCOME.md/RECEIPTS.md/BROWSER.json and eight retained native-inspected captures bind ten consuming cases to c670f6b6. Does not establish native guest/PBS recovery, installed acceptance, protected delivery or release availability." +} diff --git a/frontend-modern/public/docs/METRICS_HISTORY.md b/frontend-modern/public/docs/METRICS_HISTORY.md index 1555f2b2fb..c64478b480 100644 --- a/frontend-modern/public/docs/METRICS_HISTORY.md +++ b/frontend-modern/public/docs/METRICS_HISTORY.md @@ -4,6 +4,19 @@ Pulse persists metrics history to disk so trend views and sparklines survive res ## Read a History panel +In current drawers, open the affected resource's **Overview** and use **History +range** beside its charts. Proxmox guests and nodes, Docker hosts and resource +drawers (including PBS) show stored charts there instead of a separate History +tab. If your installed version still has a **History** tab, use that tab; do not +upgrade or restart just to follow this guide. A resource without a supported +history target may have no chart. + +These charts are separate from **Alerts → History**, table sparklines and +current summary cards. A current card or a short trend preview does not +establish the observations in the selected stored range. For a PBS host, use +the [Backup Server chart](PBS.md#pbs-is-connected-but-history-stays-empty), +not its datastore or the VM that happens to host it. + Start with the existing chart, not a restart or a storage change. A current reading does not prove that it was stored, and a selected time range does not prove continuous coverage. **Window start** and **Window end** bound the @@ -20,12 +33,21 @@ than borrowing one from the current overview. | **current**, **last known** or **freshness unknown** with no stored history | A separately qualified fallback reading. Keep its label and explanation. | Not proof that the value was persisted or belongs to a past incident. | | **Single observation. No trend yet.** | One stored time in that panel. | Not a flat trend or sustained recovery. | | **Missing observations** | Some series lack readings at other stored times in that panel; lines stop across those gaps. | Not the cause or duration of a monitoring outage. Missing is not measured zero. | +| **Shaded low–high band** | The recorded minimum and maximum within an aggregated bucket, when supplied; the line shows its average. | Not continuous coverage, every original sample, or the duration of a spike. | +| **peak** while inspecting | That metric's maximum in the inspected bucket, when the panel shows it beside the average. | Not necessarily the highest value in the whole selected range or the value at an alert's exact time. | For example, CPU **22% at 12:02** and memory **95% at 12:01** are not one observation. At **Latest stored · 12:02**, memory is a dash if there is no memory point at 12:02; the earlier 95% remains inspectable. Network and disk I/O panels have their own times, not a shared snapshot of the guest. +An aggregated CPU bucket can show **22% average, 10% to 95%**. The line at 22% +does not rule out the recorded 95% peak, and the band does not say how long +that peak lasted. No visible band may mean a single reading, equal bounds or +unavailable bounds; it does not prove that usage stayed constant. Older panels +may not show these bounds. Keep that limitation rather than inferring a peak +from the line alone. + 1. Keep the same resource, metric and range when comparing existing readings. **Inspect history**, when offered, steps through stored times: focus the slider and use the arrow keys, **Home** or **End**, or move it by touch. diff --git a/frontend-modern/public/docs/PBS.md b/frontend-modern/public/docs/PBS.md index a426c45575..0539fc2e2e 100644 --- a/frontend-modern/public/docs/PBS.md +++ b/frontend-modern/public/docs/PBS.md @@ -429,12 +429,19 @@ the pause. Monitoring and alerts are unavailable while Pulse is stopped. ### PBS is connected but History stays empty -Open **Proxmox → Backups → Backup Server → History**, not a datastore or the -PBS virtual machine's row in **Overview**. Record which route fails. A working -VM chart can use a different metrics target from the PBS host chart; it does -not establish that the PBS chart can read the same series. Current CPU and -memory values, a green connection badge, and an **API+Agent** label also do not -prove that stored host history is available. +Open **Proxmox → Backups**, choose the **Backup Server** row, then use the +drawer's **Overview** charts and **History range** selector. If your installed +version still has a separate **History** tab, use that tab instead; no upgrade +or restart is needed just to find the chart. This is not a datastore chart, +**Alerts → History**, or the PBS virtual machine's row in Proxmox **Overview**. +Record which route fails. A working VM chart can use a different metrics target +from the PBS host chart; it does not establish that the PBS chart can read the +same series. Current CPU and memory values, a green connection badge, and an +**API+Agent** label also do not prove that stored host history is available. + +Use the chart's selected stored range, not a current summary card or table +sparkline. For observation times, missing samples and aggregated low–high bands, +see [reading a History panel](METRICS_HISTORY.md#read-a-history-panel). Start with one affected PBS and a recent, unlocked time range: @@ -447,7 +454,8 @@ Start with one affected PBS and a recent, unlocked time range: 2. If the target is already known, inspect the chart's request instead of collecting the identities again. In your signed-in browser, open developer tools → **Network**, filter for `metrics-store/history`, then open the - affected **History** tab. Do not refresh or restart the server. Inspect one + affected drawer's **Overview** charts (or **History** tab on older versions). + Do not refresh or restart the server. Inspect one completed request for the drawer's `resourceType`, `resourceId` and `range`. The drawer normally fetches all chart metrics together: a request **without `metric`** is expected, so do not wait for a separate `metric=cpu` request. diff --git a/frontend-modern/src/features/docs/__tests__/drawerHistoryGuide.test.ts b/frontend-modern/src/features/docs/__tests__/drawerHistoryGuide.test.ts new file mode 100644 index 0000000000..019a029df4 --- /dev/null +++ b/frontend-modern/src/features/docs/__tests__/drawerHistoryGuide.test.ts @@ -0,0 +1,107 @@ +import { readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +import { createGuideFixture } from './guideFixture'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../..'); +const read = (file: string) => readFileSync(path.join(root, file), 'utf8'); +const shipped = (name: string) => read(`frontend-modern/public/docs/${name}.md`); +const render = createGuideFixture(shipped); +const prose = (element: Element) => (element.textContent ?? '').replace(/\s+/g, ' ').trim(); + +function section(name: string, fragment: string) { + const heading = render(name).querySelector(`#${fragment}`); + expect(heading).not.toBeNull(); + const element = document.createElement('section'); + for (let next = heading!.nextElementSibling; next; next = next.nextElementSibling) { + if (next.matches('h1, h2, h3')) break; + element.appendChild(next.cloneNode(true)); + } + return element; +} + +const pbs = () => section('PBS', 'pbs-is-connected-but-history-stays-empty'); +const history = () => section('METRICS_HISTORY', 'read-a-history-panel'); + +describe('stored-chart navigation and interpretation help', () => { + it.each(['PBS', 'METRICS_HISTORY'])('ships the exact canonical %s guide', (name) => { + expect(shipped(name)).toBe(read(`docs/${name}.md`)); + }); + + it('routes PBS to its host Overview, not its datastore, VM or alert history', () => { + const text = prose(pbs()); + expect(text).toContain('Proxmox → Backups, choose the Backup Server row'); + expect(text).toContain("drawer's Overview charts and History range selector"); + expect(text).toContain('not a datastore chart, Alerts → History'); + expect(text).toContain("PBS virtual machine's row in Proxmox Overview"); + expect(text).not.toContain('Backup Server → History'); + expect(text).toContain('resourceType, resourceId and range'); + expect(text).toContain('a request without metric is expected'); + expect(text).toContain('metrics.cpu'); + expect(text).toContain('points array for that single metric'); + }); + + it('supports older installed tabs without making an upgrade part of diagnosis', () => { + expect(prose(pbs())).toContain('If your installed version still has a separate History tab'); + expect(prose(pbs())).toContain('no upgrade or restart is needed just to find the chart'); + expect(prose(history())).toContain('If your installed version still has a History tab'); + expect(prose(history())).toContain('do not upgrade or restart just to follow this guide'); + expect(prose(history())).toContain( + 'A resource without a supported history target may have no chart', + ); + }); + + it('binds the new instructions to the current consuming drawer and control', () => { + const overview = read( + 'frontend-modern/src/components/Infrastructure/ResourceDetailDrawerOverviewTab.tsx', + ); + expect(overview).toContain(' { + const text = prose(history()); + expect(text).toContain('22% average, 10% to 95%'); + expect(text).toContain('does not rule out the recorded 95% peak'); + expect(text).toContain('does not say how long that peak lasted'); + expect(text).toContain('does not prove that usage stayed constant'); + const rows = [...history().querySelectorAll('tbody tr')]; + const band = rows.find((row) => prose(row).startsWith('Shaded low–high band'))!; + const peak = rows.find((row) => prose(row).startsWith('peak while inspecting'))!; + expect(prose(band)).toContain('Not continuous coverage, every original sample'); + expect(prose(peak)).toContain('Not necessarily the highest value'); + expect(prose(peak)).toContain("alert's exact time"); + expect(text).toContain('Missing is not measured zero'); + expect(text).toContain('Window start and Window end bound the requested interval'); + expect(text).toContain('not the first and last successful observations'); + }); + + it('keeps both existing bookmarks and the new cross-guide links usable', () => { + for (const [element, name, fragment] of [ + [pbs(), 'METRICS_HISTORY', 'read-a-history-panel'], + [history(), 'PBS', 'pbs-is-connected-but-history-stays-empty'], + ] as const) { + expect(element.querySelector(`a[href="/docs/${name}#${fragment}"]`)).not.toBeNull(); + expect(render(name).querySelector(`#${fragment}`)).not.toBeNull(); + } + }); + + it('retains the safe no-replay and private-evidence investigation path', () => { + expect(prose(pbs())).toContain('Do not edit or replay the request'); + expect(prose(pbs())).toContain('Do not refresh or restart the server'); + expect(prose(pbs())).toContain('Do not share a HAR export'); + expect(prose(pbs())).toContain('Do not delete and re-add connections'); + expect(prose(history())).toContain('do not run Diagnostics or guest-agent probes'); + expect(prose(history())).toContain('not chart inspection'); + expect( + history().querySelector('a[href="/docs/VM_DISK_MONITORING#backup-safety"]'), + ).not.toBeNull(); + }); +});