Script smoke still asserted the removed grouped runner while native
verification used a separate suite. Share the canonical execution controls
so both entrypoints reject lost checks, false passes and changed deadlines.
Preserve selected memory freshness and the VM/LXC interpretation.
Retain both native tool contracts and current API concurrency controls.
Qualify this source separately from release and installed recovery.
Contract-Neutral: Integrate already-governed parents without a new contract change. Incoming verification files already match main. Fresh integration tests and source-bound browser proof cover the combined memory and installer surfaces.
Require the existing troubleshooting jobs and their destinations without
making additional help entries break unrelated safety checks. Keep the
opening backup warning scoped to its actual entry point.
Run closed service and middleware models through the containing fixture's
selected Python interpreter without site startup. Retain every safety
scenario and deadline while qualifying the pending maintenance source.
Refs #2888
Contract-Neutral: Test-only navigation assertions and confined model interpreter selection. Production behavior and safety deadlines are unchanged.
A scheduled retry across UTC days can generate another artifact without
completing a new successful reporting period. Keep multi-day generation as
an activity observation rather than inferred repeat use, while retaining
successful distinct-period counts as their own signal.
Exercise both cross-day generation without success and multiple successful
periods on one day so the adoption summary cannot conflate these metrics.
Record successful response writes, scheduled generation, file saves and SMTP
outcomes in a durable content-free tally. Distinct active days include manual
exports, while saved-schedule periods deduplicate retries and monthly reuse.
Publish bounded scalars through the existing opt-out-controlled heartbeat and
report missing coverage explicitly. Preserve pricing and customer entitlements.
The route inventory treated RequireOrgAdmin routes as unauthenticated
"bare" routes, and the agent diagnostics contract and the API
reference anchors still named RequireAdmin for routes that now opt
into organization authority. The security-privacy contract still said
org managers receive reportingRead through ensureAdminSession; it now
names the org-bound routes' check and records that instance
administration refuses organization owners. The agent-lifecycle
contract notes that agent routes declare that authority explicitly.
Commit a5baa7563b stopped Patrol reading cache-inclusive Proxmox memory
as guest pressure (issue #2762, a QEMU VM). It also put
`cluster-resources` in that cache-inclusive group for every guest type.
An LXC without a linked Pulse agent only ever gets `cluster-resources`,
so Patrol dropped memory pressure for all agentless containers:
- a container at 97% of its limit got no triage flag, forecast or
anomaly
- the seed said "guest pressure unknown; may include reclaimable cache"
- older memory alerts went to a billed model review on every run, and
any resolve verdict was thrown away
**That premise is wrong for LXC.** On both PVE 9 (`master`) and PVE 8
(`stable-bookworm` / `stable-8`), Proxmox subtracts page cache before
reporting a container's memory:
- `pve-common` `src/PVE/CGroup.pm` `get_memory_stat`: `$res->{mem} =
$mem - $stat->{file};` on cgroup v2 (`memory.current`), and `$res->{mem}
= $mem - $stat->{total_cache};` on v1 (`memory.usage_in_bytes`).
- `pve-container` `src/PVE/LXC.pm` `vmstatus` stores that value with
`$d->{mem} = int($mem->{mem});`. `PVE::LXC::CGroup` inherits
`get_memory_stat` from `PVE::CGroup` without overriding it.
- `pve-manager` `pvestatd` `update_lxc_status` writes `$d->{mem}` into
RRD field 8. `/cluster/resources` (`PVE/API2/Cluster.pm` →
`PVE::API2Tools::extract_vm_stats`) returns `$entry->{mem} = ($d->[8] ||
0) + 0;`.
A QEMU guest's listing `mem` is the host's view of guest RAM, which does
include the guest's cache, so it stays pressure-unknown.
**Change**
- `unifiedresources.QualifyGuestMemoryForKind` now takes the guest kind:
other platform, Proxmox VM or Proxmox container.
- A current, dated `cluster-resources` reading counts as known pressure
for a Proxmox container and stays cache-inclusive and unknown for QEMU.
- `status-mem`, `status-freemem` and `derived-total-minus-used` are
unchanged for every guest type.
- `GuestMemoryEvidenceForResource` takes the kind from the resource
type, so the Patrol inventory, the observer runtime and AI query
projections agree.
- `QualifyGuestMemory(bool)` keeps the conservative QEMU reading for
callers that don't know the guest type.
- The wrong comments in `calculateLXCMemory` and the LXC monitor tests
are corrected.
- Everything else in a5baa7563b still applies: retained, undated,
unknown and unavailable readings still cannot establish pressure or
recovery.
**Tests**
- `TestPatrolAgentlessLXCListingMemoryIsPressure` (new) builds an LXC
and a QEMU guest at 97%, both on `cluster-resources`, through legacy and
canonical read state. The LXC gets a memory triage flag and alert review
reads its value; the QEMU guest gets neither.
- `TestGuestMemoryClusterResourcesIsTypeAware` (new) covers both guest
kinds, retained readings, the other cache-inclusive sources and the
resource-type mapping.
- The `cluster-cache` fixture in `TestPatrolGuestMemoryEvidence` now
expects pressure for the container variant. It previously encoded the
wrong behaviour.
- Against origin/main's production files (`go test -overlay`), the new
and updated Patrol tests fail; with this change they pass.
Refs #2762
Supersedes #3011, which failed the documentation recipe test that pinned
the old `QualifyGuestMemory(` call in Patrol; this commit also updates
that pin to `QualifyGuestMemoryForKind(`.
Patrol treated every guest's cluster-resources memory as cache-inclusive,
so an LXC without a linked Pulse agent, which only ever gets that source,
lost memory pressure entirely: no triage flag, forecast or anomaly at 97%
of its limit, a "may include reclaimable cache" caveat in the seed, and
older memory alerts sent to model review every run with any resolve
verdict discarded.
The premise holds for QEMU but not for LXC. Proxmox's container mem is
cgroup usage with page cache already subtracted: PVE::CGroup's
get_memory_stat returns memory.current minus memory.stat file (cgroup v2)
or usage_in_bytes minus total_cache (v1), PVE::LXC::vmstatus reports it
as mem, and pvestatd feeds that value to the RRD that /cluster/resources
reads. A QEMU guest's listing mem is the host's view of guest RAM and
does count the guest's cache.
Qualification now takes the guest kind. A current, dated cluster-resources
reading establishes pressure for a Proxmox container and stays unknown for
QEMU; status-mem, status-freemem and derived-total-minus-used stay
cache-inclusive for both, and retained readings still cannot prove
pressure. The typed read state derives the kind from the resource type,
so Patrol, the observer and AI queries agree. The calculateLXCMemory
comment that called the listing value cache-inclusive is corrected.
Contract-Neutral: monitoring change is a corrected comment in calculateLXCMemory and its tests; LXC memory collection and sources are unchanged
The server installer downloaded the release archive and extracted it
under /tmp. Debian 13 and many LXC templates mount /tmp as tmpfs, whose
pages are charged to the container's memory cgroup. A v6.5.0 archive is
~280 MB and extracts to ~750 MB, so an unattended update on a 1 GiB LXC
filled the cgroup with ~820 MB of shmem; the kernel OOM-killed the
container's systemd and Proxmox stopped the container mid-update.
Download and extract into owner-only hidden directories under the
install directory instead. It is always writable from the hardened
pulse-update.service sandbox, it is where the staged binary already
lives, and the disk headroom preflight now measures the filesystem the
update actually stages on. The Go updater already prefers its data
directory over /tmp for the same reason.
Unlike /tmp, the install filesystem keeps an interrupted run's staging
across reboots. Staging setup now removes stale staging directories that
match only the exact owner-only names, never symlinks or files, and only
once their ctime is over six hours old: install.sh has no update lock,
so age is what proves no live run still owns them. Every recursive
pulse chown of the install directory now goes through one helper that
skips staging, so the payload root still reads is never handed to the
service account and the ~750 MB tree is not rewritten for nothing.
The "Failed to print table: Broken pipe" line in the report was
systemctl list-unit-files | grep -q in v6.5.0's service discovery; main
already reads that inventory in full, and it was not the failure.
Contract-Neutral: Defect fix inside the existing private server-installer inputs and update-space boundaries; staging moves from tmpfs-prone /tmp to the install filesystem with no public contract change
Measure delivered extended history before changing free and paid features.
Count only successful responses containing an older metric point and retain
only bounded daily totals. Expose the rolling sum through schema v19 and
Preview with matching privacy disclosure and explicit report coverage.
Explain the existing server, agent report and target displays, mutable image references and interrupted-upgrade chronology. Link the guidance from troubleshooting and Getting Help; retain unknowns and avoid privileged/private diagnostic exports.
Contract-Neutral: Documentation and source-linked help checks only; no runtime, protocol or permissions change.
Change-source: pulse-maintainer
Distinguish configured placeholders from assigned failures, explain online parity counts, and preserve existing evidence without array changes or another retest.
Contract-Neutral: Documentation-only guidance for existing Unraid collection and alert semantics; no runtime, wire, access or trust contract changes.
Change-source: pulse-maintainer
The mutable version correctly stops at the existing full-SHA validation before the reviewed-runtime allowlist. Assert that actual rejection reason; retain the retired and arbitrary full-SHA negative controls without changing enforcement.
Change-source: pulse-maintainer
Own PR2936's stale pin and download-admission failures at b7e774d4. Refresh all current workflow consumers, including the newer pre-token issue-label job, with their exact static expectations and lifecycle/deployment contracts. Preserve Node 24, scoped lockfile caching, credential isolation and artifact identity/retention; reject old, mutable and arbitrary download pins.
Contract-Neutral: Immutable Actions pin refresh and coupled consumer/trust expectations only; workflow inputs, permissions, cache and credential controls, artifact identities and application/runtime contracts are unchanged.
Change-source: pulse-maintainer
Distinguish required and active members from spare-inclusive totals using the supplied clean-array case. Preserve genuine loss/recovery warnings, private evidence and unknown observations. Link the existing guide from symptom selection and keep its shipped copy identical.
Contract-Neutral: Existing troubleshooting guidance and its safety checks only; no runtime, storage policy, API, permission, dependency or navigation-surface change.
Change-source: pulse-maintainer
Keep stderr and trailing blank records in service-state and running-PID admission. Incomplete reads must not authorise installation, restart, rollback or update success. Require production caller and real-process controls for both read boundaries; preserve existing recovery, trust and deadlines.
Change-source: pulse-maintainer
Run the locked parser and silent triage policy on their complete input changes in a credential-free Build and Test job. Retain issue-event pre-token checks, existing runtime and strict dependency gates; exercise admission and original install/test failure exits.
Change-source: pulse-maintainer
Keep the original failed transaction command distinct from any later cleanup command. Retain fixed operation labels and require portable controls for command, timeout, assertion, cleanup-only and successful receipts, without changing update transactions or disclosing command output.
Change-source: pulse-maintainer
Current exact-head CI exposes outdated PBS proof inventory, omitted bounded-download helper extraction, closed transport rejecting the production HTTPS flags, and Darwin's truncated comm column in Linux NAS fixtures. Preserve exact transport and process ownership, restoration and migration safety assertions while binding the controls to the actual producer.
Contract-Neutral: Test and offline CI-fixture alignment only; no installed runtime, trust, credentials, deadlines, source safety or public contract changes.
Change-source: pulse-maintainer
The legacy grant-pct path deliberately permits only list and df outside the typed helper. Keep that distinction instead of calling every fallback a full-trust collector, and describe configured mounts rather than promising every mounted filesystem.
Contract-Neutral: Guidance wording only; no telemetry, privilege, API, installer or availability contract delta.
Change-source: pulse-maintainer
The deployment table still calls the typed helper a pct reader, while the implementation uses socket-free discovery and command fallback cannot recover within PrivateNetwork. Link the deployment choice, collector help and support matrix to one shipped explanation of partial, unavailable and older-helper readings, without recommending privilege or state changes.
Contract-Neutral: Guidance and consuming-proof coverage only; no telemetry, privilege, API, installer or availability contract delta.
Change-source: pulse-maintainer
Main Secret Scan job 115618327497 on 90a8b890 passed after scanning zero commits and bytes with --log-opts=-1. Bind the exact checkout, scan the complete introduced commit range plus first-parent merge resolution, and use full reachable history when the incremental base is unavailable or divergent. Preserve the pinned detector, checksum, redaction, fatal findings, ignore fingerprints and unconditional per-push admission. Real Git fixtures cover earlier removed bytes, merge-only changes, complete-history fallback and failure exits.
Contract-Neutral: Restore existing per-push secret-scanning coverage; detector rules, security exceptions, permissions, application and deployment contracts are unchanged.
Change-source: pulse-maintainer
Classify the complete tree diff before builds or browser setup. Only nonempty top-level browser receipt JSON changes are exempt; mixed source, graphs, controls, docs and unavailable diffs keep ordinary runtime checks. Keep per-push secret scanning, independent canonical receipt validation and a strict E2E verdict which never calls skipped tests passing. Exercise real Git ranges and both workflow admission/result paths.
Contract-Neutral: Only browser-evidence-only tree diffs omit redundant runtime CI; application, dependency, trust and required source-proof contracts are unchanged.
Change-source: pulse-maintainer
Keep the negative, partial-repair and nested-resolution controls beside the actual locked-graph check so future edits cannot silently disable the intended safeguard.
Contract-Neutral: Run the existing serialization regression suite in its pre-install CI check; no additional contract or user-visible runtime change.
Change-source: pulse-maintainer
Bind the existing qualified security override to its exact consumer and serializer pair instead of letting npm overrides silently carry it into a newer Solid runtime. Resolve nested lock edges, retain the strict complete audit and security floors, and cover the two stale dependencies actually present in PR 2759 with positive, partial-repair and nested-resolution controls.
Change-source: pulse-maintainer
Additive upstream composition retired the visible N/A caption and retains Usage unavailable plus actionable permission and agent errors. Assert the actual documented states without weakening producer, expiry, keyboard, phone, same-guest or safe-recovery checks. The first five-test fixture run failed on its obsolete N/A requirement; all five corrected controls pass.
Contract-Neutral: Documentation regression labels only; runtime, guidance, provenance and recovery contracts are unchanged.
Change-source: pulse-maintainer
Preserve accepted 0efe3f7a12 and exact upstream ebfdc9d669 additively. Keep quiet unavailable captions, keyboard and touch reading explanations, stopped-agent provenance and primary CPU readings. Preserve stable denied-update help outside Details and all regression assertions.
Change-source: pulse-maintainer
Give each installer and signature transfer a five-second connection and sixty-second total budget, ignore ambient curl settings and preserve HTTPS through redirects. Failed or partial downloads leave the prior install, service and identity unchanged; pinned signature admission and recovery deadlines are unchanged.
Change-source: pulse-maintainer
The Workloads table put a second line under memory and disk cells on
every stopped guest ("N/A / Unavailable", "- / Unavailable"), on every
agentless VM's disk, and "May include cache" under every agentless VM's
memory bar, even at 20%. None of them said why or what to do, and they
broke the row rhythm.
Memory that cannot be read now renders the same dash as an unreadable
disk, with its reason in the cell title, the sparkline's hover title
and assistive text. Disk shows a visible label only for a retained
reading (Last known) or a read the user can fix (No permission, Agent
error). The cache caption appears only when the guest's own memory is
at its warning or critical threshold, the figure that colours the bar
in host-share mode too, which is the case #2762 and #2639 were about;
below that the caveat stays in the title and assistive text.
An unavailable memory explanation names what is known instead of
"Unknown source": a powered-off guest reads "Unavailable while the
guest is powered off." MetricMiniSparkline gains an optional tooltip
so the chart covering the cell can carry that reason without changing
its accessible name.
The copyable healthcheck previously also imposed a 256 MB memory limit and half a CPU, without workload qualification. Preserve the check, separate sizing from health and repair, and retain existing deployment policies. Link the bounded read-only interpretation and cover the shipped document and actual Docs consumer.
Contract-Neutral: Existing documentation and offline consuming fixtures only. No runtime defaults, deployment policy, API, retention, polling, resource enforcement or subsystem contract changes.
Change-source: pulse-maintainer
Retain the exact Delivery commit and its complete source-el4dto5p proof. The CI-only partition preserves all checks and deadlines; native recovery and upstream drawer reconciliation remain separate owning-lane work.
Change-source: pulse-maintainer
Move the existing generated watchdog and collector contract to an early, fail-closed native CI phase. Discover the complete inventory in both phases, execute every check exactly once, and preserve the remaining group assignments, native runtime tools and safety deadlines. Exercise phase admission, omission, duplicate and failure controls.
Contract-Neutral: Native CI phase ordering and inventory partition only; production installers, runtime behaviour, required checks and safety deadlines are unchanged.
Change-source: pulse-maintainer
Preserve the three exact PI commits and retained real-curl plus consuming Docs proof. The change disables curl URL globbing in the existing copied helper; API runtime and authentication contracts are unchanged.
Change-source: pulse-maintainer
The private-response helper promises one request, but real curl expands quoted brace lists and bracket ranges into two or three requests, including POST, PUT and DELETE. Disable URL globbing without changing token-file transport, response privacy, scopes, TLS, deadlines or no-retry rules. Explain the literal-path and separate URL-encoding boundary.
Exercise the copied helper with both authentication headers, every supported method, encoded identifiers and adverse write responses. Add consuming desktop and narrow Docs/router verification.
Contract-Neutral: Documentation and synthetic recipe/browser checks only; API authentication, endpoint and runtime contracts are unchanged.
Change-source: pulse-maintainer
Accept exact Delivery candidate 00f0cbeb56 with retained complete documentation proof and consuming desktop/phone browser evidence. Preserve current main proxy-auth safety changes; no native QGA recovery or human PR delivery is claimed.
Change-source: pulse-maintainer
The guest-reading docs control required a literal Unavailable caption even though the reading state and its explanation are the contract. Keep retained labels, disk-specific title/assistive wiring and unavailable producer checks; negative controls reject lost reasons while permitting caption-only changes. Guide keyboard and phone users through the existing reading indicator and same-guest details without treating a dash as an agent-installation diagnosis. Add a consuming current-source Docs browser check. No human PR is imported, amended or re-authored; its source and independent integration remain separate.
Contract-Neutral: Align existing filesystem guidance and its executable checks with unchanged reading states and accessible reasons; no polling, telemetry, runtime, access, safety or recovery contract changes.
Change-source: pulse-maintainer
The copied loopback recipe inherits http_proxy and ALL_PROXY even with curl --disable. Real-curl parent controls sent the synthetic administrator-capable header to the proxy and could report its denial as a Pulse role result. Explicitly bypass proxies for the selected backend and keep the protocol boundary clear.
Exercise both response directions and three proxy environment selectors, preserving privacy, deadlines, cleanup and the existing role/IdP trust boundary. Add a repeatable production Docs/browser check for the exact copied guide.
Contract-Neutral: Documentation and synthetic recipe/browser checks only; proxy authentication semantics and runtime contracts are unchanged.
Change-source: pulse-maintainer
The exact-source broader audit suite exposed ENOEXEC differences in the direct Python exec path. Retain the former Bash executable-script fallback with a fixed exec program and literal argv, including whitespace and shell metacharacters in the executable path. Isolate the two path-lookalike fixtures so each still asserts its own one-commit guard result.
Contract-Neutral: Restore the existing Bash command compatibility within the new audit process group; audit classification, retry rules, dependency graphs, command arguments and external authority are unchanged.
Change-source: pulse-maintainer
Both required CI and the local gate must treat the new subprocess owner as audit runtime, not an unchanged dependency graph. Carry the actual helper into the isolated local-gate fixture and test positive findings, missing results, large change lists and exact-path lookalikes through both consumers.
Contract-Neutral: Extend the existing strict audit-runtime path classification to its new owned helper; advisory rules, dependency graphs, public runtime, credentials and external permissions are unchanged.
Change-source: pulse-maintainer
The existing helper leaves TERM-resistant children running after npm finishes or its attempt expires. Give each attempt its own process group and monotonic deadline, stop the owned group on every terminal path, and preserve the captured report and observed exit. An npm TERM handler returning zero cannot turn an expired attempt into a clean verdict. Exercise normal completion, failure, timeout, cancellation, positive findings and an unrelated live process through the actual shell helper.
Contract-Neutral: Repair the existing audit deadline and child ownership only; invocation arguments, manifests, strict and unchanged-graph advisory verdicts, retry budgets and external access remain unchanged.
Change-source: pulse-maintainer
The browser gate now uses the shared repository I/O module. Exercise its
real dependency in the prepush fixture so smoke tests reach qualification
checks instead of failing during import.
Contract-Neutral: Test fixture dependency only, no application or contract behavior changes.
Bind removal guidance to checked NAS quiescence and boot retirement instead of retired installation markers. Model DSM hard-link semantics through real exclusive link syscalls so macOS contract jobs no longer depend on GNU ln, retaining raced-directory adverse controls and every existing manager/recovery assertion.
Contract-Neutral: Tests only; production removal, documentation and teardown contracts are unchanged.
Change-source: pulse-maintainer
Compose upstream bed5052eb5 without replacing reviewed history. Keep the accepted one complete stdout/stderr observation and blank-line refusal instead of upstream second-sample replacement. Retain upstream real-systemctl fresh-host assertions and its platform-availability decorator alongside every reviewed failure-retention control. No release, installed recovery or LXC stop-cause claim.
Change-source: pulse-maintainer
The previous commit inserted a new real-systemctl test above the old one
and took over its skipUnless decorator, so on macOS, which has no
systemctl, the new test skipped and the old one ran unprotected and
failed. Restore the decorator on the old test.
The installer proof required the real-systemctl control to have passed.
Where systemctl does not exist it must instead report skipped, so the
proof now checks for ok on a host with systemctl and for skipped on one
without, and still fails if the control is absent from the output.
Refs: #2785
The 2026-10-10 server service discovery change lists the two historical
unit names with one pattern read and refuses to continue when that read
exits non-zero. systemctl list-unit-files exits 1, printing nothing on
stdout or stderr, when none of the named unit files exist. That is every
fresh host, so install.sh refused with "Cannot identify the Pulse systemd
service: unit inventory failed" before it did any work. The first beta's
install.sh smoke test caught it on a clean Debian 12 container; no
release carries it, and v6.5.0 listed the whole inventory and grepped it.
The tests could not see it. Their systemctl double reports "no units" as
exit 0, and test_partial_failed_inventory_is_not_absence asserted that an
empty inventory with exit 1 must be refused, which is the defect itself.
Add one inventory read shared by the installer and the unattended
updater, and use it for the auto-update mask check, which had the same
hazard. It accepts exactly the silent exit 1 as an empty inventory, and
only after a second read confirms there is still no output and no
message. A timeout, a signal, any other status, or an exit 1 that carries
output or a message stays a failed inventory, so a real failure still
cannot pass as absence.
Make the doubles model systemd: a failure now prints its message, silent
exit 1 is absence, and a new test runs real systemctl --root over an
empty unit root for both scripts. Run against the unfixed scripts, the
four new fresh-host tests fail; the rest of the suite is unchanged.
The registered runtime proofs for install.sh and the unattended updater,
which drive this suite, now require the fresh-host controls to have run
and passed, so they cannot be skipped or deleted unnoticed.
Refs: #2785
Read optional intake precautions at their expandable report fields, bind OpenShift Secret tests to the validated private file, and target helper workflow negative controls by step. Preserve the prepared GNU timeout in the private teardown probe on macOS and keep the actual security migration boundaries mandatory. Production guidance, installer behaviour and platform coverage are unchanged.
Contract-Neutral: Fixture-only integration repair; no product, installer, workflow, deployment or safety contract delta.
Change-source: pulse-maintainer