Commit graph

4186 commits

Author SHA1 Message Date
rcourtman
f4930cdd34 Synchronize the shipped transparency document
Keep the in-app documentation byte-for-byte aligned with the canonical disclosure after the support-email clarification.
2026-08-21 09:42:19 +01:00
rcourtman
ad91258d37 Pin the existing agent identity in the Unix credential repair command
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Benchmarks (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
The Repair Authentication command generated for Unix agents carried
--update and a fresh token but no identity, while the Windows path pins
PULSE_AGENT_ID and PULSE_HOSTNAME. A repair reinstall without the pin
can register a fresh suffixed agent identity for the same machine
instead of converging on the one being repaired, which is exactly what
a field report documented across repeated repair attempts.

Pass --agent-id and --hostname from the canonical connection the same
way the Windows command does.

Refs discussion #1748

Contract-Neutral: Discussion #1748: unix repair command now pins the existing agent identity, parity with the windows path; command-string generation only, no API or payload change
2026-08-21 06:48:44 +01:00
rcourtman
7da385cf28 Prepare v6.3.0-rc.4 release 2026-08-21 00:00:37 +01:00
rcourtman
5ed053dfc2 Count the alert history severity chips from the list's own predicate
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
The alert history FilterBar rendered bare Critical/Warning chips while
the platform tables' status chips now carry counts. Expose
countForSeverity from useAlertHistoryState, built on the same
filterAlertHistoryItems predicate the list renders through, so each
severity chip shows the row count its selection yields for the fetched
period and current search. Counts respect the shared Inventory totals
visibility preference; the Period facet stays uncounted because it is a
time scope, and only the currently fetched range is available to count.
2026-08-20 22:08:43 +01:00
rcourtman
ee4cd560b6 Count platform status chips from each table's own filter predicate
Only two platform tables ever adopted the attention-count affordance, so
Docker, Kubernetes, TrueNAS, Machines, and the Proxmox detail tables all
rendered bare status chips while the Proxmox and vSphere workloads bars
showed counts. Replace withPlatformAttentionCount with
withPlatformStatusCounts and roll it across every PlatformTableToolbar
status facet: each chip now carries the row count its own selection
would render, sourced from the same predicate the table filters with
(createPlatformTableFilterState.countForStatus, or the surface's
governing filter on the bespoke Docker storage, Kubernetes section, Ceph
and Replication toolbars), so a chip's number can never disagree with
the rows clicking it shows. Counts respect the existing estate-wide
'Inventory totals' visibility preference, the attention tables keep
their tone escalation, and the container-attachment options drop their
static ariaLabels so the count is part of the accessible name.
2026-08-20 21:52:34 +01:00
rcourtman
86d0af4979 Scope platform-page workload inventory counts to the forced platform
The Proxmox and vSphere overview filter chips counted the estate-wide
workload inventory while the table below applied the page's platform
scope, so a degraded workload on another platform (a Pending Kubernetes
pod) produced Attention 1 whose click rendered 'No guests found'. A
forced platform is the page's identity, not a user filter: apply it in
selectVisibleWorkloadInventory before dedupe so chip counts, search
suggestions, and availability detection describe the same guest set the
table can ever render.
2026-08-20 20:59:34 +01:00
rcourtman
4686efd8c8 Add a supported least-privilege agent install profile
The unified agent's Linux installer only offered the root profile, and the
docs called non-root unsupported. That default is the most-cited reason
security-conscious evaluators reject Pulse without trying it. install.sh
gains --least-privilege: the service runs as a dedicated nologin
pulse-agent system user with every existing hardening directive, no
LXC-attach ambient capabilities, docker-group membership for socket reads,
and refusal (not silent root fallback) on appliance platforms, non-systemd
init systems, and --enable-commands. Optional --grant-smart and --grant-pct
restore the only two collectors that need elevation through
visudo-validated exact-command sudoers rules and root-owned wrappers the
agent reaches via new absolute-path-only PULSE_SMARTCTL_PATH /
PULSE_PCT_PATH overrides; the pct grant covers pct list and pct df only
and can never widen into pct exec. --update preserves the profile and its
grants by reading the installed unit.

The agent now authors a privilege block in its report (effective root,
service user, active helpers), carried through models into the fleet
doctor as a descriptive field: Agent Doctor shows the profile and its
helpers instead of presenting intentionally absent collectors as a fault,
and a least-privilege agent can never be marked unhealthy on that evidence
alone.
2026-08-20 20:49:44 +01:00
rcourtman
74873e2b55 Add a user-facing alert delivery log and honest test-send results
The delivery health verdict said when something was wrong; nothing showed
what actually fired and where it went. Expose the queue's retained
per-attempt audit rows as GET /api/notifications/delivery-log (newest
first, retention-labeled, webhook secrets redacted from error text) and
render them as a Recent delivery activity card on the alert destinations
tab, with outcome badges, destination names, and failure classes. Audit
rows now persist the normalized destination identity in a destination_id
column; older rows fall back to their operational links.

Test sends bypass both the queue and the activation gate, which is exactly
how installs came to believe delivery worked while every real alert was
suppressed (483 installs in the 08-18 telemetry read). Successful test
responses now carry deliveryPaused: true whenever the manager is gated
off, and the destinations UI warns instead of celebrating.
2026-08-20 17:35:13 +01:00
rcourtman
a5f1567172 Fix the copy-style audit violation in the German guest-Docker description
The de translation landed with a semicolon inside user-visible copy, which
the copy-style audit rejects, so every subsequent commit on main was
blocked. Split the sentence instead; wording is unchanged.

Contract-Neutral: copy-style punctuation fix in existing de translation string; no contract, payload, or behavior delta
2026-08-20 17:34:45 +01:00
rcourtman
fd35c54773 Add canonical infrastructure search completion 2026-08-20 17:22:37 +01:00
rcourtman
a4d37447a0 Give the Docker-in-LXC opt-in an admin Settings toggle
Discovering Docker inside Proxmox LXC guests previously required an
undocumented environment variable plus a hand-edited systemd unit,
which is how the surface kept shipping invisible. The opt-in is now a
persisted system setting with an admin-only toggle in Settings →
System → General, presented as its own "Docker in Proxmox LXCs" card
under the existing Docker/Podman updates card, with the requirements
(node agent with command execution) and data-collection bounds stated
inline.

The setting rides the admin-gated system settings endpoint
(RequireAdmin + settings:write, matching every other server-wide
toggle). Flipping it persists first, then applies to the runtime
config and fires a reconfigure hook so the router rebuilds the
monitor's Docker checker and inventory collector immediately — no
restart. PULSE_ENABLE_PROXMOX_GUEST_DOCKER_INVENTORY remains
authoritative: when set it locks the toggle (EnvironmentLockBadge in
the UI, HTTP 400 from the API), and an unrelated settings save can
never clobber the runtime value. The opt-in guardrail test now pins
the settings path, the env lock, and the persisted field.

Installer and node setup-guide callouts now point at the toggle first
with the environment variable as the locking override, and the docs,
their public mirrors, and the agent-lifecycle subsystem contract say
the same. New i18n strings shipped for en/de/es.

Verified live in mock mode: toggle off/on from the UI drives the
collector teardown/setup log lines, the value survives a server
restart, and all three touched surfaces render at desktop and mobile
widths (receipt in frontend-modern/browser-verification.json).
2026-08-20 14:27:12 +01:00
rcourtman
cb12b6f35c Stop showing an in-flight backup as a completed one
An in-progress PBS snapshot (reproduced on VM 117 at 9% written) was
counted as a completed backup: the guest's LastBackup jumped to "now" the
moment vzdump started, and the workloads badge showed a green healthy
shield for a backup that did not exist yet. If the job aborted, that
shield had never been true.

Detect the in-flight state from what the PBS listing actually says - no
size and no index.json.blob manifest in files - and flag the snapshot
InProgress instead of complete. On the PVE side, correlate storage content
entries with live vzdump tasks: a guest archive whose ctime falls inside a
running task's window is that task's partial output, and a PBS-storage
entry without a size has no manifest yet.

In-flight artifacts now feed a new per-guest BackupInProgress flag rather
than LastBackup, so the badge age always reflects the latest COMPLETED
backup. The workloads badge gets a distinct blue "Running" state (green
would claim success, None would hide that work is underway), with the
last-completed age kept in the tooltip and accessible label. Recovery
points map to OutcomeRunning with no completion time, keeping posture
freshness and the Backups page's latest-restore pointers anchored to
completed backups; the recoverable-artifact list shows a Running chip.
A cached in-flight snapshot forces a per-poll re-fetch of its group so
completion or abort is noticed promptly instead of after the cache TTL.
2026-08-20 12:13:44 +01:00
rcourtman
2483dbed9c Surface Docker-in-LXC probe failures and stop the probe churn
The pct exec socket probe that discovers Docker inside LXC guests
failed invisibly: errors logged at debug only, and a failed probe never
set DockerCheckedAt, so the guest stayed in first-check state and was
re-probed on every poll forever. On a node where lxc-attach could not
enter unprivileged guests that meant ~49 pct execs per minute of
steady-state churn, with nothing at default log level to say why the
Proxmox page showed no Docker.

Track consecutive probe failures per guest in the monitor. The first
failure of a streak, and the point where the backoff reaches its cap,
log at warn with the guest, node, and error; repeats stay at debug.
Failed probes retry with exponential backoff from one minute to a
thirty-minute ceiling, a success clears the streak with an info log,
and reconfiguring the checker resets all streaks so command execution
enabled from the UI at runtime still retries immediately. Entries not
refreshed for a day belong to deleted guests and are pruned.

PULSE_PROXMOX_GUEST_DOCKER_INVENTORY_VMIDS now gates the socket probe
as well as inventory collection. It previously only limited inventory,
so opting into specific guests still probed every running LXC on every
cycle; now guests outside the allowlist are never pct exec'd at all.

Subsystem contract and configuration docs updated to match.
2026-08-20 11:40:16 +01:00
rcourtman
76ae173785 Refresh the frontend bundle-size baseline
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
The budget check had not run since 08-18 because the vitest step was
failing first. With the tests fixed it now flags a week of intentional
feature growth: the estate-overview and mobile table work grew every
major chunk (index, sharedPlatformPage, Alerts, Proxmox, AIIntelligence)
by 3-10 kB gzip, and removing the saved-views lazy boundary folded the
FormTextarea chunk into its importers. Rebaseline from a lockfile-clean
build; totals stay within the overall budget.
2026-08-20 06:57:02 +01:00
rcourtman
57195884cf test: repin platform surface tests to the estate-overview controls
The estate-overview redesign folded attention banners and guest totals
into the canonical tables and the shared View menu, updating its own
co-located tests but leaving three pins in other files on the old
surfaces. Repin them: the Docker single-host View menu now legitimately
carries the inventory-totals toggle but still no layout choice, node
availability reads as the warning-toned ready/total ratio on the
Kubernetes cluster row, and Proxmox guest totals flow through the
toolbar inventory counts instead of a page-local memo.
2026-08-20 06:23:00 +01:00
rcourtman
6399e83f6d Remove FilterBar saved views
Some checks failed
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
Patrol Qualification Regression / Catalog, scorer, and replay regression (push) Has been cancelled
Saved views stored a named URL query string in localStorage under
`pulse:filterbar:saved-views:<key>` and offered it back from a "Saved"
control on eight surfaces. A saved view was only ever the page's URL:
`saveCurrent` snapshotted `window.location.search` and `applyView`
navigated back to it.

The browser already does that, better. A bookmark syncs across devices,
survives clearing site data, is searchable, can be foldered, and can be
shared with someone else. The localStorage copy did none of those, so a
"saved" view silently vanished on a new browser or a cleared cache.

There is also no evidence anyone used it. It shipped 2026-05-01 in
v6.0.5 and no Pulse issue, comment, or discussion has mentioned it since.
#1510 is the telling case: filed two months after it shipped, by a user
who wanted their Proxmox status filter to survive a tab switch, which is
exactly the job this feature claimed. They did not reach for it, and the
fix that satisfied them was sticky route state (d20c556db).

Removed the hook, the menu, the `savedViewsKey` prop, and the per-surface
keys. URL-backed filters and sticky route state are untouched, so a
filtered page is still a shareable, bookmarkable link. On mobile, a
surface with no other auxiliary control no longer renders an action row
that existed only to hold the Saved button.

Subsystem contracts keep the URL-ownership rule this feature depended on
and now state it as the primary contract, with a note not to reintroduce
an in-app view library. A guardrail asserts the FilterBar barrel does not
re-export SavedViews.

User-visible removal: needs a release-note line at the next cut.
2026-08-19 21:01:59 +01:00
rcourtman
547afd2d17 Fix wrapped filter action alignment 2026-08-19 20:32:27 +01:00
rcourtman
202f3b2f4c Fix Proxmox LXC workload columns 2026-08-19 20:22:55 +01:00
rcourtman
e2e8b72baa fix(frontend): keep Proxmox workload controls in view 2026-08-19 20:07:48 +01:00
rcourtman
e7de6602f7 test(shared): give the nav clearance render helper its required props
The helper added in 54f623cff omitted onPrimaryClick and onUtilityClick, so
tsc rejected the MobileNavBar element while vitest, which does not type-check,
still passed. Left main failing type-check.
2026-08-19 20:00:09 +01:00
rcourtman
54f623cffb fix(ui): measure the mobile nav instead of guessing its height
The Assistant overlay and its backdrop reserved a hardcoded
calc(5rem + env(safe-area-inset-bottom)) for a bar that measures 45px. At
375x812 the backdrop ended at y=732 while the bar starts at 767, leaving a
35px band that was neither dimmed nor click-blocked: page content showed
through and stayed interactive outside an open modal.

The bar's height is content-driven and already includes the safe-area inset
via pb-safe, so no caller can hardcode it correctly. Five sites had tried:
the Assistant panel, its backdrop, the star banner, and both halves of the
.filter-bottom-nav-aware-panel rule, which also carried a derived 6rem.

MobileNavBar now publishes its measured height as --pulse-mobile-nav-height
and those five sites read it. Publishing happens in onMount rather than the
ref, because Solid runs refs before the node is in the document where
offsetHeight is 0; reading after mount forces layout and gets the real value
on first paint. A ResizeObserver covers content-driven changes and a resize
listener covers the xl breakpoint, where the bar swaps between its height and
0 by display alone.

The declared :root value is only a pre-measurement fallback and deliberately
under-estimates. Over-reserving is what produces the un-dimmed band; reserving
slightly less is harmless because the opaque bar paints over it.

Verified at 375x812 and 1280x800: backdrop and panel now both end at 767
against a bar top of 767, gap 0; the point 5px above the bar hit-tests to the
Assistant panel rather than page content; the filter panel resolves to 45px on
mobile; and the star banner's md:bottom-4 still wins at 16px.

Adds the regression proofs the shape guard asks for, including an
architecture test that fails if any runtime source reintroduces a literal
bar height, and records the ownership boundary in the frontend-primitives
and ai-runtime contracts.
2026-08-19 19:57:18 +01:00
rcourtman
66c87fb6e8 fix(frontend): place estate totals beside labels 2026-08-19 19:42:06 +01:00
rcourtman
4de1ede603 fix(frontend): make estate overview glanceable 2026-08-19 18:21:10 +01:00
rcourtman
ba4a6c46d9 fix(frontend): fold estate totals into platform controls 2026-08-19 18:21:10 +01:00
rcourtman
85ddd9f7f9 feat: add canonical platform estate overviews 2026-08-19 17:25:39 +01:00
rcourtman
63c40ebe5e fix(telemetry): make patrol run volume and refusal causes readable
Two counters could not answer the question they exist for.

pulse_intelligence_patrol_runs_30d was counted from the operator-facing
patrol run history, which is capped at MaxPatrolRunHistory (100). On any
install patrolling on a normal schedule that cap is reached within hours
of a thirty-day window opening, so the field reported the cap rather than
the run count, and the calls-per-run ratio derived from it was inflated by
a censored denominator. Patrol run history now carries an uncapped daily
tally beside the capped list, advanced from a persisted high-water mark so
repeated full-list saves cannot double count, pruned to 31 days, and read
in preference to the list. An install with no tally yet falls back to the
list, so the counter never regresses on upgrade.

Schema v8 split agent-side pre-mutation refusals into target-change,
prerequisite and contract categories so they would stop hiding in "other".
In production all three are zero fleet-wide and "other" still absorbs every
refusal, because the dominant reason code is the legacy preflight_refused
aggregate recorded when an agent sends no machine reason code at all.
Agents older than the typed refusal contract report every refusal that way,
so the split is starved rather than broken, and folding the two together
made those two states indistinguishable. Schema v9 counts uncoded refusals
separately.
2026-08-19 17:00:02 +01:00
rcourtman
5bbee0da3d feat: scale the Proxmox demo estate 2026-08-19 16:28:07 +01:00
rcourtman
c7e46b9064 fix(frontend): align backup health summary on mobile 2026-08-19 15:46:28 +01:00
rcourtman
2071325b88 Show offline alert controls in the card threshold layout
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
The desktop thresholds table carries an Offline Alerts column with the
global default control and per-row toggles, but the card layout used at
narrow container widths (including zoomed desktop browsers) omitted it
entirely, so connectivity alert state was invisible and untogglable
there. Mirror both controls into the cards: an Offline alerts tile on
the Global Defaults card and an Offline row on each resource card, with
the same tri-state cycle for guests and docker containers and the same
On/Off badge for nodes, agents, PBS and docker hosts.

Refs #1738

Contract-Neutral: frontend-only: card threshold layout renders the existing offline alert controls, no API or contract delta (Refs #1738)
2026-08-19 06:34:15 +01:00
rcourtman
2f6f2396b6 Prepare v6.3.0-rc.3 release
Contract-Neutral: Routine release-preparation cut for v6.3.0-rc.3. deployment-installability.md IS staged with the substantive active-prerelease and mobile-decision updates for this cut. The residual demand is a verification artifact, and the registry-listed proof scripts/installtests/build_release_assets_test.go plus install_docker_sh_test.go already cover this change and pass against it; they validate the packet dynamically from the repo-root VERSION rather than pinning a literal, so they have no diff to stage. Both were run green immediately before this commit and caught two real errors in the mobile-decision clauses, which are fixed here.
2026-08-18 22:36:10 +01:00
rcourtman
4a66072c15 fix(frontend): close mobile table density gaps
Contract-Neutral: Completes the already-governed mobile table readability contract on remaining Docker overview and standalone availability rows without changing the contract.
2026-08-18 22:17:13 +01:00
rcourtman
4f61cc3bfe fix(frontend): standardize mobile table readability 2026-08-18 22:04:02 +01:00
rcourtman
7575e049c3 test(alerts): pin that a resource-less system alert renders
The alert card assumes an alert has a resource behind it. It reads
resourceName unguarded and guards node and resourceId with Show, so a
system alert renders only because those guards happen to be there. Nothing
stopped a later change from adding an unguarded resource field and breaking
the notification-delivery alert, which is the one alert whose whole purpose
is to be visible when nothing else can reach the operator.

Drive OverviewTab with the exact payload internal/alerts marshals for a
raised system alert, captured from the Go side rather than hand-written:
empty resourceId, empty node, resourceName "Pulse". Assert the card names
Pulse rather than inventing a resource, title-cases the hyphenated type,
carries the message and its pointer to the destinations surface, and renders
no empty node line or undefined text.
2026-08-18 21:30:24 +01:00
rcourtman
7d44293dfd fix(frontend): preserve compact replication values
Contract-Neutral: Pixel-fit correction within the already-governed five-column mobile replication contract.
2026-08-18 21:08:16 +01:00
rcourtman
c68be00412 fix(frontend): keep narrow mobile data readable 2026-08-18 21:01:07 +01:00
rcourtman
4cd4921e49 fix(alerts): surface failing notification delivery on the alerts overview
Notification delivery health was rendered by exactly one component on the
destinations config tab. A destination that stops delivering is invisible by
nature, because the failure is the channel that would have reported it, so
confining the only evidence to a tab people open once during setup means the
state persists indefinitely.

Production telemetry shows it persisting. 78 installs retry every notification
to the maxAttempts ceiling and deliver almost nothing: 155,967 attempts and 221
deliveries across a week. 50 of them have not delivered a single notification
in 30 days, and another 28 delivered normally at some point and have since
stopped, which is the worse case because nothing about the estate looks
different. The dominant terminal failure classes are authentication and
configuration, so most of these are fixable in minutes by the operator who
cannot currently tell there is anything to fix.

Extract the delivery-health load into useNotificationDeliveryHealth and render
the existing warning on the alerts overview as well as the destinations tab.
The shared hook also holds the trigger rule, so both surfaces agree on what
counts as needing attention and neither can warn before the first load
resolves.

This does not make Pulse monitor its own delivery. Every alert type is
resource-scoped and there is no system-scoped alert concept, so raising this
like any other monitored condition remains open.

Proof: useNotificationDeliveryHealth.test.tsx pins the trigger rule across
healthy, degraded, server-reported-unavailable and unreachable-endpoint cases,
plus the silent-until-loaded guard. Verified live at 1280x800 and 375x812: a
healthy queue leaves the overview clean, and a degraded payload renders the
warning above the stat cards with the dominant failure class named.

Contract-Neutral: frontend-only reuse of the existing /api/notifications/health payload on a second surface; no subsystem Canonical Files/Shared Boundaries/Extension Points delta and no API change. Proof ships as useNotificationDeliveryHealth.test.tsx; the alerts verification-artifact list enumerates existing filenames, so a new hook's own test cannot appear on it.
2026-08-18 20:20:30 +01:00
rcourtman
7879862c24 fix(frontend): promote mobile platform switching 2026-08-18 20:15:39 +01:00
rcourtman
4be10526b6 fix(alerts): say when notification delivery is paused where destinations are configured
Alert notification delivery is gated on the alert config's activation
state: monitor.go calls SetEnabled(enabled && ActivationState ==
ActivationActive), and a new install defaults to ActivationPending. In
that state alerts keep firing and every sendAlert returns at the
disabled check with only a debug log, so nothing reaches the queue.

The destinations surface never said so. A user could configure SMTP or a
webhook, enable it, and send a test that passes, because SendTestNotification
checks only the destination's own Enabled flag and bypasses the delivery
gate entirely. The overview tab showed "Notifications paused" but the
Notifications tab, where that belief is actually formed, showed nothing.

Production telemetry shows the resulting cohort: 510 installs with an
enabled destination, alerts standing right now, and zero delivery attempts
in 7 days; 483 of them have made no attempt in 30 days. On 6.2.1 that is
357 of 1219 notification-enabled installs.

Surface the pause on the destinations tab with the consequence spelled out,
including that a passing test proves nothing while delivery is off, and
offer the activation action inline. The card stays quiet until the alert
config resolves so it cannot flash a false warning on tab open.

Proof: AlertDeliveryPausedCard.test.tsx pins the consequence copy, the
test-send caveat, the activate callback and the busy state;
alertDestinationsPresentation.test.ts pins the copy for all three paused
reasons. Verified live against a backend in pending_review at 1280x800 and
375x812: card renders above the email section, activating clears it, and
deactivating restores it.

Contract-Neutral: frontend-only disclosure of existing alert delivery activation state; no subsystem Canonical Files/Shared Boundaries/Extension Points delta, no API or payload change. Proof ships as AlertDeliveryPausedCard.test.tsx plus alertDestinationsPresentation.test.ts; the alerts verification-artifact list enumerates existing filenames, so a new component's own test cannot appear on it.
2026-08-18 20:01:07 +01:00
rcourtman
9bec2a52b5 fix(frontend): unify mobile platform table density 2026-08-18 19:49:23 +01:00
rcourtman
550b68fb13 Honor the typed connection name in assisted Proxmox setup
Adding a PVE/PBS source through the Connect via API path discarded the
name typed in the add dialog: the setup bootstrap only carried type and
host, and auto-registration named the new connection after the node's
self-reported hostname. A user asking for a cluster named enacon got a
connection called pve01 and could only rename it after adoption.

Carry the typed name on the one-time setup token
(SetupTokenRecord.DesiredName) via an optional name field on
/api/setup-script-url, and let the setup-token-authenticated
auto-register completion name a newly created connection from it,
falling back to the hostname-derived default when absent. Dedup and
cluster-member adoption identity stay hostname/candidate based, so the
carried name cannot fork an existing registration.
2026-08-18 19:23:52 +01:00
rcourtman
006d5da1c7 Keep unsaved node editor input across background refreshes
Editing a Proxmox/PBS/PMG connection raced the configured-nodes reloads
that fire during adoption (refresh_nodes, node_auto_registered): the
editor's JSX computed findEditableNode eagerly, so every reload
remounted the editor and reset the form, and the node modal state
rebuilt formData from each refreshed snapshot, wiping typed input
within seconds. Reported by a Pro customer who could only rename one
or two things per refresh window.

Thread the editing target through a non-keyed Show and an accessor so
reloads update the mounted editor instead of recreating it, and guard
the form sync with a dirty flag: once the user has typed, background
refreshes stop rebuilding the form, while a different editing target
still resyncs. Regression test proves the poll-clobber case fails
without the guard.

Contract-Neutral: behavioral bugfix: preserve unsaved node editor form input across background config reloads; no API payload, endpoint, or public contract change
2026-08-18 19:05:37 +01:00
rcourtman
6ffc6b4f3a feat(frontend): standardize dense mobile platform tables 2026-08-18 17:39:07 +01:00
rcourtman
642d1ec5b0 feat(frontend): show more mobile operational columns
Contract-Neutral: Phone-width responsive column presentation only; no API, ownership, route, or subsystem contract changed.
2026-08-18 16:50:38 +01:00
rcourtman
61bc4b25eb feat(frontend): add compact mobile operations mode
Contract-Neutral: Phone-width presentation geometry only; no subsystem behavior, API, ownership, route, or extension contract changed.
2026-08-18 16:18:46 +01:00
rcourtman
6ca27721ef feat(frontend): increase mobile information density
Contract-Neutral: Phone-width spacing and density-only presentation change; no subsystem behavior, API, ownership, or extension contract changed.
2026-08-18 15:29:19 +01:00
rcourtman
e7cc3f4b67 fix(patrol): make handled decisions recoverable
Contract-Neutral: Patrol lifecycle recovery UI fix with no API, primitive, or subsystem contract change
2026-08-18 15:05:42 +01:00
rcourtman
2eeb643afe Prepare v6.3.0-rc.2 release
Some checks are pending
Public docs / check (push) Waiting to run
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
2026-08-18 13:31:21 +01:00
rcourtman
7cb3fc2e6b fix(actions): link reviews back to Patrol records 2026-08-18 13:23:02 +01:00
rcourtman
bcea56db97 fix(patrol): clarify decisions and action origins 2026-08-18 12:44:20 +01:00
rcourtman
ac4116e09b feat(patrol): simplify the decision workspace 2026-08-18 11:51:29 +01:00