The previous layout put AddressProbeStep at the top with the agent card
below as "Or install the agent on the host." Both sections read as
alternatives to the probe, which made the probe the primary action even
though the probe is inherently a Platform-API-oriented move (figure out
which API to connect to). A user committed to the agent path has no use
for it. The "Recommended" chip on the agent card did not fix this — the
structural lead won.
Move the agent card to the top of the Add landing with no divider above
it (it's the primary, not an alternative). Fold the probe + platform
grid into a single "Or connect a platform API directly" section below,
so the ordering matches the story: agent first, API as fallback for
hosts the agent can't cover.
Architecture pin in settingsArchitecture.test.ts updated to guard the
new ordering: agent card copy indexOf < API section indexOf < probe
indexOf. Explicitly asserts the old "Or install the agent on the host"
divider is gone.
Flip the two sections so the agent card sits above the platform grid
and reads "Recommended" with a blue border. The platform grid is now
explicitly framed as "Or connect a platform API directly — for VMware,
TrueNAS, PMG, or a remote Proxmox you can't install the agent on."
The ordering matches what the agent actually delivers: on a Proxmox
host it mints the API token and auto-registers both PVE and PBS with
zero credentials paste (RunAll in internal/hostagent/proxmox_setup.go).
The platform grid is the right path when the target is an appliance
you can't install on or a remote machine. Visual weight now matches
friction.
Architecture pin extended to assert the section ordering so a future
edit doesn't silently swap them back.
On a PVE or PBS host, the agent mints its own API token and registers
the node via RunAll (internal/hostagent/proxmox_setup.go + agent.go
:1295–1340). That is the fastest path to a working Proxmox connection
— no address paste, no credentials. Previous copy framed the agent
purely as "host metrics you add on top," which hid the shortcut and
steered Proxmox users into pasting credentials they did not need.
Reframe the two sections:
- "Or connect a remote platform API" — five tiles for when Pulse lives
elsewhere from the target (PMG / VMware / TrueNAS have no host-side
agent path, and remote Proxmox reaches the tile from a different box).
- "Or install the agent on the host" — leads with "On a Proxmox host,
this is the fastest path," then keeps the host-metrics pitch for
bare-metal Linux / Unraid / FreeBSD / Docker / Kubernetes.
Architecture pins extended to guard the Proxmox auto-register copy.
The flat catalog put Install Pulse Agent beside Proxmox / VMware /
TrueNAS as a peer. A user arriving to connect Proxmox picks Proxmox
instantly and never learns the agent also adds CPU temperature, disk
SMART, systemd services, and Docker/Kubernetes auto-detect on the host
itself.
Split the landing into two sections:
- "Or pick a platform to connect" — five peer tiles for PVE / PBS /
PMG / VMware / TrueNAS. These are management-API integrations and
belong together.
- "Or install a host-level agent" — a distinct full-width card with a
"Runs on a host" chip. Copy names the telemetry the agent adds and
says explicitly that it layers *in addition to* a platform connection
above, not instead of one.
The agent is a different kind of integration from the platform APIs.
Framing it as a peer hid its value; framing it as an adjunct teaches
it.
The intermediate two-card picker screen ("Add connection" vs "Install
agent") restated the same decision twice and framed the agent path as a
walled-off alternative. Replace it with a single catalog landing that
matches how Datadog, Grafana, and GCP Marketplace present "add an
integration" — an address probe at the top, and a tile grid below
listing Proxmox VE / PBS / PMG / VMware / TrueNAS / Install Pulse Agent
as peer entries. Picking a tile or a probed candidate drops straight
into that product's credential slot; "Back to catalog" returns to the
landing.
Retires the ConnectionsExplainer component, the picker-mode state in
InfrastructureWorkspace, and the "Enter credentials manually" toggle
in AddressProbeStep. Tests updated for the shortened path (one click
from ledger header to any credential slot).
The two teaching cards lived permanently above the ledger, so every
repeat visit paid visual cost for content the user only needs when
adding something new. The new shape:
Ledger — just the table plus one "Add infrastructure" action.
Picker — clicking Add brings up the two cards on their own
screen ("Add connection" / "Install agent") with full
teaching context.
Sub-flow — the chosen mode runs as before: probe for Platform API,
install section for the agent. Each sub-flow exposes a
"Change method" button so the user can step back into
the picker without leaving add mode.
Legacy ?add=pick deep links still route into the new picker rather than
jumping directly into the probe.
The teaching cards and the ledger-header action buttons were duplicating
the same mode split: cards explained Platform API vs Unified Agent, then
the header restated it as "Add connection" / "Install agent." The cards
are now the entry surface — each carries its own CTA button inside the
card body — and the ledger header goes back to just titling the table.
Removes the dismiss (X) affordance; the cards are now functional, not a
tutorial banner, so they stay permanently visible. In read-only mode the
CTAs are hidden so the cards remain informational.
The two-card explainer teaches Platform API vs Unified Agent as peer
options, but the ledger-header buttons had Add connection rendered as
primary/blue and Install agent as secondary/bordered — implying Add
connection was the default choice. Neither is a default: the right one
depends on the user's system. Both buttons now use the secondary tone so
they read as peers, and the user's choice is driven by the explainer
above rather than by button prominence.
The Add screen's subtext offramp was the only path to the agent install
page, and that was the wrong design: the explainer teaches Platform API
and Pulse Unified Agent as peer modes, but clicking Add connection
funneled users into a probe-first flow where the agent path was a
footnote. Users wanting CPU/disk temps, SMART, or bare-metal Linux
couldn't find the page without hunting.
Changes:
- Ledger header now has two peer actions: Add connection (Platform API
probe) and Install agent (direct to agent install slot).
- Drop the always-visible "Install the Unified Agent on a host" subtext
from the Add screen; the header button makes it redundant.
- Enhance the probe no-match box to name bare-metal Linux / Unraid /
FreeBSD and offer the agent as a first-class alternative so a user who
probed the wrong thing isn't stranded on a Platform-API-only picker.
- Rewrite the infrastructure description to match: "Add connection for
Platform API setup; Install agent for host-level metrics or bare-metal
Linux / Unraid / FreeBSD."
Tests updated to match (architecture pins, workspace flow, editor
behavior). 41 affected vitest cases pass. Playwright-verified against
the live dev server: both header buttons route correctly, subtext is
gone, no-match box surfaces the agent alternative.
Agent rows already got an honest confirm-expansion ("Removing forgets
this agent from the ledger; history is retained…") plus uninstall
commands as a courtesy. Platform API rows (PVE / PBS / PMG / VMware /
TrueNAS) had nothing beyond "Click again to confirm," which left two
honest questions unanswered:
- Does this delete my history? (No — history is retained in v1.)
- Does this log me out of the platform itself? (No — credentials on
the platform side are untouched.)
Add a parallel expansion for non-agent rows that answers both directly.
Matches the plan's "user-facing copy in Remove dialog must be explicit"
note and keeps honesty symmetric between Agent and Platform API rows.
The ledger had two columns that told the same story twice: "Coverage"
(the Monitor* surfaces) and "Collection" ("API" / "Agent" / "Docker" /
"Kubernetes"). The Collection column repeated information the System
column's subtitle already carries (the product label — "Proxmox VE",
"Pulse Unified Agent", etc.) in a less informative form: "API" doesn't
tell a new user anything the word "Proxmox VE" below the row name
doesn't already imply.
Drop the Collection column and rewrite the subtitle to speak the
explainer's vocabulary directly. Platform-API-backed rows now render
"Platform API · Proxmox VE" / "Platform API · TrueNAS" / etc. — the
exact two-mode split the explainer just taught at the top of the page.
Agent / Docker / Kubernetes rows keep their product-name subtitle
("Pulse Unified Agent" / "Docker" / "Kubernetes"); the product name
already carries the source.
Table widths rebalanced (Coverage now gets the space Collection used).
colSpan for error rows drops 6→5 / 5→4. Test fixtures drop the
collectionLabel field; the "renders one row per monitored system" case
asserts the new "Platform API · TrueNAS" subtitle string.
The Infrastructure shell subtitle carried a tail clause —
"Billing and self-hosted plan features live in Plans & Billing." — which
cross-links to a page that already owns its own sidebar entry. That
sentence belongs in Plans & Billing's introduction, not in the
Infrastructure header. Dropping it focuses the Infrastructure subtitle
on what the page is actually for: the ledger of monitored systems and
the Add connection flow.
The ledger's "How Pulse collects data" explainer already teaches the
Platform API / Pulse Unified Agent split. Repeating that split as a
choose-your-path screen when the user clicks Add connection was a dead
decision step — the subtitle ("Paste an address. Pulse detects the
product…") already commits to API-first, then the body below
contradicted it with a 50/50 fork between API and Agent.
Collapse the fallback to: one address probe (the primary path), the
existing manual-type picker under "Enter credentials manually", and the
agent install as a one-line contextual offramp beneath ("Setting up
bare-metal Linux, Unraid, or FreeBSD…? Install the Unified Agent on a
host."). The agent CTA is still there — it's just not presented as an
equal-weight peer to the probe.
Tests adjusted: the former "two cards mirroring the explainer" case is
replaced with "leads with address probe; agent install is an offramp,"
which pins the same buttons but explicitly forbids a "Platform API"
card header from reappearing. Architecture snapshot simplified to pin
only the shape-level anchors (AddressProbeStep import, manual-type
list, agent offramp CTA), not copy details.
The architecture test still pinned "Paste an address and Pulse detects
the product." inside ConnectionEditor.tsx. That helper line was moved to
InfrastructureWorkspace.tsx (and rephrased) when the add flow was
restructured around the Platform API / Pulse Unified Agent cards.
Replace the stale copy assertion with the anchors that actually matter
for the shared-editor model: the two-mode labels and the install-agent
CTA. Keeps the test honest about what the editor renders today.
The always-on framing read as unavoidable surveillance — a reason not to
install the agent, not a reason to want it. The paragraph above already
names what the agent collects (CPU/disk temps, SMART, power, Ceph/RAID)
in concrete benefit-focused terms; the chip was redundant and its label
backfired.
Only the "Off by default, opt in per host" group remains, covering
Assistant commands and Patrol remediation — users specifically want
these framed as opt-in because they involve command execution.
Test restructure: split the old combined "capabilities + trust facts"
case into two focused tests, with explicit guards that neither the
"Always on" label nor the "Hardware metrics" chip reappears.
The word "telemetry" reads as vendor phone-home (especially since Pulse
has a separate, clearly-labelled "Anonymous outbound telemetry" system
on the server for usage pings). What the agent actually does is collect
host-level metrics that flow to the user's own Pulse deployment, with
no vendor endpoint.
Updated in three places to match:
- Explainer subhead and always-on chip ("Hardware metrics",
"host-level metrics").
- Add-connection Unified Agent section ("Host-level metrics on
Proxmox...").
- Matching assertion in ConnectionsExplainer.test.tsx plus a guard in
ConnectionEditor.test.tsx that the old "host-level telemetry" copy
does not come back.
Also:
- Strip the em dash from the Add-connection workspace subheader.
- Drop the redundant inner "Add a connection" heading now that the
outer card already titles it "Add connection".
The explainer at the top of the ledger teaches two paths (Platform API
and Pulse Unified Agent), but clicking Add dropped that vocabulary: the
user landed on a generic address box with no label and could only reach
the agent by clicking "Enter credentials manually" and picking it from
a flat list of six raw-token types.
Reshape the pre-credential step into two labeled sections that mirror
the explainer:
- "Platform API" section wraps the probe input and the manual-type
fallback (now filtered to the five API types; heading reads "Choose
Platform API type manually").
- "Pulse Unified Agent" section with a direct "Install the Unified
Agent on a host" button, tinted to match the explainer's agent card.
Also aligns the CONNECTION_TYPE_LABELS entry for agent to "Pulse Unified
Agent" so the header on the install surface matches the brand used in
the ledger and the explainer.
Split agent capability chips into two labeled groups: "Always on"
(Hardware telemetry) and "Off by default, opt in per host" (Assistant
commands, Patrol remediation). Visually distinguishes the opt-in chips
with a dashed border and muted text so users can see at a glance that
command execution is a deliberate choice, not the default posture.
Also strips em dashes from copy and the one inline comment for
consistency with the rest of the product voice.
The first pass explained both modes but gave users no reason to
choose the agent when the API also covers their platform. Polish the
layout and add concrete positives grounded in what the agent actually
ships:
- Two-column split with a divider and a subtle blue-tinted accent on
the Agent panel. Agent icon badge uses the same accent so it pairs
visually without shouting.
- "Recommended" pill next to Pulse Unified Agent — it unlocks more
features, so the default bias is toward installing it.
- Capability chips on the agent panel: Hardware telemetry, Assistant
commands, Patrol remediation. These match what agentexec, hostagent,
and ai/patrol_findings actually do at runtime.
- Trust strip of verifiable facts: Single Go binary · ~13 MB download
· No runtime dependencies · Open source. Specific numbers beat
vague adjectives like "lightweight" — the ~13 MB figure is the
actual compressed size of pulse-agent-v6.0.0-rc.1-linux-amd64.tar.gz.
- Tests pin the capability labels and trust facts so the benefit copy
can't silently drift.
New users landing on an empty monitored-systems ledger had no way to
know when they should connect via a platform API vs install the Pulse
Unified Agent, and no signal that the agent unlocks Assistant command
execution and Patrol remediation on the host.
- ConnectionsExplainer: dismissible (localStorage) callout above the
table, two-mode grid with lucide glyphs (Cloud for API, Cpu for
agent), short copy tying each mode to concrete capabilities.
- Mount in InfrastructureWorkspace only in ledger mode (hidden during
add/edit flows so it doesn't compete for attention).
- Rename the agent type label from "Pulse agent" to "Pulse Unified
Agent" in CONNECTION_TYPE_LABELS. Inline prose elsewhere (tooltips,
install guides) keeps the conversational "Pulse agent" form — the
formal branded name only lives where the ledger treats it as a
type.
Two papercuts in the monitored-systems ledger:
- Agent rows stacked the hostname twice in the System column because
the aggregator emits Name = Address = host.Hostname for agents. The
repeated "delly / delly / Pulse agent" noise adds nothing. Suppress
the host line whenever it equals name so the agent row becomes just
"delly / Pulse agent", and Proxmox-style rows that still have
distinct https://host:port URLs keep both lines.
- Coverage label for agents rendered as lowercase "host" because the
aggregator's Surfaces slice is ["host"] (singular — one host per
agent) but SURFACE_LABELS only mapped the plural "hosts". Add the
singular key.
The detail panel never earned its keep — it showed the same status, host,
and last-seen already on the row, and gated Edit / Pause / Remove behind
a navigation hop. Collapse it into row-level actions so the ledger is the
single surface for managing a monitored system:
- New useConnectionRowActions hook: per-id pending / error / confirming
state so pause + two-click remove coexist across rows without
component-per-row signal proliferation. Confirm window bumped to 6s.
- ConnectionsTable: inline Edit / Pause / Remove cluster per row, red
alert row for action errors, expansion row with agent uninstall
commands (Linux + Windows, copy-to-clipboard) during remove-confirm.
- InfrastructureWorkspace: 3-mode shape (ledger / add / edit), no
detail selection state, pulls uninstall commands from the operations
context and wires clipboard + notifications.
- Deleted ConnectionDetailPanel + its test; subsystem contracts
(agent-lifecycle, frontend-primitives, api-contracts, registry)
updated to point at useConnectionRowActions and ConnectionsTable
as the canonical drill-in surface.
All 30 Settings tests pass; type-check clean.
Row click on the ConnectionsTable was still popping a right-side Dialog
drawer. That re-wrapped the per-type panels rather than replacing them —
"consolidation means shared fabric, not shared container."
Swap ConnectionDetailDrawer for ConnectionDetailPanel: same props, same
pause/remove handlers, but rendered as an inline bordered card instead
of a Dialog. The workspace Switch/Match gains a 'detail' mode that
shows the panel with a "Back to systems" header. Removing a connection
exits detail mode via onRemoved; editing routes through the existing
handleEditConnection.
Subsystem contracts updated to match: agent-lifecycle, frontend-primitives,
and api-contracts now name ConnectionDetailPanel.tsx (with a note that
it must not be re-wrapped as a floating Dialog); registry owned_files
and platform-connections-workspace-lifecycle-proof match_files renamed
to match the new filename.