Commit Graph

8524 Commits

Author SHA1 Message Date
rcourtman 61a4c232bc Project PBS datastore alert overrides back onto the thresholds page
buildProjectedOverrides matched every pbs- prefixed override key
against the PBS instance map and returned even on a miss, so datastore
override keys (canonical <instance>/<name> and legacy
<instance>-<name>, both pbs- prefixed) were dropped from the
projection. Evaluation honoured the saved override but the thresholds
page rendered the alerts toggle as re-enabled after every reload. Fall
through to the storage lookup when the key is not a PBS instance.

Fixes #1591

Contract-Neutral: #1591 frontend projection fix for PBS datastore override keys; evaluation semantics unchanged
2026-07-22 23:27:25 +01:00
rcourtman 685c67cf3f Rename awk variable shadowing the exp builtin in the PVE setup script
pulse_pve_token_expire_label assigned a column to a variable named exp,
which mawk and BSD awk reject as a syntax error because exp is a
builtin function. The generated API setup script printed awk syntax
errors on every run and reported the token expiry as unknown.

Fixes #1604

Contract-Neutral: #1604 rename of a shell-template awk variable inside a rendered script; no API surface or contract change
2026-07-22 23:24:43 +01:00
rcourtman 24e7bf41c4 Route infrastructure workspace node edits to the update endpoint
The source-manager workspace retired the legacy per-type shells that set
the configured-nodes editingNode signal, so every save from the Manage
flow fell through to the add endpoint. Editing an existing PVE, PBS or
PMG connection then failed with "Authentication credentials required"
(or a duplicate-host conflict) and the editor closed anyway, discarding
the form. Pass the edited node along with the save call, report save
success, and keep the editor open when the save fails.

Fixes #1605

Contract-Neutral: #1605 frontend-only fix routing workspace node edits to the update endpoint; no subsystem boundary moved
2026-07-22 23:20:54 +01:00
rcourtman 8510243094 Fix platform navigation during stream reconnects 2026-07-22 22:54:49 +01:00
rcourtman 82083378a2 Cover cloud tenant registry queries, unified views and slowlog wrappers
Three new branch-coverage tests taking eighteen previously unreached functions
from 0.0% to covered.

internal/cloudcp/registry: the workspace limit error message on both the nil
and populated receiver, the active workspace count per account, the tenant
lookup across owning account, foreign account and missing rows, the invitation
listing by email including case and whitespace normalization, and the
invitation delete for both an existing pair and a pair that never existed.

internal/unifiedresources: the Docker container and Kubernetes node typed
views, the presentation listing and the metrics target, each asserted on the
nil receiver, the nil nested payload and the populated case, with returned
slices proven independent of the store.

pkg/db: the tracing wrappers around BeginTx, Query, QueryRow and their context
variants plus the pool setters, with exact histogram deltas proving the
observe wiring rather than that SQLite works.

All three files are new; no source or existing test was touched.

Contract-Neutral: test-only: new Go branch-coverage tests, no source or contract change
2026-07-22 21:41:02 +01:00
rcourtman bdebc0242f Sweep missing Patrol verdicts with a bounded follow-up pass
Smaller Patrol models (reported on Gemini 2.5 Flash-Lite, still failing
on 6.1.0 after the tolerant verdict-ID resolution shipped) end full runs
without calling patrol_assess_finding for the active findings, so every
run lands as 'Patrol needs attention' with the incomplete-assessment
error even though the infrastructure is fine. The main pass ran exactly
once with no enforcement when verdicts were missing.

When the pass completes cleanly but verdicts are missing, the run now
performs one bounded assessment sweep mirroring the signal evaluation
pass: a follow-up prompt listing exactly the missing findings with their
stored evidence, same executor so verdicts land through the shared
adapter and tolerant ID resolution, uncertain accepted as a complete
verdict, no investigation or new findings, capped at missing+2 turns
(max 12). Verdicts still missing after the sweep keep the existing
run-level error.
2026-07-22 21:09:23 +01:00
rcourtman f58073372c Record v6.1.0 release completion 2026-07-22 20:18:00 +01:00
rcourtman f8cdd76b79 Keep clusters that reuse node names apart in node state aggregation
Two PVE clusters whose nodes share hostnames (pve01/pve02 on different
subnets) collapsed into one: the agent-link hostname fallback bound the
second cluster's node to the first cluster's agent despite contradicting
IP evidence, and the shared LinkedAgentID then overrode the
cluster-scoped merge key, so the fresher node overwrote the first
cluster's slot and relabeled it. The endpoint-host merge alias had the
same hole when TLS settings degrade node endpoints to bare node names.

Hostname matches now reject candidate agents whose linked nodes live in
a different named cluster or whose reported IPs exclude the node's
endpoint IP, and neither a shared agent identity nor an endpoint alias
can merge two nodes whose named clusters contradict. Empty cluster
names still merge freely so standalone views keep folding into their
cluster node.

Reported via support by an MSP running two clusters plus a standalone
that reuse pve01/pve02 across sites.
2026-07-22 20:09:09 +01:00
rcourtman e1f33c1bad Fix release integration gate target v6.1.0 2026-07-22 18:15:19 +01:00
rcourtman 0b5763764c Cover API relay converters, request filters and AI approval helpers
Six new branch-coverage tests taking twenty-one previously unreached functions
from 0.0% to between 83.3% and 100.0%.

internal/api: the approval and context-confidence converters including a
round-trip inverse assertion, the handoff identity and safe-field merge, PBS
and PMG user normalization, the auto-register missing-fields message, the
magic link redactor, the metadata save message, shell quoting, the docker and
podman command count, the hosted entitlement backoff with its cap boundary,
the PVE backup and replication job filters, the ceph discovery target, the
metric point sampler, the install job status derivation, the ledger status
summary and validation error, the licensing bridge identity and retention
normalizers, the mock fixture env gate, and the AI to unified finding and
lifecycle converters.

internal/ai: the run command approval target resolver and record builder, the
provider catalog fallback merge with its case-insensitive dedup, the attention
alert recency comparison, the patrol run fact counter, and the patrol resource
metrics lookup.

Three suspected source issues were surfaced by this work and are recorded but
not fixed: metadataSaveErrorMessage and unifiedFindingFromAI both dereference
their argument without a nil guard, and unifiedLifecycleFromAI copies the
event metadata map by reference rather than cloning it.

All six files are new; no source or existing test was touched.
2026-07-22 16:29:44 +01:00
rcourtman b5d30649ee Cover SSO conversion, TrueNAS app mapping and config store round-trips
router_routes_auth_security: all seven SAML and OIDC conversion helpers, each
driven through both its nil-input and fully populated arms with field-by-field
assertions, from 0.0% to 100.0%.

truenas_app_providers: the four TrueNAS app-to-tool mappers for ports,
networks, volumes and containers, plus the log output formatter, each over
nil, empty, populated and partially populated input, from 0.0% to 100.0%.

config report_schedules and multi_tenant: the report schedule store path,
load and save, and the organization load, strict load, save, list and delete
operations, all over a temp directory. The pair contrast that matters is
covered explicitly: LoadOrganization defaults for a missing org where
LoadOrganizationStrict errors. Targets move from 0.0% to between 87.5% and
100.0%, with incidental lift on the surrounding normalize and parse helpers.

All four files are new; no source or existing test was touched.
2026-07-22 16:18:00 +01:00
rcourtman 8f32bdd396 Fix ping timeout ceiling expectation 2026-07-22 16:15:31 +01:00
rcourtman bfe40e6a27 Cover report schedule, recovery point and maintenance wiring helpers
Three new branch-coverage tests in internal/api, each taking its named targets
from 0.0% to 100.0%.

report_schedules: htmlEscape single-pass escaping, parseReportScheduleWeekday
over every accepted form plus rejects, occurrenceKey timezone defaulting,
lastReportScheduleOccurrenceAt for monthly and weekly cadences with its four
error paths, resourceHasAnyReportScheduleTag, and the validation error writer.

recovery_handlers: parseRecoveryListPointsOptions accept and reject paths,
protectionPostureStateRank over every state, paginateProtectionPostures edges,
and the series and facet builders including timezone offset bucketing.

maintenance_verification_wiring: alertMatchesResource, both severity mappers
exhaustively over their declared constants, canonicalToSourceID, the org
context, and extractMaintenanceVerificationReportID.

All three files are new; no source or existing test was touched.
2026-07-22 15:53:34 +01:00
rcourtman 674364c749 Record v6.1.0 unsigned Windows exception 2026-07-22 15:37:48 +01:00
rcourtman 2c3c188fc8 Cover settings system state and infrastructure operations hooks
useSystemSettingsState.branchcov0722pm covers the fifteen accessors and change
handlers that no test reached: the six env-override lock gates, the backup
interval select value and summary, the four settings toggle handlers, and the
update check and install path. Zero-hit functions on the module go from 15 to 0
and uncovered branches from 23 to 20.

useInfrastructureOperationsState.branchcov0722pm is the first test for that
module, which shipped untested today. It drives the privilege-escalation
rewrite, the insecure and custom-CA flag builders for both shell and
PowerShell, the resolved command token, and the canonical uninstall and
connection identity helpers, reaching 65 of 77 branches and 19 of 24 functions.

A third candidate covering useSettingsNavigation was written and dropped: it
added no branch coverage and its handleSelectAgent cases only round-tripped a
one-line setter.

Both files are new; no source or existing test was touched.
2026-07-22 15:34:58 +01:00
rcourtman 984ca06918 Prepare v6.1.0 stable release 2026-07-22 15:05:37 +01:00
rcourtman e1b38a2fe6 Reframe commercial transition proof as activation gate 2026-07-22 14:35:28 +01:00
rcourtman 1f2a086535 Release superseded rc.5 work claim 2026-07-22 14:12:58 +01:00
rcourtman 5abbaba091 Release mobile compatibility work claim 2026-07-22 13:45:30 +01:00
rcourtman 9e39ffc920 Enforce Pulse Mobile compatibility from the core contract
Contract-Neutral: mobile compatibility generation does not change agent lifecycle or storage recovery contracts
2026-07-22 13:41:09 +01:00
rcourtman 94732c24ae Bump dompurify to 3.4.12 for GHSA-c2j3-45gr-mqc4
Dependabot alert 137 (low). Fixes a CUSTOM_ELEMENT_HANDLING hook-policy
bypass upstream; Pulse does not enable CUSTOM_ELEMENT_HANDLING so the
bump is hygiene, not an active exposure. Verified vite build, bundle
budget, and the AI chat vitest suite that exercises the sanitizer.

Contract-Neutral: Dependabot alert 137 GHSA-c2j3-45gr-mqc4: patch-level dompurify 3.4.11->3.4.12 security bump in frontend-modern; no dev-runtime orchestration, manifest-floor, or build-chunking behavior changed, contract already governs security lockfile bumps generically; dev-runtime proof artifacts are inapplicable to a frontend dependency version pin
2026-07-22 12:52:29 +01:00
rcourtman 862984aaf5 Remove decorative auto-update schedule controls from Settings
The Check Interval and Preferred Time selectors persisted values that
nothing consumes: the host systemd timer installed by install.sh owns
the schedule (daily overnight window with a randomized delay) and only
reads the autoUpdateEnabled toggle and channel from system.json. The
selectors promised schedule control they did not deliver.

The enabled state now shows an honest static note describing the real
schedule. The toggle keeps working as before. The persisted
autoUpdateCheckInterval/autoUpdateTime fields stay in the API and
config for compatibility; the UI just no longer pretends to edit
anything with them.

Contract-Neutral: presentation-only removal of dead UI controls: the auto-update Check Interval / Preferred Time selectors persisted values nothing consumes (host systemd timer owns the schedule); API payload fields, persisted config, and the toggle behavior are unchanged
2026-07-22 12:45:43 +01:00
rcourtman 3c8705da92 Let mobile relay tokens reach the Patrol attention routes
v6.1.0-rc.4 shipped the attention workbench gated on monitoring:read
only, so mobile relay tokens (which carry just relay:mobile-access) got
403 on alert sync, finding detail, and acknowledge the moment a server
upgraded to rc.4. The attention routes supersede the legacy patrol
findings routes, which accepted the mobile capability, so they join the
governed relay mobile runtime route inventory: reads accept
relay:mobile-access alongside monitoring:read (legacy ai:execute),
mutations alongside monitoring:write.
2026-07-22 12:31:44 +01:00
rcourtman 0296e4ed3e Let a failed Patrol run leave the Community hourly slot free
The 1-run-per-hour Community cadence gate on manual Patrol runs keyed off
lastFullPatrol, which is stamped on every completed run including errored
ones. Debugging a broken provider therefore cost an hour per attempt,
raised in discussion #1571. The gate now keys off the most recent
successful full run from history, matching the success-aware skip logic
the startup path already uses.

Contract-Neutral: Behavioral fix: Community manual-Patrol cadence gate now ignores failed runs; no API payload or endpoint change (#1571)
2026-07-22 12:08:37 +01:00
rcourtman 4a516ae79b Scale the Patrol readiness advisor budget with the configured provider timeout
The advisor endpoint capped the whole four-turn evaluation at a fixed two
minutes, ignoring the operator's provider request timeout. Local runtimes
like LM Studio that legitimately spend minutes on prompt processing per
turn were cut off mid-run, reported in discussion #1571 on rc.4. The
handler budget now scales to four streaming turns at the configured
timeout plus slack, floored at the old two minutes.

Contract-Neutral: Behavioral fix: Patrol readiness advisor HTTP budget now scales with configured provider timeout; no API payload or endpoint change (#1571)
2026-07-22 12:04:39 +01:00
rcourtman faaf505eb4 Reload after in-app update only once the new version is serving
The updater reports 'restarting'/'completed' and then keeps the old
process alive for a ~2s grace period before exiting for the service
restart. The post-update reload keyed on /api/health alone, so the
first probe hit the still-running old process, reloaded the old bundle
before the restart happened, and nothing re-triggered afterwards - the
page kept showing the old version until a manual refresh.

All four reload sites in UpdateProgressModal now probe /api/version
(public, no-store) and reload only when the reported version differs
from the version that started the update, which also covers rollbacks.
A bounded same-version fallback keeps deployments that intentionally
never restart (mock/CI) from waiting forever.
2026-07-22 12:02:02 +01:00
rcourtman fb3d60e475 Document What's New dialog in rc.5 2026-07-22 11:53:50 +01:00
rcourtman 723bdd2aaa Present post-update What's New highlights in a dialog instead of a banner
The curated highlights are multi-bullet reading content, which the old
full-width top banner rendered as a cramped, edge-to-edge wall of text
that pushed the whole app down and even scrolled internally. The shared
Dialog gives the content a readable measure, a proper header and footer,
and leaves the first post-update paint of the dashboard unobstructed.
Show/dismiss gating is unchanged: once per release, only when the
release has a Highlights section, and any close path records the
version as seen.

Contract-Neutral: presentation-only change: post-update What's New highlights move from a top banner strip to the shared Dialog; show/dismiss gating, highlights extraction, and release-note transport are untouched
2026-07-22 11:50:45 +01:00
rcourtman 67d7a1b454 Delegate candidate artifact read permission 2026-07-22 11:41:10 +01:00
rcourtman d3070ce1cb Prepare v6.1.0-rc.5 release 2026-07-22 11:28:45 +01:00
rcourtman 671b159711 Retain last-known platform on removed agents for scoped cleanup handoff
Removed-agent records kept no platform identity, so the fleet doctor
emitted an empty platform for removed rows and the Agent Doctor
uninstall handoff could never tighten to a single platform command.
Host and Docker removal now capture the agent's last-known reported
platform onto the removed record, and the fleet diagnostic resolves the
retained value through the same strict normalization as live subjects,
so /api/agents/diagnostics reports a platform for removed agents and
the handoff shipped in 4fc86ec18 scopes to one command automatically.
A retained value that does not normalize to a known platform still
yields an empty diagnostic platform rather than a guess, and removed
Kubernetes clusters retain nothing because the cluster report never
carries a platform. The field is additive and optional, so removed
lists serialized before it existed load unchanged. Monitoring contract
updated; proofs staged in agent_fleet_doctor_test.go and
monitor_host_agents_test.go.
2026-07-22 11:06:27 +01:00
rcourtman 4fc86ec187 Hand off host-local uninstall commands for removed agents in Agent Doctor
Removed targets previously dead-ended with no next step. Their row
expansion now offers the host-local uninstall command, resolved from the
retained diagnostic identity via the strict platform resolver, or both
labeled Linux-family and Windows commands when the platform is unknown
(the current wire reality, since removed records retain no platform).
The handoff carries the diagnostic's agent id and hostname as uninstall
identity flags and states the command runs on the affected host, not
from Pulse. The governed uninstall builders now accept a narrowed
AgentUninstallIdentity view so surfaces without a ledger-backed
inventory row reuse the same transport.
2026-07-22 10:56:04 +01:00
rcourtman c87ad1cf26 Add status filters and copyable diagnostic reports to Agent Doctor
The summary chips now double as status filters over the fleet table
(click to filter, click again or let the status empty out to clear),
so a large fleet collapses to the rows that need attention. The page
also offers plain-text diagnostic reports for pasting into a support
thread or GitHub issue: a fleet-level copy over the currently visible
rows and a per-agent copy inside each row expansion. Reports carry
status, versions, last-seen, diagnosis reasons, identity evidence, and
non-command repair actions, and deliberately never embed host-local
update commands since those can carry install tokens.
2026-07-22 10:37:11 +01:00
rcourtman c8b1e9d07b Re-home Agent Doctor from a settings modal to a routed page
Agent Doctor now lives at /settings/infrastructure/agent-doctor as a
full page in the settings shell instead of a max-w-lg dialog stacked
over the Infrastructure workspace, which was cramped on phones. The
per-agent card pile becomes one fleet table (agent, system, status,
reported and target versions, last seen) with per-row expansions
holding the diagnosis reasons, identity evidence, repair actions, and
the host-local update command. A lone scoped target auto-expands so
platform-page deep links land straight on the diagnosis.

Legacy ?agentDoctor=1 / ?agentUpdates=1 workspace links redirect onto
the route with their agents scope preserved, and closing the page is
now ordinary back navigation. The dialog component is renamed to
InfrastructureAgentDoctorPage; shared-template registry, the
agent-lifecycle subsystem inventory, and the frontend-primitives
settings deep-link clause follow the change.
2026-07-22 10:26:06 +01:00
rcourtman f1eac15833 Clear expired release work claim 2026-07-22 09:10:40 +01:00
rcourtman b8e78fa9c2 Persist Patrol proposal evidence in action origins 2026-07-22 09:10:40 +01:00
rcourtman b63fc0a23d Convert audit.db to incremental auto-vacuum and reclaim after retention
Retention deletes freed pages inside audit.db but the file never
shrank on disk, the same bloat class #1496 fixed for
unified_resources.db. Migrate existing databases to incremental
auto-vacuum at startup and return freed pages to the OS after each
retention pass, capped per cycle so a backlog drains gradually.
2026-07-22 09:09:25 +01:00
rcourtman fa54516f8c Explain the missing CAP_NET_RAW grant when ICMP probes are blocked
In-place updates never rewrite the systemd unit, so installs upgraded
past v6.1.0-rc.1 keep a unit without AmbientCapabilities=CAP_NET_RAW
and every ICMP probe fails with ping's raw stderr. Detect the
capability failure and point at the unit and its fix instead (#1554).
2026-07-22 09:09:25 +01:00
rcourtman 743ba7e5a3 Cover the Unraid merge, setup classifier and retry helpers left untested
The Unraid disk mergers, the Proxmox setup output classifiers, the ZFS SMART
annotator, the Docker runtime command builder, the approval decision actor
and the update retry classifier were all at zero coverage. Each decides
something a user feels directly, whether an Unraid disk keeps its name after
a merge, whether a setup run reports an already-registered token as a
failure, which pool a SMART entry is attributed to, and whether a failed
update request is retried or surfaced as an error.

Adds branch coverage for mergeUnraidDiskINI, mergeUnraidDisk,
defaultUnraidDiskName, isAlreadyExistsOutput, the client error Error and
Unwrap methods, annotateSMARTWithZFSPools, dockerRuntimeCommand,
approvalDecisionActor and isRetryableUpdateRequestError, including nil and
whitespace arms, field precedence between base and incoming values, in-place
slice mutation asserted on the caller's value, and wrapped errors that only
resolve through errors.Is and errors.As. Every named target moves from zero
to full statement coverage.

Test-only change.

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no contract surface touched
2026-07-22 08:06:56 +01:00
rcourtman f109aac93f Cover the connection ledger and agent update branches the fresh drop left cold
The TrueNAS settings panel, the connections ledger, the workloads guest
metadata subscription and the agent update commands model all changed in the
last drop and each kept a block of branches no test reached. Those branches
decide which error a user sees when a TrueNAS connection fails, which rows
the connections ledger shows, whether guest metadata survives a canonical
event, and whether manual update commands stay available while an update is
applying, so a silent regression there lands straight in front of a user.

Adds branch coverage for getTrueNASErrorMessage and the dialog open, close,
reset and delete transitions, for findById plus the ledger derivation
fallbacks, for the metadata subscribe, refresh and teardown arms, and for the
actively-applying guards on the update commands. Measured uncovered branches
drop from 24 to 17, 35 to 12, 23 to 15 and 14 to zero, with the six and one
zero-hit functions in the first two modules now covered.

Test-only change.

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no contract surface touched
2026-07-22 07:23:42 +01:00
rcourtman 567303778a Cover the connection and monitoring pure helpers left untested by the fresh drop
The connections aggregator, alerts and grouping helpers plus the monitoring
group-key, availability-key and read-state mapper helpers were all at zero
coverage. Each of these is a pure value-in to value-out function on a path
that decides how a connection is grouped, whether an alert snapshot is
emitted, and how host thermal state crosses the read-state boundary, so a
silent regression there is invisible until it reaches a user.

Adds branch coverage for buildPMGConnection, connectionTypeForAlerts,
snapshotConnectionsForAlerts, uniqueMatch, connectedInfrastructureGroupKey,
availabilityConnectionKey, ConnectionHealthKey, pingArgs,
hostThermalStateFromReadStateView, latestMetricPoint and MemorySourceTrust,
including nil and whitespace arms, candidate precedence order, and clone
independence in both directions for the mapper. Every named target moves
from zero to full statement coverage apart from the pingArgs arms that are
unreachable on this platform.

Test-only change.

PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no contract surface touched
2026-07-22 06:42:33 +01:00
rcourtman f8c5c55d11 Let --report-ip lead host identity and pass it through the installer
An explicit --report-ip is the user naming the primary address on a
multi-NIC host, but identityFromHost appended it after the auto-detected
interface addresses while every consumer of ResourceIdentity.IPAddresses
treats the first entry as primary, so the override never changed what
the Machines table displayed. Prepend it instead.

The install script also rejected --report-ip as an unknown argument even
though the agent supports the flag, forcing hand edits to the service
unit that a later --update run would drop. Accept the flag, render it
into the service ExecStart, persist it in connection state, and
recognise it during saved-state and arg-stream recovery so updates
preserve it.

Refs #829

Contract-Neutral: behavioral fix: user-specified report-ip leads host identity addresses and the installer passes --report-ip through; no public contract change (#829)
2026-07-22 00:49:43 +01:00
rcourtman b853638980 Keep the manage flow synced to the ledger and surface TrueNAS TLS errors
The infrastructure manage flow froze the row it opened with, so removing
an attached Pulse Agent kept rendering the removed agent as Active even
though the server had already deleted it, which read as the button doing
nothing. Derive the editing row from the live ledger by ID, falling back
to the opening snapshot while a reload is in flight.

TrueNAS connection tests collapsed every failure into the generic
"Failed to connect to TrueNAS" toast while the actionable cause (the
exact x509 verification error) rode unread in details.error. Prefer the
detail, mirroring the existing VMware presenter, so users can tell an
untrusted CA from a hostname mismatch instead of reaching for Skip TLS
Verification.

Refs #1602

Contract-Neutral: behavioral fix: manage flow renders live ledger state after agent removal and TrueNAS test toasts surface the underlying TLS error detail; no public contract change (#1602)
2026-07-22 00:49:08 +01:00
rcourtman 0928584625 Pro updater requests its channel from the dual-channel broker
The license-server broker now serves stable and rc manifest slots
(pulse-pro side). The Pro self-updater passes channel=rc for rc-channel
installs so they track the RC slot; stable installs keep the unchanged
default URL, which older brokers also understand. The client-side guard
that refuses a prerelease pin on the stable channel stays as the backstop
for a drifted or single-manifest broker. Contract updated under
deployment-installability; fixture now pins the channel query behavior.
2026-07-22 00:16:43 +01:00
rcourtman f16a7f4d7d Record commercial proof approval boundary 2026-07-21 23:57:45 +01:00
rcourtman dc25f20cfd Record executable commercial floor proof 2026-07-21 23:49:43 +01:00
rcourtman be0ef26d16 Supersede unsafe commercial rehearsal outline 2026-07-21 23:26:12 +01:00
rcourtman d6fa623035 Compose ESXi hosts as members of their owning vCenter connection
One vCenter connection spans many ESXi hosts, so the Infrastructure source
row now lists them the way Proxmox cluster rows list their nodes: an
expandable member list with per-host state, aliases, and last-seen. Members
are API-side composition only — no primary marker, no agent connection, and
the member subtitle reads 'vSphere host' instead of cluster-node wording.
TrueNAS keeps no member composition because that connection monitors exactly
one machine.

Mock mode now feeds its vCenter and TrueNAS fixtures into the connections
aggregator (only when no real instances are configured), so the mock ledger
shows the same platform source rows a real deployment would instead of
omitting vSphere and TrueNAS entirely.
2026-07-21 23:09:29 +01:00
rcourtman 278820283c Record commercial rehearsal CLI validation
Contract-Neutral: Evidence-only commercial rehearsal record; unrelated runtime files and shared index belong to another active task
2026-07-21 23:03:59 +01:00
rcourtman 77cfa9f1e4 Record fail-closed commercial event rehearsal 2026-07-21 22:53:52 +01:00