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.
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.
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.
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.
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)
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)
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.
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).
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
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
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)
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.
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.
vSphere ESXi hosts and TrueNAS boxes rendered as standalone rows under
'Pulse Agent hosts' in Infrastructure settings even though no Pulse Agent
runs on them. The rows were unmanageable duplicates of their owning platform
connection: they carry no credentials, pause, or remove semantics, and they
can never attach to their vSphere/TrueNAS system because attachment requires
a shared host while the machine hostname differs from the vCenter address.
They also inflated connected-system counts.
buildConnections now skips hosts with a non-empty IntegrationSource, so the
ledger and grouped systems represent those machines solely through their
owning platform connection. Per-machine visibility is unchanged on the
vSphere/TrueNAS platform pages and Machines, which read the unified fabric
directly. Connection.integrationSource stays declared as defense-in-depth
for agent-only client workflows. No alert impact: agent-type rows were
already dropped from alert snapshots.
The connections ledger derives agent rows from the unified fabric, which
includes machines whose telemetry comes from platform integrations (vSphere
ESXi hosts, TrueNAS). Agent Doctor rendered every one as a permanent
'Unknown / no structured reason' row, while agents the ledger does not carry
(Docker-only, Kubernetes-only) were silently dropped from the fleet view.
- Expose HostView.IntegrationSource() (source-set based: only SourceAgent
ingest counts, since integration providers fabricate an Agent payload) and
plumb it through models.Host to the connections ledger as the optional
integrationSource field.
- Agent Doctor skips integration-backed connections and appends
diagnostics-only agents, honoring scope, so the doctor covers exactly the
real Pulse Agent fleet.
- Update readiness agent checks no longer count integration-backed machines
as registered agents.
- Humanize doctor copy: plain-language stale message with '10m 2s'-style
durations, offline wording without enum leakage, no 'Supported target:
Unknown' cell when no target is published, host-local command banner only
when a command is actually offered, and a compact non-zero summary strip.
Contracts updated for unified-resources, monitoring, api-contracts,
agent-lifecycle, and dependent storage-recovery; verification via
views_test.go, monitor_host_agents_test.go, state_host_test.go,
contract_test.go, and the frontend connections API test.
PVE's disks/list endpoint labels healthy SCSI/SAS drives OK while ATA
drives say PASSED, and failing ATA drives come back as FAILED! with the
bang. Pulse ingested those raw strings, so a healthy SAS drive rendered
as health Unknown even though the Proxmox UI showed S.M.A.R.T. OK. The
host agent and TrueNAS paths already normalize their health text; the
PVE ingestion was the only entry that did not.
Map OK/PASS to PASSED and FAIL-containing values to FAILED at ingestion,
and accept OK as healthy in the disk presentation layer as defense for
state produced by older servers.
Refs #1595
Contract-Neutral: behavioral fix: normalize PVE disk health strings at ingestion, no public contract delta (#1595)
smartctl reports SAS drives with device protocol SCSI, so detectDiskType
fell through to its blanket sata default, and that non-empty type also
masked the text-output transport evidence the fallback parser had
already extracted. The wrong sata label then blocked the merge layer
from promoting the smartctl serial over the SAS transport address
Proxmox reports.
Classify SCSI-protocol devices via the scsi_transport_protocol
descriptor, let the text and sysfs refinements upgrade a generic scsi
label, and apply the legacy sata default only after all evidence is
exhausted. Parse the SCSI log-page fields (power-on hours, grown defect
count, endurance used) that SCSI drives report instead of an ATA
attribute table, and let agent-reported sas replace coarse hdd/ssd/sata
types during the disk merge.
Refs #1595
Contract-Neutral: behavioral fix: SAS transport detection and SCSI attribute parsing in host agent SMART collection; no public contract delta (#1595)
Patrol's verdict contract required the model to echo finding IDs like
update-analysis-docker:<uuid>/<64-hex-digest> exactly. Smaller models
(reported with Gemini 2.5 Flash-Lite) drop the key prefix or digest
tail, every patrol_assess_finding call fails, and the whole run ends
with 'Patrol finding assessment incomplete'. Resolve an ID that
unambiguously identifies one active in-scope finding (case-insensitive,
prefix, or separator-suffix match), record the canonical ID so the
verdict accounting and duplicate guard still hold, and list the valid
active IDs in the lookup error so the model can self-correct instead of
guessing. Hallucinated or ambiguous IDs still fail closed.
Refs support report from Johannes Strasser, 2026-07-20
Contract-Neutral: behavioral fix: tolerant patrol assessment ID resolution for weak models; fail-closed contract unchanged
Agents report network interfaces sorted by name, which places docker0
and br-* bridges ahead of eth*/en* interfaces, and every consumer of
ResourceIdentity.IPAddresses treats the first entry as the host's
primary address. Docker hosts therefore showed a 172.x bridge IP in
the Machines and Docker tables instead of the machine's LAN address.
Collect physical-looking interfaces first so bridge and overlay
addresses never lead the list.
Refs #1529
Contract-Neutral: behavioral fix: order physical interface IPs before virtual bridges in identity collection; no public contract change (#1529)
vCenter answers a newer-than-supported vim25 release path with 404 on
some builds but HTTP 500 on others: 8.0.x returns 500 for the 9.0.0.0
service-content probe. The negotiation loop only continued on
not_found, so it aborted on the first probe and never tried the 8.0.3
release the server actually speaks, failing the connection test with
'vi-json service content request failed with HTTP 500'.
Continue the probe loop on generic endpoint failures as well as 404;
still abort immediately on auth, permission, TLS, and network errors,
which no release string can fix.
Fixes#1596
Contract-Neutral: behavioral fix: vSphere release negotiation falls through HTTP 500 probe responses (issue #1596); no public contract delta
An agent running inside an LXC measures /proc through the shared kernel
and reports the node's utilisation, not the container's. Because
SourceAgent outranked SourceProxmox in the metric merge, that value
overrode the hypervisor's cgroup-accounted CPU on the guest's canonical
resource, so the workloads row disagreed with the (Proxmox-sourced)
history chart by orders of magnitude on idle guests.
Demote agent-sourced utilisation metrics below the platform source when
the target resource is a hypervisor-managed guest (vm /
system-container). The freshness gate still lets a live agent cover for
a stale platform source.
Fixes#1597
Contract-Neutral: behavioral fix: demote in-guest agent utilisation below platform source on hypervisor-managed guests (issue #1597); no public contract delta
Test-only (GLM nightly grunt, round 2): new
monitored_system_projection_matchers2_branchcov0720am_test.go covering the
previously-uncovered Docker/TrueNAS/PBS/VMware per-type replacement-selector
matchers and the monitoredSystemReplacementSelectorMatches dispatcher in
internal/unifiedresources. Each per-type predicate drives its nil-source guard,
every OR-chain arm in isolation, no-match, and whitespace-only; the dispatcher
drives each DataSource route plus the unmatched arm. Complements the round-1
Agent/Proxmox/PMG/K8s coverage. No source changed; contract-neutral.
GetLatestActionAuditByOrigin and GetLatestActionAuditByOperationalRecord
were token-for-token identical except for which Origin field they matched,
tripping golangci-lint's dupl check. The scan/latest-selection/clone logic
now lives in latestActionAuditMatching(match); each getter keeps only its
input trimming and predicate.
Contract-Neutral: dupl lint dedup: extract shared latestActionAuditMatching helper, behavior identical, no public-contract delta
Contract-Neutral: The first-session agent install handoff keeps the existing API and setup contracts while binding runtime handlers to the Router-owned canonical config through startup and monitor reloads.
Contract-Neutral: RC qualification fixes preserve existing public API, tenant, monitoring, and organization contracts while correcting canonical runtime ownership and test fixtures.
Cover pure functions left at 0% after the canonical Operational Trust and
unified-resource work, surfaced by a fresh coverage probe.
- unifiedresources views.go accessors for K8sNodeView, DockerContainerView,
PodView, K8sDeploymentView, HostView, DockerHostView, K8sClusterView and
the smaller ContainerView.Pool / NodeView.IsClusterMember /
PhysicalDiskView.MetricResourceID / PBSInstanceView.Datastores /
PMGInstanceView.InstanceID accessors. Each exercises the nil-receiver and
nil-nested defensive arms, the populated projection, and slice/map
clone-independence.
- unifiedresources clone.go seven Ceph deep-clone helpers, asserting value
equality, mutation independence, and nil/empty inputs.
- agentcapabilities firstStringPayloadValue, MCPManifestPromptProjectionSupported
and JSONRPCError.Error.
- ai/tools data_types NormalizeCollections (three receivers),
ValidateCurrentResourceAvailable and ErrExecutionContextUnavailable.Error.
Test-only. Every target function moved 0% to covered. No source changes.
Contract-Neutral: test-only branch-coverage tests; no source or contract changes
GatePatrolAutonomyUpdate validates and consumes the activation
acknowledgement, then re-marshals the settings for the downstream
handler - but the marshaled body still carried acknowledgement_id.
The Pro autonomy handler decodes the body with DisallowUnknownFields
and has no such field, so every Autopilot (full mode) activation on a
Pro build failed with 400 Invalid request body. Lower autonomy levels
forward the original body untouched, which is why only full mode broke.
Clear the consumed acknowledgement before normalizing and hand it to
the downstream handler via request context only. The new regression
test strict-decodes the forwarded body with the enterprise handler's
exact field set.
Contract-Neutral: Autopilot activation gate normalization fix; endpoint contract unchanged
Test-only wave, contract-neutral. New *_branchcov0719pm_test.go files cover
previously-uncovered pure value-in/value-out helpers, each verified to move
its target functions from 0% to full coverage:
- internal/agentcontext: formatKubernetesServicePorts (empty/single/cap/overflow
arms) and addMetricFact (nil-metric, percent/value/ratio arms) now 100%.
- pkg/reporting: reportLogoTypeFromPath 0->100, reportLogoTypeFromData 28.6->100,
scaledLogoSize 70->90 (extension and aspect branches).
- internal/cloudcp/email: RenderMagicLinkEmail 0->80 (render success path; the
compile-time template-error arm is unreachable and left uncovered).
- internal/recovery: recoveryDetailString (nil map, missing key, non-string,
string arms) and recoveryPointObservedAt 40->100.
- internal/ai/tools: ErrStrictResolution/ErrRoutingMismatch ToToolResponse 100.
- internal/ai/providers: every NormalizeCollections receiver 0->100.
No source or existing test modified.
Cover pure functions the recent canonical Operational Trust and protection
posture work landed with no unit test:
- recovery/model posture: enum Valid, struct Clone/Validate/Payload and the
normalize/cloneTime/validOutcome/sortedUniqueStrings/compareProviderStates
helpers (20 funcs, all 0%->100%)
- operationaltrust contracts: EvidencePayloadRef/Acknowledgement/Suppression/
LifecycleTransition Validate plus LifecycleTransition/NotificationLink Clone
- cloudcp/proxytrust: ClientIP, ExtractRemoteIP, rightMostUntrustedForwardedIP
and IsTrustedProxyIP forwarded-header and CIDR parsing
- mockmodel: NormalizeBlendWeight, SeriesForTimestamps, seriesForProfile,
diskIOValue and flatValue deterministic seeded math
- updatesignature: DecodePrivateKey and HasTrustedPublicKeys
- recovery keys: ProxmoxPBSGuestLooseContinuityKey branch guards
- vmware SourceID and truenas availableAppLogContainers formatting
Test-only, contract-neutral. New *_branchcov0719_test.go files only; no source
or existing test touched.