A design-partner review of real output called the PDFs out as thin, and
the page renders agreed: fleet reports spent one nearly blank A4 page
per resource, metric cards showed raw store keys with unformatted
values ('diskread 880000.00'), and value cells overlapped the adjacent
column for long readings.
- Fleet per-resource blocks now flow several to a page with separators,
breaking only when the next block will not fit (a 6-resource report
drops from 8 pages to 3); each block gains an availability line
- Rate metrics get display names and humane units (Disk Read 859.38
KiB/s instead of diskread 880000.00); byte units are self-describing
so the old +unit suffix no longer renders '12.00 GiBbytes'
- Metric card stat columns use bounded cells so long values cannot run
under the Avg/Samples labels
- Single reports merge Resource Details and Performance Summary onto a
shared page when they fit instead of stranding a third of a page of
content on each of two pages
Verified by regenerating branded fleet and single reports and reviewing
every rendered page.
Performance reports answered 'what were the averages' but never 'was my
infrastructure up' - the question a managed-service client reads a
monthly report for. Reports now carry an Availability summary derived
from the recorded resource change timeline (state_transition entries
keyed by the canonical unified ID):
- uptime percent over the observed portion of the window, outage count,
total downtime, and longest outage, rendered in the executive summary
with an explicit semantics note; fleet summaries gain a per-resource
Uptime column and CSV exports gain availability header lines
- absent/unknown spans are unobserved time: excluded from the uptime
math entirely and disclosed as coverage, never counted as downtime.
The journal records a registry absence for every monitor restart, so
treating gaps as outages would invent fleet-wide downtime every time
the operator restarts Pulse
- warning states count as up (the resource is reachable and serving);
the uptime label clamps rounding so any real downtime can never
display as a clean 100%
- resources with no timeline render no availability section at all
rather than a fabricated number
Verified live against a real 7-day window: uptime/outage/downtime
figures reconcile with the raw resource_changes journal.
Replace manual resource ID entry with a searchable, filterable resource
picker that uses live WebSocket state. Support selecting multiple
resources (up to 50) for combined fleet reports.
Multi-resource PDFs include a cover page, fleet summary table with
aggregate health status, and condensed per-resource detail pages with
overlaid CPU/memory charts. Multi-resource CSVs include a summary
section followed by interleaved time-series data with resource columns.
New POST /api/admin/reports/generate-multi endpoint handles multi-resource
requests while the existing single-resource GET endpoint remains unchanged.
Also fixes resource ID validation regex to allow colons used in
VM/container IDs (e.g., "instance:node:vmid").