Commit Graph

6398 Commits

Author SHA1 Message Date
rcourtman 69263ff68b Bind alert schedule form labels 2026-05-20 14:35:14 +01:00
rcourtman bc5275b8d8 Color disk tooltip dots by utilization in vertical-bars mode 2026-05-20 14:30:16 +01:00
rcourtman d02b9ef7ff Bind alert notification form labels 2026-05-20 14:28:35 +01:00
rcourtman b8d23ae616 Name labeled toggle controls 2026-05-20 14:13:38 +01:00
rcourtman f83d25d155 Add Discovery provenance markers 2026-05-20 13:32:34 +01:00
rcourtman ba896a5366 Normalize PMG drawer tables 2026-05-20 13:25:35 +01:00
rcourtman 9c55c341e2 Implement Discovery observed context UX 2026-05-20 13:21:55 +01:00
rcourtman 74ec866498 Normalize Kubernetes drawer tables 2026-05-20 13:20:21 +01:00
rcourtman daa155c2d2 Normalize Docker swarm drawer table 2026-05-20 13:02:49 +01:00
rcourtman 4332129ac2 Fix Docker host header clipping 2026-05-20 12:52:57 +01:00
rcourtman 6ac02408c0 Normalize Proxmox drawer tables 2026-05-20 12:48:36 +01:00
rcourtman 435cf816fb Harden Discovery command-scan gating 2026-05-20 12:40:04 +01:00
rcourtman 18e62d3ed6 Normalize Proxmox backup tables 2026-05-20 12:37:55 +01:00
rcourtman 92b3116047 Use shared metric bars for Kubernetes clusters 2026-05-20 12:19:11 +01:00
rcourtman 300af43128 Move Uptime after the bar block in platform top tables
Uptime placement was the most visibly drifty ordering on platform top
tables: Proxmox and TrueNAS sat it at position 3 (before any bars),
Docker / vSphere at position 8 (after bars), Kubernetes Nodes at the
very end (after Capacity). Same monitoring concept rendered in three
different positions depending on the table.

Lock in the convention: Uptime is monitoring state, not identity. It
canonically sits between the bar block (CPU / Memory / Disk / Storage,
plus the Temp diagnostic) and the inventory counts.

Apply that rule:
  - Proxmox host model: Uptime moves from position 3 to position 7
    (Node, Version, CPU, Memory, Disk, Temp, Uptime, VMs, CTs, Cluster).
  - TrueNAS systems table: Uptime moves from position 3 to position 7
    (System, Version, CPU, Memory, Storage, Temp, Uptime, Pools,
    Datasets, Disks, Apps).
  - Kubernetes nodes table: Uptime swaps with Capacity so Uptime sits
    between the bars and the Capacity inventory string (Node, Cluster,
    Roles, Kubelet, Runtime, CPU, Memory, Uptime, Capacity).

The columnAlignment.ts docstring now documents the recommended column
ordering (identity → context → state → bars → diagnostic → time →
inventory → external) alongside the kind-to-align mapping, so the
ordering convention sits in the same source-of-truth file every agent
already reads when editing a platform table.

Other ordering questions (where integer counts sit relative to bars;
where the cluster-reference column sits) are left as each table's
current choice. They are defensible either way and have a real visible
UX cost to change. Revisit if drift surfaces.
2026-05-20 12:16:37 +01:00
rcourtman b94c6164d7 Normalize platform loading states 2026-05-20 12:13:54 +01:00
rcourtman 9f031ade1d Normalize Proxmox Ceph table consistency 2026-05-20 12:07:19 +01:00
rcourtman 29fc340d6a Polish Proxmox table and settings consistency 2026-05-20 11:59:02 +01:00
rcourtman 111af289ab Fix platform UI consistency regressions 2026-05-20 11:46:30 +01:00
rcourtman ecd44945bc Enforce canonical platform table alignment in pre-push audit
Adds canonical-platform-table/no-legacy-align-helper to
canonical-platform-audit.mjs: any platform table file that calls the
legacy `getPlatformTableHeadClass(<align>)` or
`getPlatformTableCellClass(<align>)` helper now fails the audit (which
runs in the pre-push hook). Authors are pointed at the kind-based
wrappers and the canonical column-kind file.

Two intentional allowlist entries:

  - sharedPlatformPage.tsx: the kind-based wrappers internally delegate
    to the align-based helpers; that's the one legitimate call site.

  - KubernetesClustersTable.tsx: another agent has this file
    mid-staged in their working tree. Removing this entry is part of
    that file's migration commit (drop the entry whenever the file
    moves to the *ForKind wrappers).

To support per-rule exemptions cleanly, the audit's inner loop now
honours an optional `allowFiles: Set<string>` on each HELPER_RULE
entry. Existing rules are unchanged; only the new rule uses it.

The platformOverviewLayout.guardrails.test.ts assertions are updated
to match the kind-based forms (regex match on the new
getPlatformTableHeadClassForKind('<kind>') ... <label> shape) for the
migrated files. The two Kubernetes Clusters assertions continue to
match the legacy form until that file migrates.

Workspace-level CLAUDE.md and AGENTS.md (at /Volumes/Development/pulse/,
outside this git repo) also got a one-paragraph entry pointing at the
canonical source-of-truth file and the audit rule, so every agent
session that reads those bootstrap files at startup picks up the
convention.
2026-05-20 11:18:29 +01:00
rcourtman 6d2faf66bf Apply canonical column alignment to shared workloads table
The shared WorkloadTableHeader (the bottom guest-list table on every
platform overview page) previously center-aligned every non-name,
non-image column with a hardcoded ternary. Under the canonical
established in the previous commit, that's wrong for several columns:
Uptime / Net I/O / Disk I/O are numeric-value columns that should
right-align (so units and digits line up for column scanning), and
Runtime / IP / Node / Image / Namespace / Context are text columns
that should left-align (for reading).

Approach: add an optional `kind?: PlatformTableColumnKind` field to
the shared ColumnDef in useColumnVisibility.ts, tag every column in
GUEST_COLUMNS with the right kind, and have WorkloadTableHeader read
col.kind via `getPlatformColumnAlign()`. The first column is always
left-aligned regardless of kind (since it's the primary identifier
for the row). Unknown / unset kinds fall back to text → left.

The kind tags map straightforwardly: name → 'name', metric bars
(cpu/memory/disk) → 'metric-bar', integer/scalar columns
(uptime/info/vmid/netIo/diskIo) → 'numeric-value', text strings
(runtime/ip/node/image/namespace/context) → 'text', visual pills /
icons (type/backup/tags/os/update) → 'badge'. The canonical helper
maps those to text-left, text-center, text-right consistently across
every platform page.

Visible result: on every platform page, CPU/Memory/Disk headers now
center over their bars in both the top and bottom tables, integer
counts and time/I/O values right-align in both, and text columns
left-align in both. The "complete hobbledy codge" of the same column
showing left in one table and center in another is gone.
2026-05-20 11:11:52 +01:00
rcourtman 348b731408 Establish canonical platform table column alignment
Until now, the same column (CPU/Memory/Disk) was aligned differently
depending on which table it appeared in: Proxmox top tables defaulted
to left, Docker/TrueNAS/vSphere/Kubernetes top tables used right, the
shared workloads bottom table used center, and integer-count columns
(VMs/CTs/Containers/Datastores/Desired/etc.) were a mix of right and
center. The user's read: "complete hobbledy codge."

Codify the rule once and for all. The new
frontend-modern/src/features/platformPage/columnAlignment.ts defines
five canonical column kinds with a documented align mapping and the
rationale for each:

  - 'name'           left   primary identifier
  - 'text'           left   readable strings
  - 'metric-bar'     center bar fills cell, label rides at fill edge
  - 'numeric-value'  right  units/digits line up for column scanning
  - 'badge'          center pill/icon-only cells

sharedPlatformPage exposes getPlatformTableHeadClassForKind() and
getPlatformTableCellClassForKind() wrappers; every platform top table
plus the Docker services table now consume these instead of passing
literal align strings. The Proxmox host column model carries a kind
field instead of a raw align so the same source of truth drives both
the model file and the JSX.

KubernetesClustersTable.tsx is intentionally not migrated in this
commit: another agent has it staged in their working tree. The same
migration applies once that lands; left for a follow-up.

The WorkloadTableHeader (shared bottom guest-list table) is also
unmigrated here. It center-aligns everything today, and changing it to
canonical means flipping Uptime / Net I/O / Disk I/O from center to
right across every guest list on every platform page — bigger visual
blast radius, so it gets its own scoped commit next.

A canonical-platform-audit rule and a CLAUDE.md/AGENTS.md note will
follow in a third commit, so any future agent that writes a
non-canonical align fails the pre-push hook rather than discovers the
drift visually weeks later.
2026-05-20 11:06:32 +01:00
rcourtman 881e1ffda6 Drop "+ Filter" menu across remaining workload surfaces
Kubernetes, vSphere, TrueNAS, and Proxmox all rendered the same
single-axis "+ Filter" combobox the Docker overview just lost, leaving
the page surfaces visually inconsistent. The unified workload search
(workloadSearchCandidates in workloadSelectors.ts:52-70) already covers
node, namespace, contextLabel, clusterName, platformType and
containerRuntime — i.e. every axis the dropdown carried on these pages
— and each surface's search placeholder advertises that coverage
("Search pods by name, namespace, image, cluster, or node",
"Search VMs and LXCs by name, VMID, node, or status", etc.). Suppress
hostFilter and namespaceFilter on all four surfaces so the workload
toolbar is consistently chip-only: Status, Runtime (where applicable),
Group by, Metric display.
2026-05-20 10:49:01 +01:00
rcourtman 037919abed Rebalance Kubernetes nodes and deployments table column widths
Same equal-width problem on /kubernetes/nodes: the Nodes table (9
columns) and the Deployments table (7 columns) both ran under
table-fixed with no per-column desktop widths, so longer values like
runtime "containerd://1.7.20" and capacity "6 cores / 51.0 GB"
truncated, while the four integer-count Deployments columns (Desired /
Updated / Ready / Available) each held 183px for a single-digit value.

Set explicit md:w-[...] on every TableHead so the longer-content
columns (Node, Runtime, Capacity, Deployment, Namespace) get the room
they need, the CPU and Memory bars get a balanced share each, and the
short text and integer-count columns trim to fit their headers and
values. Mobile widths are unchanged.

Leaving KubernetesClustersTable.tsx alone for now: another agent has
it mid-edit. Same fix pattern can be applied to it once their work
lands.
2026-05-20 10:42:12 +01:00
rcourtman e20270f5a0 Drop Node "+ Filter" menu from Docker workload toolbar
After promoting Runtime to a chip toggle, the "+ Filter" combobox
carried only the Node scope. The Docker workload search box already
matches against host identity candidates (docker.hostname,
agent.hostname, identity.hostname, canonicalIdentity.hostname and
aliases — see filterDockerHosts), so narrowing by host name is
already a search keystroke away. Same-named hosts can be disambiguated
by typing the host's id suffix. Dropping hostFilter from the Docker
page surface only, since other workload surfaces (notably Proxmox
where many nodes are not always visible in a top table) have different
ergonomics and should be evaluated separately.
2026-05-20 10:41:15 +01:00
rcourtman b2c6872b61 Rebalance Docker swarm services table column widths
Same equal-width problem as the platform overview tops: this table sat
on table-fixed with no per-column desktop widths, so all 7 columns
rendered at the same ~183px. Image references like
"registry.example.com/backend-deluge:2025-..." got truncated while the
1-digit Desired and Running columns held the same allocation.

Set explicit md:w-[...] on every TableHead. Service and Image take the
biggest shares (22% and 25%) for their long content, Mode / Desired /
Running trim to short text and integers, and Ports / Host get a middle
slice. Image now renders its full registry path at 1440px instead of
truncating. Mobile widths are unchanged.
2026-05-20 10:38:46 +01:00
rcourtman e9ebf3ddea Style runtime chips to match Status row and runtime badges
The promoted Runtime chip group rendered with a verbose "All runtimes"
label and lowercase, dot-less docker/podman pills, breaking the visual
rhythm of the Status / Group by / Metric display chips it now sits
alongside. Map the runtime options to the same style as Status: concise
"All" for the empty value, capitalized "Docker" / "Podman" labels, and
leading dots in the canonical runtime badge colors (sky for Docker,
violet for Podman) so the chip palette echoes the runtime badges
rendered in the table rows.
2026-05-20 10:37:18 +01:00
rcourtman 96fb624789 Promote workload runtime filter to inline chip toggle
The Runtime axis was hidden inside the "+ Filter" combobox under a
"Properties" optgroup with at most two real choices (docker, podman),
sitting next to Status / Group by / Metric display which are already
chip toggles. Render it inline alongside Status so the binary axis is
one click instead of two, and hide the filter entirely when fewer than
two distinct runtimes are present so single-runtime deployments are not
shown a degenerate All/<one-runtime> control. The leftover "+ Filter"
select now carries only the Node scope.
2026-05-20 10:33:17 +01:00
rcourtman d141819795 Rebalance vSphere hosts top-table column widths
Same equal-width issue as the other platform overview top tables: 9
columns at ~147px each under table-fixed meant FQDN-style host names
(esxi-01.lab.local) and longer cluster names ("Production Cluster") sat
on the same width as the Datastores and VMs integer-count columns, and
the CPU / Memory bars couldn't show their inline "% (used / total)"
labels.

Set explicit md:w-[...] on every TableHead so the Host column gets 18%
(comfortable for FQDNs), CPU and Memory share 13% each as parallel bar
metrics, Cluster / Datacenter / Power / vCenter take what their text
content needs, and Datastores / VMs trim to fit their headers plus
1-2 digit counts. Memory now shows the full size info ("71% (57.2 GB/
80.0 GB)") at 1440px instead of just the percentage. Mobile widths are
unchanged.
2026-05-20 10:29:14 +01:00
rcourtman 5d3c0a72f7 Rebalance TrueNAS systems top-table column widths
Same equal-width issue as the Docker and Proxmox top tables: table-fixed
across 11 columns with no per-column desktop widths gave every column the
same ~120px slice. That squeezed the Version field (where
"TrueNAS-SCALE-24.10.2" is a normal 21-char value) and clipped the size
labels on the CPU / Memory / Storage bars, while the four integer-count
columns (Pools / Datasets / Disks / Apps) sat on 120px each holding
single-digit values.

Set explicit md:w-[...] on every TableHead so the three bar columns get
balanced shares (CPU 11%, Memory 12%, Storage 13%), Version and System
each get 14% for their longer mono-spaced and hostname content, and the
four count columns trim to what their headers need (Pools/Disks/Apps
~5-6%, Datasets 7% for the longer header). Memory and Storage bars now
show their full "% (used / total)" labels at 1440px instead of hiding
the size portion. Mobile widths are unchanged.
2026-05-20 10:27:42 +01:00
rcourtman 2e8bcad909 Gate heavy pre-commit governance checks on staged paths
The husky pre-commit hook unconditionally ran the full release-control
governance audit chain (`governance_stage_guard.py`, `control_plane_audit.py`,
`status_audit.py`, `registry_audit.py`, `contract_audit.py`, the
`go test ./internal/repoctl` package test, the readiness assertion guard,
and 15 Python helper unit tests) on every single commit, regardless of
what was actually staged. For a one-file frontend TS change, that meant
~15-20 minutes of audits the change couldn't possibly affect.

That cost is amplified by Pulse being driven by multiple agent harnesses
in parallel (Codex lanes, Claude Code interactive sessions, Anti-Gravity).
Each agent's commit serialized behind the same unrelated suite, even
when none of their staged files touched governance.

The hook already had `staged_files_match` and was using it to gate the
golangci-lint and `npm run lint` blocks. Apply the same pattern to the
governance block: run it only when the staged set actually touches
governance-relevant paths (docs/release-control/, scripts/release_control/,
internal/repoctl/, any .go file, go.{mod,sum,work,work.sum}, VERSION,
or the husky hooks themselves so hook changes still validate).

Frontend-only commits now finish in roughly the time the sensitivity
check, gitleaks scan, and `npm run lint` need (tens of seconds), rather
than tens of minutes. Governance-touching commits are unaffected; the
full audit chain still runs whenever any path in the gating regex is
staged.

Smoke-tested the regex against 13 representative paths (frontend TS,
README, internal docs JSON, release_control py, repoctl go, generic go,
go.mod/go.sum, VERSION, .husky/pre-commit, .husky/pre-push). All
classifications matched the intended skip/run behavior. Bash syntax of
the modified hook validated with `bash -n`.
2026-05-20 10:13:04 +01:00
rcourtman 274bb133c0 Clean up multi-mount disk cell labels 2026-05-20 10:12:33 +01:00
rcourtman a3ba8a1663 Rebalance Docker hosts top-table column widths
The Docker overview top table was running on table-fixed with no
per-column desktop widths, so all 10 columns rendered at the same ~132px.
That gave CPU / Memory / Disk bars the same allocation as one-digit
badge columns like "Containers" or short-text columns like "System",
and the bars couldn't show their inline "% (size / size)" labels at all.

Set explicit md:w-[...] on every header so the three bar-metric columns
share an equal generous slice (14% each), the Host name column gets
headroom for full hostnames, and the short-content columns (Version,
System, Uptime, Temp, Containers, Swarm role) trim to what their labels
and values actually need. Mobile widths are unchanged.
2026-05-20 09:59:22 +01:00
rcourtman 46f2f459c1 Rebalance Proxmox hosts top-table column widths
Disk shared half the weight of CPU and memory while rendering the same
kind of usage bar, so the disk meter was squeezed and the over-allocated
version/uptime/temp/cluster columns wasted space the node column needed
for longer cluster-style names. Equalize CPU/memory/disk to the workloads
table convention (140px each), give node headroom, and trim the
short-content columns to what their labels and values actually need
across desktop, tablet, and mobile layout modes.

(Recommits the change intended for bd18c0f5e, whose message accidentally
landed against an unrelated Kubernetes diff due to a parallel-agent
commit race; that earlier commit was left intact rather than rewritten.)
2026-05-20 09:39:13 +01:00
rcourtman 1ce47e21a0 Add sortable storage table headers 2026-05-20 00:29:33 +01:00
rcourtman a1b862ba9f Replace add-filter menu with direct selector 2026-05-20 00:26:55 +01:00
rcourtman bd18c0f5eb Rebalance Proxmox hosts table column widths
Disk shared half the weight of CPU and memory while rendering the same
kind of usage bar, so the disk meter was squeezed and the over-allocated
version/uptime/temp/cluster columns wasted space the node column needed
for longer cluster-style names. Equalize CPU/memory/disk to the workloads
table convention (140px each), give node headroom, and trim the
short-content columns to what their labels and values actually need
across desktop, tablet, and mobile layout modes.
2026-05-19 23:36:47 +01:00
rcourtman fb97f83574 Tighten platform table resource drawers 2026-05-19 22:42:20 +01:00
rcourtman a191f2ab2b monitoring: surface CrashLoopBackOff pods as degraded on the frontend
monitorFrontendStatus's "pod" branch was re-deriving status from
Kubernetes.PodPhase alone, so a CrashLoopBackOff pod (Phase=Running
with unready containers) was reported as "running" even though
statusFromKubernetesPod had already classified it as StatusWarning.

Drop the unconditional phase=running -> "running" mapping and fall
through to the unified-status switch, which surfaces StatusWarning as
"degraded". Phase-based mappings for pending/unknown/succeeded/failed
are kept since they still cover edge cases where the unified status
is StatusOnline but phase carries useful information.

Adds two regression cases in TestMonitorFrontendAndMetricHelpers:
- pod warning running degraded (CrashLoopBackOff)
- pod online running running (healthy baseline)
2026-05-19 22:37:55 +01:00
rcourtman 8ae422c243 Filter upstream tables on platform overviews
The workload-filter search at the top of each platform overview page now
also narrows the Hosts / Nodes / Clusters tables above the workloads
table, mirroring how Docker filters its hosts table from the same input.
Typing "pve1" on the Proxmox overview now reduces the Nodes table to
matching rows; typing a cluster or namespace fragment on Kubernetes
narrows clusters/nodes/deployments. Status / view / runtime filters
remain scoped to the workloads table (they don't apply to nodes).

Adds a shared `resourceMatchesSearch` helper in src/utils that walks the
common Resource identity fields (name, displayName, hostnames, cluster
and namespace names, tags, canonical aliases).
2026-05-19 22:18:29 +01:00
rcourtman cd85d581a3 Add platform table resource drawers 2026-05-19 22:05:45 +01:00
rcourtman 11ab890880 mock: extend offline/degraded posture to Docker, PBS, PMG, and host agents
Round out the curated demo estate so every platform page surfaces a
non-healthy entry rather than rendering uniformly green:

- generator.go: always seed a secondary PBS and PMG instance (drop the
  random spawn gates) so the demo PBS and PMG pages reliably show two
  instances.
- demo_scenarios.go (applyDemoDockerScenario): force docker host index 2
  to "field-office-edge-01" with status=offline and exited containers,
  giving the Docker page a disconnected host with stopped workloads.
- demo_scenarios.go (applyDemoKubernetesScenario): mark
  prod-euw1-k8s-03 NotReady in the last cluster so the k8s nodes
  table shows a NotReady worker.
- demo_scenarios.go (applyDemoHostScenario): also force the
  prod-euw1-k8s-03 host agent offline so the Hosts page shows an
  outage that matches the k8s nodes view.
- demo_scenarios.go (applyDemoStorageScenario): mark the dr-vault PBS
  and mail-gateway-us PMG instances as degraded with degraded
  connection health.
- demo_scenarios_test.go: update the curated-posture helpers to expect
  the new offline/degraded entries while still asserting the rest of
  the estate stays healthy after metric refreshes.
2026-05-19 22:03:06 +01:00
rcourtman 58b2e10c1f Hoist workload filter to top on platform overview pages
Move the WorkloadsFilter to the top of the Proxmox, Kubernetes, TrueNAS,
and vSphere overview surfaces so every workload page renders the filter
bar above its tables, matching the Docker pattern. The mid-page filter
placement was inconsistent across pages and pushed the search input
below the fold on busy hosts/nodes/clusters tables.

Each page lifts useWorkloadsState to its overview component, renders
WorkloadsFilter directly, and passes the state into WorkloadsSurface
(removing showFilterToolbar and the filter* configuration props from
the surface). Filter scope is unchanged — it still governs the embedded
workloads table only.
2026-05-19 21:45:40 +01:00
rcourtman a13bf7da6d Keep guest drawer discovery reads non-suspending 2026-05-19 20:59:34 +01:00
rcourtman 423beeb903 Fix Docker overview drawer interactions 2026-05-19 20:08:58 +01:00
rcourtman cd2be53725 mock: simulate offline Proxmox VE host pve5 and stopped workloads
Configure pve5 as offline, cascading to stopped status on all its guests and zeroing metrics to simulate a node outage. Also force standalone offline states on postgres-replica-01 and dev-portal-01 workloads. Update test helpers and platforms coverage checklist in platform_support_contract_test.go.
2026-05-19 19:51:23 +01:00
rcourtman 02791eaeb0 Add inline drawer route regression tests 2026-05-19 19:46:54 +01:00
rcourtman 86ac84f68e Keep inline drawer expansion local 2026-05-19 19:36:22 +01:00
rcourtman 34d53b23ea Scope platform filters to page context 2026-05-19 18:51:59 +01:00
rcourtman 2d62ae5f19 Make platform filters page-specific 2026-05-19 18:00:43 +01:00