Verify failed node-link persistence rolls back both the displaced owner and replacement, preserves report watermarks and reservations, and permits a durable retry on the same store. A rollback-omission negative control reproduces the assertion failure; no runtime behaviour changes.
Change-source: pulse-maintainer
Commit link and unlink journal updates before publishing state, preserve manual selections across report and provider refresh boundaries, and reserve dormant owners across restart. Re-evaluate known automatic associations using provider names while retaining legacy unknown links.
Change-source: pulse-maintainer
New-finding telemetry lost older activity once the run history reached its
100-entry cap. Persist a bounded daily finding tally with a separate
upgrade cursor, preserving run counts while backfilling retained findings.
Cover restart, repeated saves, upgrade, read failure and UTC-day retention.
Record the measurement boundary and retire the resolved coverage gap.
The customers most likely to lapse are the ones who stopped opening Pulse,
so the in-app "This week" card cannot reach them. Report schedules gain a
kind: the default resources kind is the existing PDF or CSV performance
report, and patrol_digest emails the same weekly "what Patrol did for you"
rollup the Patrol page shows, in plain language, for the whole workspace.
It reuses the existing scheduler, cadence, recipients, tenant email config,
and Pro advanced-reporting entitlement rather than adding a second
scheduler. Digest schedules are weekly and email-only; the server fixes
scope, format, and attachments, and a run with no AI service, no Patrol, or
no email destination records a last_error that names the missing piece
instead of sending an empty week. Nothing is written to disk.
The digest assembly moves from the HTTP handler into
AISettingsHandler.BuildPatrolDigest so the schedule runner and the
endpoint share one code path. docs/MSP.md documents the kind for
providers; the Settings > Reporting "Report type" selector follows in a
separate change once it has a Pro-licensed browser pass.
Forty-two percent of paying installs never get Patrol running, and the
support and issue evidence says the wall is model choice and cost: a
prospect asked which models Pulse recommends before trying, a Pro customer
picked Flash-Lite to save money and Patrol then could not file verdicts,
and a mispriced Opus row tripped the 20 USD budget so the circuit breaker
disabled Patrol with nothing but a log line (#1789).
Answer both questions at the point of choice. The Patrol and shared
default model pickers pin guided models in a "Suggested for Patrol"
section with recommended / suggested / caution markers and a one-line
reason: the Ollama preflight blessing is the only recommended entry,
Gemini Flash-Lite the only caution, and cloud starting points are labelled
price-driven and unqualified until this install's own readiness pass
upgrades them to "Verified on this install". A server-computed cost
preview sits under the model choice: monthly estimate from Pulse's price
table, the schedule, and the install's own median full run once three
priced runs exist (a measured 104,528-in / 4,491-out run otherwise), with
the assumption stated and a token explained once, plus 30-day spend
against budget. Each schedule preset is priced in the Schedule select.
When a per-token model is picked while the schedule is still the 6-hour
default, the cost model proposes the slowest preset that keeps scheduled
runs under half the budget (20 USD reference when none is set) and says
what the slower check costs in detection delay; a schedule the install
already chose is never changed.
A used-up budget is now a spending decision, not a provider fault: the
budget refusal is a typed sentinel with the figures, classified as the
budget_exhausted cause, excluded from circuit-breaker accounting, and
promoted into the Patrol block state, so the Patrol page shows "Patrol
paused" with the spend and limit and a "Raise the cost budget" action
instead of "Check Patrol model".
Two standalone Proxmox sites that reuse one short node name and one
pasted install token still folded into a single host record: the
token+hostname binding and the continuity store's hostname+token
fallback both resolve identity without consulting the machine ID, so
the second site's reports adopted the first site's host ID and the
record flip-flopped between machines. Removing that collapsed record
(the agent uninstaller does this on reinstall) then revoked the shared
token because the still-used guard could only see one record, leaving
every surviving agent rejected with 401 "Unauthorized access attempt"
on /api/agents/agent/report - the new symptom reported on #1753 after
the v6.4.2 retest.
Qualify host token binding slots with the reporting machine's identity
so two machines behind one token+hostname hold separate bindings,
refuse to adopt a bound or continuity identity whose recorded machine
ID disagrees with the report's, and rebuild bindings machine-qualified
after token reloads. Reports without a machine ID keep the legacy
binding shape and same-machine reinstalls keep their stable IDs.
Regression coverage: a state-layer test proving two shared-token pve01
machines keep distinct stable identities, continuity-match tests for
the machine guard, and an end-to-end router test walking the reporter's
estate - both install patterns, alternating reports, uninstall and
reinstall - asserting report auth never breaks and no token is revoked
while its sibling site still uses it.
Persist canonical token IDs, scopes, and organization bindings before admitting them to startup or watcher state. Fail configuration load rather than weakening token-only authentication when credential storage cannot be read or migrated.
Contract-Neutral: hardens credential migration failure handling without changing successful API payloads or extension contracts
Serialize API-token disk reload snapshots with live token mutations so a stale watcher read cannot overwrite a newer rename or revoke. Add deterministic rename and revoke race coverage and record the governed concurrency boundary.
Change-source: pulse-maintainer
Telemetry could see only saved connections, so an install that tried to
reach a node and could not was indistinguishable from one that never
opened the add-node dialog. Both report zero configured connections and
stall at the same activation stage. Fleet data shows that population is
real and concentrated three to one in container deployments, and nothing
recorded whether those installs attempted a connection at all.
Record node connection test attempts and failures in a bounded,
day-bucketed tally in the config directory, pruned to a 31-day retention
window, and report both over the install-ID rotation window as
node_test_attempts_30d and node_test_failures_30d.
Recording starts only once a request carries a target and credentials, so
an incomplete form is never counted as a node that could not be reached.
A host string that turns out to be unusable does count, because the
attempt was made and it failed. Only the add-node dialog endpoint is
instrumented: instrumenting the unused test-config endpoint as well would
double-count a single operator action.
The tally holds counts alone. Hosts, credentials, and error text never
enter it, which is why it is plain JSON rather than encrypted history.
LoadAIFindings and LoadAIChatSessions swallow parse failures by returning
empty data with a nil error, and an undecryptable file lands in the same
branch because a failed Decrypt falls back to parsing the ciphertext as
JSON. Every read-modify-write saver (SaveAIFindingsWithSuppression,
SaveAIChatSession, DeleteAIChatSession, CleanupOldAIChatSessions) then
sees "empty, no error" and rewrites the file, permanently destroying
whatever it still held — user-authored suppression rules and chat
conversations. 9038afc68 fixed the read-error variant of this clobber;
this is the parse/decrypt variant that bypassed that abort because the
loader hid the failure.
Policy is preserve-for-recovery: the loader moves the unreadable file
aside to <file>.corrupt-<timestamp> and logs a warning before returning
empty data, keeping the self-healing behavior without the silent data
destruction. If the move itself fails, the load now fails, so the savers
abort instead of rewriting. recordActivityHistoryLocked keeps its
reset-on-parse-error behavior — those files are content-free telemetry
markers.
Tests cover corrupt and wrong-key-encrypted findings/session files being
preserved across the previously clobbering saves, and the move-failure
path aborting every saver while leaving the corrupt file intact.
SaveAIFindingsWithSuppression and SavePatrolRunHistory read the existing
file to carry data forward (suppression rules, the daily run tally) and
treated a failed read as nothing to preserve, rewriting the file without
it. Same clobber mechanic as the system.json reset fixed in cf5b86e62.
Both loaders return empty data with no error for a missing file, so an
error at these sites is a real read failure.
Suppression rules are user-authored config, so that save now aborts and
returns the read error. The run tally is telemetry, so that save
proceeds but logs a warning that the tally restarts. Tests cover the
abort leaving the file intact, explicit rules saving despite a failing
read, and the tally warning plus restart.
A boot-time LoadSystemSettings error fell into the missing-file branch:
no log line, every persisted system setting reverted to defaults for
the run, and a default system.json was written over the file on disk.
Observed once in practice (2026-08-20): a restart came up with a
persisted toggle unset while system.json still held the correct value.
Distinguish the error from the genuinely-missing file: retry the read
once (transient failures at boot were the observed shape), then warn
with the error and file path and run on defaults without touching
system.json. Tests cover the warning, the untouched file, and the
retry-once behavior.
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).
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.
* Honor the configured request timeout for discovery AI analysis
Both discovery services accepted an AIAnalysisTimeout config field, but
neither production construction site ever set it, so every AI analysis
call ran under the hardcoded 45s fallback. RequestTimeoutSeconds reached
the provider HTTP client but never the per-analysis context deadline, so
raising it could not help a slow local model: the outer 45s deadline
always fired first and surfaced "AI analysis timed out after 45s".
Add GetDiscoveryAIAnalysisTimeout, which returns the configured request
timeout when one is set and keeps the tighter 45s discovery default
otherwise, and wire it into both service constructors. Both services also
gain SetAIAnalysisTimeout so a settings change applies live instead of
requiring a restart; the timeout is now read through a mutex-guarded
accessor since it can be mutated while scans are running.
* Inherit the configured request timeout for discovery analysis
Returning 45 seconds when RequestTimeoutSeconds is zero left the
discovery deadline disagreeing with what operators are shown. Zero is
omitted from the settings response, the frontend displays 300 via
request_timeout_seconds ?? 300 and states that value applies to
discovery, and it only submits the field when the form value differs
from the displayed current value. An installation showing 300 seconds
therefore still timed out discovery after 45, and saving the visible
default did not correct it.
GetDiscoveryAIAnalysisTimeout now delegates to GetRequestTimeout so the
persisted default canonicalizes to 300 seconds. The discovery services
keep their own 45-second fallback for construction without an AI
configuration.
Configuring PROXY_AUTH_ROLE_HEADER without PROXY_AUTH_ADMIN_ROLE now gates
admin access on an exact-case match against the "admin" default. That closes
the fail-open, but it also means an IdP sending "Admins", "Admin", or
"authentik Admins" grants nobody admin — and a proxy-auth-only deployment has
no local credential to fall back on. The change is recoverable by setting
PROXY_AUTH_ADMIN_ROLE and restarting, but only if the operator can tell that
is what happened.
Log a startup warning naming the header, the effective admin role, the
case-sensitivity, and both ways out. The warning is scoped to deployments
that actually configured a role header; defaulting the value with no role
header is inert and stays at info. The troubleshooting entry in
docs/PROXY_AUTH.md now leads with the exact-match rule, since that is where
a locked-out operator looks first.
A reverse-proxy deployment that set PROXY_AUTH_ROLE_HEADER without also
setting PROXY_AUTH_ADMIN_ROLE granted every proxy-authenticated user full
administrator access. CheckProxyAuth only evaluated roles when both values
were non-empty, so the half-configuration skipped role gating entirely and
returned isAdmin=true. docs/PROXY_AUTH.md has always documented an `admin`
default for that variable, but the Config struct's envconfig `default` tags
are legacy and never applied (config.go), so nothing ever populated it.
CheckProxyAuth is the single admin verdict all 20+ proxy-auth gates consume,
so the fail-open reached every one of them. Verified on a scratch instance
with PROXY_AUTH_ROLE_HEADER set and no admin role: a request carrying only
`X-Proxy-Roles: user` received HTTP 200 and the full admin payload from
GET /api/system/settings, HTTP 200 from POST /api/system/settings/update,
and proxyAuthIsAdmin=true from /api/security/status. All three now return
403 / false, while `X-Proxy-Roles: admin` still passes.
Resolve the documented default in both layers that can produce the verdict:
config load populates ProxyAuthAdminRole when proxy auth is configured, and
CheckProxyAuth now keys role gating on the role header alone, resolving an
empty admin role through config.DefaultProxyAuthAdminRole. Configuring a
role header is the operator's signal that admin access is role-gated;
leaving the admin role unset must not switch that off.
Deployments that intentionally treat every proxied user as an admin are
unaffected: that is still expressed by leaving the role header unset.
The per-guest Backup/Snapshot toggle persisted a full copy of the current
global defaults just to flip enabled, freezing the threshold values into
the override. Later global edits (a 32-day backup warning) then silently
never applied to toggled guests, which kept firing at the frozen factory
7-day warning. Reported twice in discussion #1126.
- Guest overrides now resolve against the globals at evaluation time:
zero-valued fields inherit the global value, explicit values still win,
and hand-written sparse overrides stop decoding as accidental zeros.
- The toggles write enabled-only overrides instead of freezing a copy.
- Normalization rewrites stored overrides whose thresholds exactly match
the current globals into sparse form, which is behavior-preserving at
migration time and un-freezes existing installs.
- The Backups/Snapshots global editors reconcile warning/critical pairs
by adjusting the untouched field, so typing a 32-day warning no longer
silently snaps back to the 14-day critical default.
Refs #1126
Agent install and update commands advertised http://<LAN-IP>:7655 on
installs where the operator browses Pulse over an https FQDN, because
boot-time detection pre-fills config.PublicURL with an IP guess and
resolvePublicURL treated that guess as configuration. Track
auto-detected values (boot probe or first-request capture) separately:
explicit config from the env var or the settings UI still wins, but an
auto-detected guess now yields to the origin of the admin request that
is asking, so copied commands match how the instance is actually
reached.
Also stop boot detection from clobbering a settings-persisted public
URL on restart, which silently reverted the operator's saved value
whenever PULSE_PUBLIC_URL was unset.
Refs #1692
Contract-Neutral: behavioral fix to advertised-URL derivation: no API shape, field, or endpoint changes; PublicURLAutoDetected is internal (json:-)
persistGuestIdentity spawned a detached goroutine per changed guest to write
guest_metadata.json, with a comment noting it avoided blocking the monitor.
Nothing tracked those goroutines, so neither Monitor.Stop nor
MultiTenantMonitor.Stop could wait for them and a queued write could land after
shutdown. In hosted mode that means a write into a tenant directory that
offboarding is already removing, and a stray guest_metadata.json.tmp left
behind when the atomic write is interrupted.
The store now owns the goroutine. SetAsync tracks the write on a WaitGroup and
WaitForPendingWrites drains it under a bounded timeout matching
tenantMonitorShutdownTimeout, so a wedged store cannot hold up tenant teardown.
Monitor.Stop drains before closing the metrics store.
This is what made TestHostedTenantAgentInstallTokenCannotReportToOtherTenant
flaky: t.TempDir cleanup raced a queued write into orgs/client-b and failed
with "directory not empty". The test itself is unchanged, because it was never
a test bug. A goroutine dump at cleanup time showed the writers still live,
created by persistGuestIdentity, blocked on the store mutex.
Verified causally rather than by observation alone: the target test fails 0/4
with the drain removed and passes 8/8 with it, against 2/3 failures on the
unmodified baseline. The regression tests fail if SetAsync stops tracking its
goroutine.
Note for a future pass, deliberately not changed here: each changed guest still
triggers a full-file save, so one poll cycle over N changed guests does N
marshals and N atomic writes that serialize on the store mutex anyway. Fixing
that means coalescing at the call site and is a behavioural change beyond this
defect.
Schema v6 shipped audit_logging_persistent and audit_events_30d as Pro adoption
signals. Neither discriminated. pkg/server installs the SQLite audit logger on
every install for defense in depth and gates only the read/export endpoints, so
the boolean was true on all 8 installs that had taken rc.8 and 0 rows in the
retained table have ever had it false. The event count measured that background
write volume: three of those eight unlicensed community installs were pegged at
the receiver's 100000 clamp ceiling, with the rest between 4863 and 67509.
Schema v7 replaces both with audit_reads_30d, a count of requests that cleared
the license gate on an audit read or export surface. A read requires a human
action, so unlike store presence or write volume it cannot settle into a
constant. The recorder is wrapped INSIDE RequireLicenseFeature so unentitled
requests never count, and the persisted marker carries a timestamp and a coarse
activity class from a fixed allowlist. Query filters, actors, ranges, and every
audit row read stay on the install.
The retired columns are left in the live database. They hold real rc.8 rows and
migrations only add, so dropping them would be a pointless risk; nothing writes
them once the receiver struct loses the fields.
Adds the guard this class needed. LicensedFeatureAdoptionFields registers every
field that exists to measure licensed-feature adoption, and
TestLicensedFeatureAdoptionFieldsDiscriminate builds an unused install through
the real production snapshot paths, installs a real SQLite audit logger exactly
as pkg/server does, records a baseline audit event, and fails if any registered
field is non-zero. Pinning a console logger there would have made the guard pass
while the payload lied, so it deliberately does not. The guard was verified by
reintroducing the v6 sourcing and confirming it fails with the field named.
A companion test pins the three retired fields so they cannot return under
their old names.
This is the third instance of one bug class. v6 removed
pulse_intelligence_patrol_autofixes_30d, hardcoded to zero with no increment
site, and then introduced two fields that were constant in the other direction.
Three occurrences is a guard, not a habit.
Verified end to end on a running unlicensed install: the payload that reported
audit_logging_persistent true under v6 now reports audit_reads_30d 0, and
seeding two in-window reads, one outside the window, and one with an invalid
activity class yields 2.
Six of the eight Pro-exclusive features had no telemetry field at all, so
there was no way to answer whether RBAC, audit logging, scheduled reporting,
agent profiles, alert-triggered AI, or Kubernetes AI were being used by the
installs paying for them. Schema v6 adds nine content-free adoption signals:
alert_ai_enabled AIConfig.IsAlertTriggeredAnalysisEnabled()
rbac_custom_roles non-built-in roles, per org
rbac_user_assignments user-to-role assignments, per org
audit_logging_persistent a persistent audit store is active, not console
audit_events_30d audit events retained inside the window
report_schedules configured scheduled reports
report_schedules_enabled scheduled reports switched on
report_schedules_run_30d schedules whose last run falls inside the window
agent_profiles configured agent profiles
Counts only. Role names, permissions, usernames, schedule names, delivery
recipients, report scope, profile names, and every audit event field stay on
the install. kubernetes_ai needs no field of its own: it is derivable at read
time from alert_ai_enabled combined with the existing kubernetes_clusters
count, and a dedicated field would be redundant.
Config-sourced signals are read through applyLicensedFeatureConfigSnapshot;
RBAC and audit live behind the router and are read through
Router.ApplyLicensedFeatureTelemetrySnapshot. The RBAC read goes through a new
TenantRBACProvider.PeekManager so a background telemetry read can never
provision an RBAC store for an org that has never used RBAC.
Also removes pulse_intelligence_patrol_autofixes_30d and the AutoFixCount
field behind it. patrol_run.go hardcoded AutoFixCount to 0 and no increment
site existed anywhere in the tree, so the counter was zero in all 233,364
retained production pings. That was a wiring bug, not evidence that nobody
uses Patrol fixes; governed fixes are delivered through the approved-action
pipeline, which is already instrumented. The field was plumbed through run
records, history persistence, the Assistant handoff, and telemetry while being
structurally incapable of holding a non-zero value.
Verified end to end against a running install rather than only in unit tests,
which is precisely the check the autofix counter never had: seeding three
report schedules (two enabled, one last run inside the window) and two agent
profiles produced report_schedules 3, report_schedules_enabled 2,
report_schedules_run_30d 1, agent_profiles 2 in the Settings telemetry
preview, and signing in moved audit_events_30d to 1.
The private receiver landed first in pulse-pro 78ff7dd so the new fields are
accepted on arrival.
Guest metadata (web interface URL, tags, notes) is keyed by the
node-scoped guest ID, so a VM or container migrating to another node in
the same cluster orphaned everything attached to it (#1669). VMIDs are
unique within a Proxmox cluster, so when the exact ID and the legacy
formats all miss, an entry for the same instance and VMID under a
different node is the same guest and migrates to the new node-scoped ID.
The explicit availability check link stores the hashed canonical
resource ID, which also embeds the node; that half needs a
node-independent canonical guest identity and is tracked separately.
Contract-Neutral: guest metadata follows node migration; behavioral bug fix, no wire contract or payload change
Found while triaging #1643 and #1637: both fields were stored via
system.json and accepted, validated, and echoed by the settings API,
but nothing ever consumed them. The unattended update schedule is owned
entirely by the systemd timer install.sh renders, and no UI control ever
set the fields, so the API persisted a schedule preference that could
never take effect.
Remove the fields from config.Config, SystemSettings, the settings
handlers, and the frontend config type, along with the interval
validation and the .env AUTO_UPDATE_CHECK_INTERVAL rewrite (a legacy
line is now preserved verbatim). Legacy clients that still send the
keys get them silently ignored instead of validated, and a system.json
written before the removal still loads cleanly - both behaviors are
pinned by new tests. User docs no longer describe the phantom schedule
settings.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Availability targets gain an optional probe agent assignment. Setting
it requires the external_probe entitlement, enforced only at the
moment of assignment - local targets never consult the license path.
Assigned targets are delivered to their agent through the signed
agent-config channel, skipped by the local poller, and resume local
execution automatically if the entitlement lapses. Probe-reported
results are accepted only from the currently assigned agent, share
the local failure-threshold accounting, carry source attribution,
and derive to indeterminate at read time when reports go stale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two clusters at different sites that reuse the same corosync cluster
name and the same RFC1918 addressing presented colliding member IPs,
which ConsolidatePVEInstances took as strong endpoint overlap and folded
the second site's connection into the first. The standalone-into-cluster
merge had the same hole for a standalone whose address collides with a
cluster endpoint at another site. The v6.1.1 node-aggregation guard
never fires in this case because it keys on cluster-name conflict, and
same-name clusters do not conflict.
Address coincidence is weak evidence across sites, but the TOFU-captured
TLS certificate fingerprints already stored on instances and cluster
endpoints are strong evidence: contradicting fingerprints for the same
authority, node name, or endpoint address mean different machines.
Consolidation now refuses to merge in that case. The fail-safe direction
is deliberate - a certificate rotation may leave a genuinely duplicated
cluster as two views, but two distinct clusters are never silently
folded into one.
Reported via support by an MSP whose two customer clusters kept merging
after the v6.1.1 aggregation fix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Seventeen files closing the zero-coverage functions the current source drop
left behind. Every named target was measured off 0.0 percent by a per-function
coverage delta, re-measured against current main.
- config: the five new durable Proxmox cluster-node identity helpers
(deterministic id, endpoint equality, alias lookup, id existence, lookup by
id) to 100 percent; VMware and agent-profile persistence round-trips under
t.TempDir including that AppendProfileChangeLog appends rather than
replaces; AI chat session save, load, delete, per-user scoping and age
cleanup, with explicit timestamps rather than time.Now-relative fuzz;
PVEInstance.DeepCopy asserted for nested independence.
- truenas: incidentFromPoolStatus over every pool health string,
RecordsFromSnapshot over nil, empty and populated snapshots, both
TransportStatus accessors, and the RPC handshake and auth typed errors
through errors.Is and errors.As.
- unifiedresources: the maintenance-window operator-state lifecycle on
MemoryStore including the not-found and already-cleared arms, plus the
four remaining View accessors asserted on their exact formatted output.
- api: restoreAgentExecMetadata, buildAlertConnectionSnapshotsWithRuntimeSources
and both mock series generators, asserted on shape, ordering and
determinism rather than non-emptiness.
- cmd/pulse-control-plane: the four remaining MSP and mobile proof report
printers, asserted on the concrete strings in captured stdout.
- ai: cost.EmptySummary, approval.emptyExecutionState, demo.IsDemoRuntimeIntended
and tools.findCanonicalAppContainerResourceByReferences across no-match,
first-match, later-match and ambiguous references.
- monitoring, models, alerts: trueNASAppRunning,
supplementalProviderOwnedSourcesForOrg, IOCounterPresence.Effective,
ValidAlertIntentSignal and intentTimePointer.
No source file is modified. Adversarial review returned no rejects across all
seventeen files and flagged four padding cases plus one dead table field; all
were removed and the per-function coverage re-measured as identical, proving
they carried nothing.
PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT=test-only branch coverage, no source or contract change
Preserve tenant-scoped metadata through partial URL updates and project stable URLs across runtime identities. Use a safe adjacent launch control across overview tables with desktop and mobile regression coverage.