Commit Graph

6968 Commits

Author SHA1 Message Date
pulse-triage[bot] 16edccf6d2 Clarify Docker-only tokens in host authentication triage
Separate a registered Docker reporting token from the scopes needed by the host module. Keep scope rejection distinct from HTTP 401 and identify the token-generation screen before changing credentials or reinstalling.

Contract-Neutral: Documentation and source-alignment controls only; no authentication, token authority, runtime or install contract changes.
Change-source: pulse-maintainer
2026-10-11 05:03:08 +01:00
pulse-triage[bot] 8dcf561e4e Check absent agent-token registration before another install
A normal-host enrolment report supplies lookup and host-report 401s, repeated installs and an absent inventory record. Document one named server-side registration check before touching the affected agent, with separate limits for persistence, credential hand-off and accepted reports.

Contract-Neutral: Existing authentication help and its source-alignment controls only; no runtime, API, scope, route or credential-policy change.
Change-source: pulse-maintainer
2026-10-11 04:09:06 +01:00
pulse-triage[bot] 45a211387d Integrate reviewed byte-bounded log diagnostic guidance
Change-source: pulse-maintainer
2026-10-11 02:57:30 +01:00
pulse-triage[bot] fcc633337e Bound copied log diagnostics by bytes as well as records
Cap server and agent excerpts at 256 KiB before display or request-ID search. Buffer only a bounded owner-only temporary file, preserve reader failures, and apply the hard deadline to the complete read-only pipeline. Exercise exact-size, overflow, stream, privacy, cleanup and closed-pipe deadline controls without accessing a real journal, daemon or service.

Contract-Neutral: Documentation-only passive log recipe bounds and executable copied-recipe tests; no runtime, API, authentication, installer or monitoring contract change.
Change-source: pulse-maintainer
2026-10-11 02:24:14 +01:00
pulse-triage[bot] b9d465d349 Integrate reviewed captured resource publication ownership
Preserve Core's exact initialization repair and additive corrections. Retained final-source normal/race ownership controls, complete normal API population, genuine embed build and vet support source acceptance; installed behavior and reported baseline relief remain separate.

Change-source: pulse-maintainer
2026-10-11 02:14:26 +01:00
pulse-triage[bot] dbf65f305c Audit main without directing dependency repairs to retired lines
The latest daily advisory watch passes main and fails only retained release branches, then recommends backports that no longer have a delivery route. Keep the complete strict main audit, read-only/cache-free execution and security proof, remove obsolete line discovery and give accurate main repair and containing-release guidance. Add fixed-main recipe, retired-branch refusal and preserved strict-audit controls.

Change-source: pulse-maintainer
2026-10-10 23:43:37 +01:00
pulse-triage[bot] 3879097db4 Reconcile reviewed maintenance with published monitoring fixes
Preserve every reviewed commit while incorporating current upstream main additively before exact final-source publication checks.

Change-source: pulse-maintainer
2026-10-10 23:06:49 +01:00
pulse-triage[bot] 1f6b648107 fix(monitoring): bind publication changes to captured providers
Capture store, tenant and provider selection once across source ownership, records and consume-once changes. Keep provider callbacks outside locks, refuse replaced collections, and order non-queueing read refreshes with ingest. Preserve admitted history and leave skipped changes pending.

Contract-Neutral: Internal dependency/publication ownership only; resource schemas, tenant authorization, notification policy, thresholds, retention and shutdown semantics are unchanged.
Change-source: pulse-maintainer
2026-10-10 22:50:31 +01:00
rcourtman 3056dccfea Say when a Proxmox guest's memory reading may include cache
A VM that Pulse can only read through Proxmox's own memory value (no guest
agent, no Pulse agent inside it) shows a cache-inclusive percentage that can
sit near 100% on a healthy guest. Patrol already treats those sources as "may
include cache" for pressure and recovery; the Workloads row and drawer gave no
hint, so the red bar read as memory pressure (#2762, #2639).

A current reading from status-mem, status-freemem, cluster-resources or
derived-total-minus-used keeps its numbers, colour and thresholds, and is now
qualified: VM rows show "May include cache" ("Cache?" in compact layouts), the
bar tooltip and the drawer's "About this reading" explain it, and the drawer
says what can supply the guest's own figure (a Pulse agent in the guest, or
the QEMU guest agent on a Linux VM, worded as "can" because source selection
does not guarantee a replacement). Every running container reads the cgroup
value, so containers carry the note in the tooltip and drawer only, not a
label on every row. Retained, unknown and unavailable readings keep their own
labels and are never called current.

Mock guests now carry memory provenance and a refreshed observation time;
before, every mock VM and container read "Freshness unknown", which hid this
cue from the demo and from screenshots. Windows VMs never claim Linux meminfo
provenance, and scenario-stopped guests read powered-off.

Refs #2762, #2639
2026-10-10 22:50:30 +01:00
pulse-triage[bot] bc1fed0594 Integrate reviewed bounded memory diagnostic recipe
Preserve the exact reviewed b0a567cb candidate and dcd68512 repair. Retained source-od0gj8fl proves ordinary owned-child sampling, deadline cleanup and privacy controls; no runtime memory relief is claimed.

Change-source: pulse-maintainer
2026-10-10 22:49:14 +01:00
pulse-triage[bot] ef45e2da1a Merge commit 'da44cec421a11f7e5e8c25f82eddbeab369b7e6c' into maintainer-async/20261010T214610Z-core-runtime/pulse
Change-source: pulse-maintainer
2026-10-10 22:46:49 +01:00
pulse-triage[bot] b0a567cb54 Merge commit 'dcd6851220a56f3ada34abb71a41dd13b95b5e7d' into maintainer-async/20261010T211905Z-product-intelligence/pulse
Change-source: pulse-maintainer
2026-10-10 22:22:46 +01:00
pulse-triage[bot] 0eca75915b Register native updater ownership and ordered proof routes
Repair candidate preflight's two registry failures without weakening the audit: declare the native harness as deployment-installability runtime and order its exact dedicated proof paths. Add live-route and adverse drift regression controls, and record canonical ownership in the contract.

Change-source: pulse-maintainer
2026-10-10 22:11:01 +01:00
pulse-triage[bot] 491e872adb Refuse tracked source drift before native updater compilation
The checkout label alone cannot establish source bytes after frontend preparation. Require an unchanged tracked tree before compiling the private pair; the exact recipe control now rejects dirty source before either compiler or root command and preserves all existing failure propagation.

Contract-Neutral: Implements existing exact-source admission on tracked bytes; native runtime, input and completion contracts are unchanged.
Change-source: pulse-maintainer
2026-10-10 21:44:17 +01:00
pulse-triage[bot] 2ce307154f Keep native updater fixture traffic on loopback
Use the existing BIND_ADDRESS setting in the fixed fixture environment, alongside telemetry opt-out. The connected production-function control verifies this exact private profile and persistence across all seven phases; no product default or native manager safeguard changes.

Contract-Neutral: Fixed local fixture environment only; source/native admission and transaction-completion contracts are unchanged.
Change-source: pulse-maintainer
2026-10-10 21:25:19 +01:00
pulse-triage[bot] ba2a8f9903 Check unattended updater transactions on native source CI
Exercise real systemd and signed local current-source replacement/recovery in the existing real-embed job for #2785. Preserve service masks, persistent bytes, backups and owned cleanup; source controls do not claim published or LXC recovery.

Change-source: pulse-maintainer
2026-10-10 20:50:35 +01:00
pulse-triage[bot] de47d67f2c Integrate reviewed monitoring recovery and freshness repairs
Preserve exact Web candidate bd6e21ca40. Retained focused and current-byte browser controls cover complete availability replacement, source withdrawal, mounted stale evidence, held attention and vSphere table naming. No new product surface or installed recovery claim.

Change-source: pulse-maintainer
2026-10-10 19:44:02 +01:00
pulse-triage[bot] bd6e21ca40 Keep recovered checks and ageing monitoring views truthful
Replace complete availability records instead of restoring withdrawn failure fields after recovery. Age held metric readings and availability views on the existing shared clock, and name expired Settings evidence Stale rather than Online. Keep the vSphere VM table named in kiosk and filtered-empty views.

Adapt these repairs to current source-withdrawal and page-owned state without importing old branches. The wider timestamp sweep remains separate; authentication, polling and notification recovery policy are unchanged.

Change-source: pulse-maintainer
2026-10-10 19:27:13 +01:00
pulse-triage[bot] da44cec421 fix(monitoring): publish complete resource wiring once
Apply saved notification policy before initial resource publication, batch supplemental bindings and retain explicit withdrawals for existing and future tenants. Serialize complete router initialization and refuse old binding collections before ordered writes; preserve admitted alert joins without clearing active alerts. Keep native and demo provider records separated across the mock-switch hook gap.

Contract-Neutral: Internal resource dependency wiring and publication ordering only; existing resource/API schemas, authorization and tenant boundaries, alert thresholds, retention and shutdown policy are unchanged.
Change-source: pulse-maintainer
2026-10-10 19:23:09 +01:00
rcourtman 01b95820a7 Record why a disk's operator state does not follow its ID (#2815)
Supersedes #2808 and the earlier #2806: the same change as one commit on
current `main`. #2808's checks were green, but a peer's addition to the
contract's proof list conflicted with this note, and a conflicted merge
cannot pass the commit hooks. The proof lead-in line is now left
unchanged so such an addition does not conflict again.

## What

Records, in the unified-resources contract and a registry-level test,
that a physical disk's operator-state row (retirement, muting,
maintenance window, criticality, note) does not follow the disk when its
canonical ID changes. No runtime code changes.

A disk keyed by a serial that two machines report is re-keyed in place
from the unscoped serial ID to a machine-scoped ID
(`rekeyPhysicalDiskLocked`), and takes the unscoped ID again on the
rebuild after the other copy goes away. The alert, Patrol, resources-API
and agent-context readers resolve the disk's reference to its current
canonical ID and read that ID's row, so:

- at the split the disk reads no posture (unless a row already sits on
its scoped ID), and is eligible to alert again;
- after the return the original row applies again, over anything the
operator wrote on the scoped ID in between (that row stays stored, but
resolving the live disk's reference does not select it).

Reproduced with a probe on both stores before deciding.

## Why the row is not carried

- The registry cannot tell one disk seen from two machines (a
dual-ported SAS shelf, where carrying to every copy is right) from two
disks behind one serial (fixed-serial USB bridges, cloned VMs with an
explicit serial, where it is wrong).
- The two outcomes differ in kind. A split exclusion that reaches an
extra copy only costs an extra row; a retirement or mute that reaches a
disk the operator never silenced can hide that disk's failure alerts. A
row left behind makes the disk eligible to alert again.
- A copy that keeps the original would bring back a row the operator
cleared unless a durable record says it was made (a clear deletes the
row); a move needs a policy for the return; a read-side fallback needs
no record but spreads one disk's posture to unrelated disks and cannot
be cleared per copy.
- Not a remediation-lock hole: no producer gives a physical disk an
action capability, pinned by
`TestPhysicalDiskResourcesAdvertiseNoActionCapability` and the TrueNAS
counterpart.
- No report of a posture lost this way is known; #1516 and #1654 concern
missing and duplicated disks.

The contract states the quiet outcome it accepts (the return case above,
when the operator had written a row that suppresses less) and what would
reopen the decision: a report of a lost or resurrected disk posture, or
a disk gaining an action capability.

## Proof

`TestPhysicalDiskOperatorStateIsNotCarriedAcrossASerialSplit` runs on
MemoryStore and SQLite, drives the monitor adapter's rebuild and reads
as alert intent does (resolve the PVE disk alert alias, then read that
ID's row). Rows are compared as JSON, so every persisted field counts.
It fails when the row is copied to the incoming copy, copied on re-key,
copied into empty scoped IDs at the adapter rebuild, a re-keyed
sibling's scoped row copied onto the incoming copy, moved on re-key,
partly copied back on return (its note, or its `SetBy`), carried forward
only while a window is active, or replaced by the scoped row on return.
It does not run the API write path, Patrol's provider or the other
readers; the contract says so.

Full `internal/unifiedresources` package, `go vet` and the TrueNAS
tripwire pass.
2026-10-10 18:31:22 +01:00
rcourtman f983b8c08f Record why a disk's operator state does not follow its ID
A physical disk keyed by a serial that two machines report is re-keyed
in place to a machine-scoped ID, and the unscoped ID returns on the
rebuild after the other copy goes away. No operator-state row moves with
those changes: after the split the disk reads no posture (unless a row
already sits on its scoped ID), and after the return its original row
applies again, over anything written on the scoped ID in between.

Whether to carry the row has been open since the remediation-lock fix
chipped it. The registry cannot tell one disk seen from two machines
(right to carry to every copy) from two disks behind one fixed serial
(wrong: a carried retirement or mute can hide an unrelated disk's
failure alerts), and a copy that keeps the original would bring back a
row the operator cleared unless a durable record says it was made. No
report of a posture lost this way is known, and a row left behind makes
the disk eligible to alert again, so the rows stay where the operator
put them.

Pin that in registry_test.go on both stores, driving the monitor
adapter's rebuild and reading as alert intent does, with rows compared
on every persisted field and the second machine's copy checked for a
stray row at every step. Copying the row to the incoming copy, on
re-key or into empty scoped IDs at the rebuild, copying a re-keyed
sibling's scoped row onto it, moving the row, carrying a single field
back (its note, its SetBy) or an active window forward, and letting the
scoped row win on return all fail it. Replace the contract's "not
carried" with the decision, its reason, the quiet outcome it accepts and
what would reopen it, keeping the proof lead-in line unchanged so a
peer's addition to the proof list above it does not conflict.
2026-10-10 18:03:39 +01:00
pulse-triage[bot] a04c246adf Keep updater collector failures and final installer evidence
Drain the installer pipe after a failed log write, retain its first observed failure across later successes and collect an unterminated final line. Reuse the guarded restoration path without changing signed-input, service-state, mask or prior-activity safeguards; bind the regression controls to package acceptance.

Change-source: pulse-maintainer
2026-10-10 17:15:39 +01:00
pulse-triage[bot] 4da917c6ce Integrate reviewed alert-history storage compaction
Preserve both exact candidate commits and the 63 normal/race history controls. This bounds discarded-row backing storage without claiming reported process-memory attribution.

Change-source: pulse-maintainer
2026-10-10 16:35:52 +01:00
pulse-triage[bot] f07630e769 Integrate reviewed notification recovery lifetime repair
Preserve the exact candidate and its parent-bound browser receipt. Focused source, static and desktop/phone browser controls support the bounded existing-flow repair.

Change-source: pulse-maintainer
2026-10-10 16:35:48 +01:00
pulse-triage[bot] 27f24b3c5d Retire notification recovery callbacks with their view
A Retry or Dismiss response could outlive Overview or Notifications, publish a global toast over its replacement and start new health/activity reads. Retire pending health and queue-action completions, scheduled activity work and exposed handlers with their mounted owner, without cancelling or undoing already-issued requests. Preserve active-view confirmations, read ordering and persistent failure feedback.

Cover both actions and health/callback disposal in focused and connected source tests. Retain immutable-parent adverse controls and parent-bound desktop Chromium/phone WebKit browser evidence. Full source-suite, lint, production-build and installed-delivery acceptance remain separate.

Change-source: pulse-maintainer
2026-10-10 16:16:41 +01:00
pulse-triage[bot] a15529711b Release removed alert-history row storage after compaction
Count surviving rows before allocating expiry, deduplication and removal results. Keep unchanged history and preserve occurrence/evidence and primary, backup and retired-JSON recovery semantics. The parent retains original-sized arrays after sparse compaction; capacity and connected loader controls discriminate without forced GC or process profiles.

Contract-Neutral: Application alert-history backing storage only; retention windows, occurrence identity, ordering, evidence, persistence/recovery authority and public response shapes are unchanged.
Change-source: pulse-maintainer
2026-10-10 16:11:43 +01:00
pulse-triage[bot] f03a6a9f52 Incorporate published upstream without rewriting reviewed integrations
Preserve the accepted local maintenance batch and compose the exact current main through an additive merge.

Change-source: pulse-maintainer
2026-10-10 16:06:29 +01:00
pulse-triage[bot] 9fa21f6c22 Keep updates recoverable and alert settings and History trustworthy (#2795)
## What changed and why

Keep server updates recoverable and notification settings and History
trustworthy.

- Server installers and unattended updates select an exact, bounded
service inventory. Ambiguous, failed or incomplete discovery stops
before service mutation or installer handoff. Recovery requires complete
manager observations: uncertain state preserves verified backups rather
than replacing or restarting an unconfirmed executable. Existing masks,
consent, signatures and prior-active safeguards remain.
- Notification settings require a successful saved-policy read. Policy
and destinations save together; newer drafts survive older
acknowledgements and rejected saves. Webhook forms, toggles and deletion
use one acknowledged writer. Partial acceptance preserves accepted
changes and stops without automatic retry or rollback.
- Alert History now distinguishes a failed read from an empty result.
Same-range saved entries remain explicitly incomplete; another range's
snapshot is excluded. Frequency/severity summaries are withheld on
failure, live alerts remain visible, and one pending read-only retry
ignores retired-context replies.
- Failed-upgrade intake separates attempted versions, failing runtimes
and later recovery. Help explains timer-only containment without
stopping Pulse or cancelling an active update, preserving evidence,
backups and known schedule state.
- Updater logs retain installer and log-collector exits separately
without attributing a past host interruption. Ordinary Frontend CI now
requires backend compilation and server/API vet with that checkout's
actual, byte-matching production embed; unavailable checkout identity
stops before compilation.

Related to #2785

The report prompted service-discovery investigation, but these
reproduced defects do not establish why its LXC stopped. Native signed
update/restoration and the stop cause remain separate. Notification
editing and read-failure controls do not establish ordinary notification
delivery.

## Included source

Complete unpublished range against observed current upstream:
`0c3a17694bb7939022f9c55946b6776222bbc9ab..3610313fd3ac0d6ad7de925b2fb73938a6f25488`.

Previously proposed reviewed source at
`c15f7554724b20f3e2130bd228257780b1ac31d7` is retained:

- Discovery: `3c9a102e83f558463d186e84c494fc7bdbdd7d27`,
`4e79e047f4d9daf2a8749b4e3490bde994d38931`.
- Guarded recovery: `9060dbd54de372cd4d83d7390f4e42caf00938e6`;
behavioral regression correction
`a9fa251b784cfe270c12463e7699955bffcf6d57`. Earlier additive
reconciliation `eebef3559f43a89ef5c2d2d2516bd187dffeff00` remains in
ancestry; its bare-call spelling assertion is superseded by the
behavioral wrapper.
- Notification editing: `8c9c137ef755b7e393f218b9ba909561eecd73e6`,
`63f8e40e7ef7b1c430b8efb2a67b8c93c1088621`,
`c9bcb74200851d6c37061311d1aa68f1375fc2a3`,
`d359c8433c91342500b0dae00ba7e0a15422bf0e`, preserving composition
`5f434bcd09b81731d7dce3f0fff6e86ce89df2dd`.
- Intake/help: `6809ca2b23173145f3e43b3e5e55e52ecde14088`,
`f2acf2f114c158687b1a8db13c18057c5e77bd49`.

New reviewed integrations through starting tip
`c6967e947feabb2fe1e4f2f8d5dc021a78da6676`:

- History repair `eaa51d38fe2c0b5e79665d8fed91089b38b6874a`, integrated
by `c2fd6079d25b96e7939bf7a0c6aff73efec0ad84`.
- Pipeline observations and real-embed CI:
`74a8d0d05ac260397e8a316a77c7c541c68ff992`,
`8195090415ca88b34122a28c2571460a3656cca9`,
`7a509b33316922293f7222fa6424ab2c868525ba`, integrated by
`c6967e947feabb2fe1e4f2f8d5dc021a78da6676`.

Publication correction `34fbcced8f480c228732589f5cf6f557df1e0c3f`
reconciles the versioned installer URL fixture with guarded recovery.
Completed checks on the prior proposal cannot land independently: Build
and Test `38054465854` and Unified Agent Native Verification
`38054465853` fail
`TestPulseAutoUpdatePerformUpdateUsesVersionedInstallerURL` because its
extracted updater omits `read_update_service_state`. The fixture now
runs the real reader/restart helpers against a complete synthetic active
manager observation; both versioned installer and signature URL
assertions remain. No production behavior or check threshold changes. An
unchanged-head retry would not repair this deterministic defect.

Additive full-SHA merge `3610313fd3ac0d6ad7de925b2fb73938a6f25488`
incorporates current published upstream, including PR2807's Workloads
state cleanup. It does not replace the reviewed starting tip or rewrite
any accepted commit, author or ancestry. Upstream work is already
published, not a new candidate here.

## Validation and remaining limits

- Retained reviewed recovery evidence: 143 Go identities across 17
roots, 16 discovery methods and 10 updater smoke controls; behavioral
wrapper controls and earlier failure evidence remain. Confined doubles
do not qualify native systemd installation.
- Retained notification evidence: 75 focused passes and desktop/narrow
browser controls. Reviewed History outcome records 55 focused passes
plus desktop/phone error, retry, range, context and disposal controls.
Final changed bytes remain matched after upstream composition.
- Retained guidance evidence: eight copied-command recipe controls and
nineteen connected documentation checks.
- New confined fixture checks: five real-embed recipe tests and ten
updater manager/pipeline methods pass. These use compiler/manager
doubles, not actual compilation or installed recovery. The committed
versioned-URL fixture's shell passes with a complete active observation;
omission of its reader reproduces the failure, and an empty observation
refuses before installer download. This executes shell bytes, not the Go
test suite.
- Final provenance, clean-tree/ancestry, whitespace, Bash syntax, new
per-commit canonical checks, contract/registry audits and browser
coverage pass. All 13 changed user-visible frontend files have matching
in-range receipts; no unchanged browser rerun was needed.
- On the prior proposal head, Frontend, script smoke/build, API shards
and all eight Core E2E shards pass. These are evidence for that source,
not the new head or the newly added real-embed phase. The failed backend
rest-0 and four native Unix jobs are retained above; Windows and FreeBSD
pass separately.
- Both aggregate VM requests before composition and both exact
final-source requests were refused before execution, client exit 2 /
`proof-capacity-low`. Commands were the full installer suite with
updater/real-embed recipes, and frontend offline
install/type-check/lint/test/build. There is no executed receipt or
aggregate pass. The preparation-feedback helper was also unavailable;
direct supported lightweight checks are not a full helper verdict.
- Earlier Docker URL-edit failure/three skips, incomplete-output proof,
planning-doc Git permission failure and driver exit 143 remain
historical adverse evidence. Focused checks do not erase them. Exact
new-head containing CI must supply and assess whole-source results,
including actual real-embed compilation/vet.

This source proposal is suitable for independent review and exact-head
CI, not permission to land failed checks. Required administrative review
and exact-head checks remain necessary. No release, deployment,
installed recovery or issue closure is claimed.


[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-10 15:04:32 +00:00
rcourtman 655a4b70e6 Rebuild the mock view when only a link's creation time changes (#2811)
Follow-up to #2793.

## What was wrong

The mock-mode unified view caches its linked build keyed on
`sameManualLinks`, which compared each link's resource pair and primary
and said link metadata, time included, does not change a fold. After
#2793, among otherwise equal candidates, a chain of links folds into the
primary of the earliest-created link (`linkChainRootLocked`), so that is
false for `CreatedAt`: a link list that differs only in a creation time
can leave a different row standing.

In a cycle of a Proxmox storage, a TrueNAS VM and a vSphere VM the
TrueNAS VM keeps its row while its link is older than the vSphere VM's,
and the vSphere VM keeps it once it is newer. With the fixture data
version and link order unchanged and only a `CreatedAt` replaced, the
cached mock view kept serving the old row while the live rebuild, which
reloads the links from the store, served the new one.

## What changes

- `sameManualLinks` also compares `CreatedAt` (`time.Time.Equal`). It
has three readers and all take the fix: the data-version cache hit and
the keep-previous-freshness decision on publication in
`currentUnifiedStateView`, and `cachedMockStructureView` (the structure
view behind `currentStructureUnifiedStateView`). The comment no longer
says time cannot change a fold.
- `TestMockUnifiedViewFollowsALinkCreationTimeChange` replaces the
TrueNAS VM's link with one that differs only in its time, with no
fixture tick, and expects the surviving row a
`mock.UnifiedResourceSnapshotWithLinks` build keeps: through the
structure view and the data-version view when the time moves later, and
through the data-version view when it moves back. It also asserts the
two link lists differ in exactly one creation time, in the same order,
and that the fixture data version did not move.
- The monitoring and unified-resources contracts ("Mock-mode unified
view applies operator links") say the key includes each link's creation
time; the agent-lifecycle contract gets the owner note for `monitor.go`.

## Verification

Run on pulse-dev:

- The new test and the ten neighbouring `TestMockUnifiedView*`,
`TestMockMetricsTarget*` and `TestMockStructure*` tests pass.
- `go test ./internal/monitoring/ ./internal/mock/` passes in full.
- Negative controls, each failing the new test: removing the
`CreatedAt.Equal` term; reverting only the data-version cache hit;
reverting only the freshness-keep decision; reverting only the structure
view's comparison.
2026-10-10 15:58:28 +01:00
pulse-triage[bot] 7b53ea4875 Integrate independently verified rolling-alert cache bounds
Accept the exact Core candidate with retained normal/race monitoring proof and discriminating complete-window controls. Optional copies are bounded without truncating source windows; no installed RSS relief or broad fleet acceptance is claimed.

Change-source: pulse-maintainer
2026-10-10 15:57:16 +01:00
pulse-triage[bot] deaad7f815 Incorporate upstream present at the coordination boundary
Change-source: pulse-maintainer
2026-10-10 15:56:29 +01:00
rcourtman 3c813bf793 Stop pin succession erasing an operator split (#2810)
## What

An operator split of a reporter from a sibling that reports the same
machine could be erased by pin succession. A source the operator split
(unlink or report-merge records the exclusion `(sibling's ID, source's
source-specific ID)`) pins the shared machine key on its own
source-specific ID while the sibling is present. With the sibling
absent, the source mints the sibling's ID, and pin succession read the
source's pin as that ID's earlier era. Applying the pair rewrote the
exclusion into one of the ID from itself, so the sibling's return merged
the two.

Unforced rebuild sequence {agent + Docker host, Docker host only, agent
+ Docker host}: 2, 1, 1 agents in 37 of 40 trials on `main`, 2, 1, 2 in
40 of 40 with this change.

## Change

`PersistIdentityPins` (`internal/unifiedresources/canonical_id_pins.go`)
now:

- refuses a succession whose retired ID the operator excluded from its
successor (the registry owns the check, beside the observed-ID check, so
a rebuild that could not read its decisions guards the split from the
exclusions it carried);
- drops the successions of one batch that would retire two IDs the
operator split from each other onto one successor
(`withoutSplitFanInLocked`; only each predecessor's first declaration
counts, as in both stores).

A split that pairs the retired ID with a third resource still follows
the succession. The stores are unchanged; the reporter-era skip from
#2800 and record-declared eras are unchanged.

## Boundary

With the sibling absent the split source is listed under the sibling's
ID, and an operator lock set on its candidate ID does not cover it until
the sibling returns. Before this change the lock was carried onto the
sibling's ID and the split erased; in the other pin-holder state the gap
already existed, and the reporter-era skip in #2800 accepts the same
boundary. The unified-resources contract records it, with the limits of
the check (chains through stale carried decisions, a split recorded
after the registry read its decisions, an already-recorded predecessor
counting toward a fan-in). Keeping a split reporter on its candidate ID
in the sibling's absence would close the gap but renames every currently
split reporter whose sibling is absent; that is a separate decision.

## Proof

`TestPinSuccessionKeepsASplitSourceApart` (both stores): key held by the
split source or by the sibling, a rebuild that cannot read its decisions
(with a control that records the succession through the same store), the
fan-in batch with controls (no split; a stored self-pair exclusion), the
narrowness control (a split of a superseded ID paired with a third
resource still follows), and the filter's own table. Mutation controls
run on pulse-dev: pair check removed, blanket rule, wrong pair, previous
exclusions not carried, fan-in filter not applied, self pair counted,
suppression when decisions were unreadable (with and without an
exclusion), later declarations weighed, whole group dropped, every batch
dropped. Each fails its intended subtests. Full
`internal/unifiedresources` and `internal/monitoring` suites pass on
pulse-dev on the rebased tree, `internal/alerts` on the tree before the
last rebase. `internal/api` there had two failures that are not this
change: `TestServerInfoEndpointReportsDevelopment` (the rsynced tree has
no `.git`) and `TestContainerBootstrapFailuresStopAndClean/hangup` (a
load flake: 5 of 5 alone, and the `configapi` package 2 of 2).
2026-10-10 15:54:14 +01:00
pulse-triage[bot] 88efa8d41e Integrate verified agent token-inventory help
Review accepts the exact documentation candidate and its retained focused checks and desktop/phone proof. The separate unqualified memory-reader candidate remains excluded; no authentication or credential-use recovery is inferred.

Contract-Neutral: Existing agent documentation and source-alignment tests only; no runtime, navigation, credential or API changes.
Change-source: pulse-maintainer
2026-10-10 15:50:24 +01:00
pulse-triage[bot] eb4460ffd9 Bound rolling-alert history retention independently of fleet projection
Reuse only the independent window-retention repair from ed1b561d on the identical cache parent. Do not import the broader fleet conversion or sealed getters. Bound optional copies before allocation and release accounting on replacement, expiry and Reset. Add connected complete-window controls at both ceilings, preserving measured zero, timestamps, fresh live-tail authority and ownership. This is not attribution or installed relief for issue #1803.

Contract-Neutral: Only optional in-process window retention changes. Persistent history, complete returned windows, the 30-second hit expiry, live-tail authority, alert calculations and public API/wire contracts are unchanged.
Cache-repair-source: ed1b561d8b435a64c43fc87a5240aff4d9f70bd0
Change-source: pulse-maintainer
2026-10-10 15:35:14 +01:00
pulse-triage[bot] ce5354cdab Incorporate upstream present at the coordination boundary
Change-source: pulse-maintainer
2026-10-10 15:33:06 +01:00
rcourtman 0b6d7bbff2 Keep a remediation lock readable on an ID a succession retired (#2809)
## Problem

A rebuild applies canonical-ID successions to the store, which moves the
predecessor's operator row (or carries its remediation lock onto an
existing successor row), before `replaceRegistryLocked` publishes the
generation that lists the successor. For the length of the rebuild the
previous generation keeps serving the old ID, and the three places that
decide on the lock read the row of that ID:

- `actionlifecycle.validateExecutionPolicy` (`ValidatePlanFresh` passes
because `registry.Get(oldID)` still finds the resource, then the lock
read finds no row and `!found` means not locked);
- the Patrol broker's `loadPatrolActionPolicyInputs` (via
`Service.ResourceOperatorState`), which records `resource missing` and
authorizes against an empty state;
- `isResourceRemediationLocked` in `internal/ai/tools/action_audit.go`.

Nothing re-resolves the ID through the new generation, and the plan step
reads the old ID's row too. Every kind of succession reaches the window:
pin-driven (`PersistIdentityPins`), Proxmox guest eras, and the host and
Docker reporter eras added in #2800. I did not reproduce a concurrent
dispatch; I reproduced the state it would read:
`TestRemediationLockHoldsOnTheRetiredIDWhileThePreviousGenerationServes`
hooks the store between the succession and the publish, and fails on
`main` in all 8 variants (2 era kinds x 2 stores x 2 lock kinds) after
its fixture checks confirm the previous generation still lists the old
ID.

## Fix

`GetResourceOperatorState` (both stores) follows the recorded
successions (`canonical_id_successions`) when the requested ID holds no
row. One walker, `followRetiredID`, serves both stores:

- it answers as the ID the chain **ends on** would, with that row's
remediation block alone (`CarryRemediationLock` into an empty survivor,
`system:identity-change`, dated by the source row so a plan's policy
revision is stable across reads);
- an ID with a row of its own answers with it; a lock the operator
lifted on the successor (edit or clear) stays lifted, and a stale
unlocked row left on an earlier ID does not hide a lock at the end;
- a chain that returns to an ID it has seen has no end, so a lock
anywhere on it counts; there is no depth cap;
- nothing is written, so no ghost rows (`ListResourceOperatorStates`
still lists only rows);
- the SQLite walk runs in one transaction. With one statement per hop, a
reverse succession committing mid-walk can move the lock onto an ID the
walk already passed (a review finding).
`TestRetiredCanonicalIDReadSeesOneStateWhileTheLockMoves` fails when the
transaction is reverted.

Delaying the move was rejected: later steps of the same rebuild read the
store, and it would not protect plans already issued against the old ID.
The alternative of leaving a lock-only row on the predecessor conflicts
with `TestSuccessionBatchOrderKeepsLinkedRemediationLock`'s "no row for
a re-keyed intermediate" and leaves ghost rows.

Seven existing assertions used `GetResourceOperatorState(oldID) == not
found` to prove "the row moved"; they now read rows through
`ListResourceOperatorStates` (`operatorStateRow`), with the same intent.

## Limits (in the contract)

Only the block crosses, so the retired ID still reads none of the row's
other settings while the window is open (as before). An operator edit
made to the retired ID inside the window goes to that ID's own row, not
the successor's (the API writes the ID the served generation lists, and
a succession applies once per predecessor): a lock set there does not
reach the successor, a lock lifted there leaves the successor's lock in
place. A retired ID that a different resource later re-mints with no row
of its own reads the successor's lock until the operator writes it a
row. The contract paragraph that listed the window as a limit now
documents the read.

## Verification

- New tests: `canonical_id_succession_test.go`
(`TestRetiredCanonicalIDReads*`, 5 tests incl. a 150-hop chain, loops,
chain-end, and the interleaving stress test), `registry_test.go` (the
window test above), `internal/actionlifecycle` (`Execute` refuses a
locked resource whose row moved, and the policy read the broker uses
finds it), `internal/ai/tools` (the dispatch gate).
- 9 negative controls on pulse-dev, each caught by a named test: skip
the read in each store, drop the loop guard, nearest row instead of
chain end, whole row instead of the block, wall-clock stamp, ignore
locks on a loop, succession answer overriding an own row, and the
non-snapshot SQLite walk.
- `go test` over `unifiedresources`, `actionlifecycle`, `ai/...`,
`api/...`, `monitoring/...`, `maintenancesentinel`, `truenas`,
`alerts/...`, `agentcontext` on pulse-dev; the interleaving test 8/8
repeats and the retired-ID tests under `-race`. The only two failures
are artifacts of the remote run, not of this change:
`TestServerInfoEndpointReportsDevelopment` needs the `.git` the rsynced
tree lacks, and `TestContainerBootstrapFailuresStopAndClean/hangup`
fails 3/3 the same way on a pristine `origin/main` tree on pulse-dev (a
detached `nohup` run ignores SIGHUP).
- Two independent read-only review rounds before pushing: round 1 found
the SQLite interleaving and two contract overclaims, all fixed; round 2
found nothing.
2026-10-10 15:30:29 +01:00
pulse-triage[bot] a9b4015872 Point agent authentication help at the real token inventory
The installed token manager is API Access in the Security group, not an API Tokens page, and it displays Created, Last used and Scopes rather than expiry. Keep dual lookup/report rejection separate from a proven cause; preserve credentials and avoid repeating failed installs.

Contract-Neutral: Existing documentation and passive source-alignment checks only; no authentication, credential issuance, agent reporting, navigation or runtime contract changes.
Change-source: pulse-maintainer
2026-10-10 15:15:14 +01:00
pulse-triage[bot] 3610313fd3 Incorporate current upstream while retaining reviewed maintenance
Add the published Workloads state cleanup without rewriting accepted commits. Retain the exact reviewed starting tip and the additive guarded-recovery fixture correction.

Change-source: pulse-maintainer
2026-10-10 15:11:30 +01:00
rcourtman 0582d2922a Rebuild the mock view when only a link's creation time changes
The mock-mode unified view caches its linked build keyed on
sameManualLinks, which compared each link's resource pair and primary
and said link metadata, time included, does not change a fold. Among
otherwise equal candidates, chained links now fold into the primary of
the earliest-created link (linkChainRootLocked), so that is false for
the creation time: a list that differs only in a time can leave a
different row standing. In a cycle of a Proxmox storage, a TrueNAS VM
and a vSphere VM the TrueNAS VM keeps its row while its link is older
than the vSphere VM's and the vSphere VM keeps it once it is newer, yet
with the fixture data version unchanged the cached view kept serving
the old row while the live rebuild, which reloads the links from the
store, served the new one.

Compare CreatedAt too. The comparison has three readers and all take the
fix: the data-version cache hit, the decision to keep the previous
freshness when a view is published, and the structure view that serves
callers reading identity and topology.

The regression test replaces the TrueNAS VM's link with one that differs
only in its time, with no fixture tick, and expects the surviving row a
mock.UnifiedResourceSnapshotWithLinks build keeps: through the structure
view and the data-version view when the time moves later, and through the
data-version view when it moves back. Removing the CreatedAt term fails
it, and so does reverting any one of the three readers on its own. The
monitoring and unified-resources contracts now say the key includes the
creation time.
2026-10-10 15:03:48 +01:00
pulse-triage[bot] c6967e947f Integrate updater exit observations and real-embed CI verification
Preserve the exact three reviewed Delivery commits. Retained pipeline and workflow recipe controls cover this bounded diff; full source and installed restoration outcomes remain unproved.

Change-source: pulse-maintainer
2026-10-10 14:48:57 +01:00
pulse-triage[bot] c2fd6079d2 Integrate truthful alert History read failures
Accept focused History ownership controls, final type and lint checks, and parent-bound desktop and phone browser evidence. Preserve the exact reviewed Web commit; broad inherited qualification and native notification gaps remain separate.

Change-source: pulse-maintainer
2026-10-10 14:48:54 +01:00
pulse-triage[bot] 3059538ba3 Incorporate upstream present at coordination batch start
Preserve local reviewed history and the captured upstream frontier; later advances remain with the publication gate.

Change-source: pulse-maintainer
2026-10-10 14:48:51 +01:00
rcourtman b329730e6b Keep a remediation lock readable on an ID a succession retired
A rebuild applies canonical-ID successions to the store, which moves the
predecessor's operator row to the successor, before it publishes the
registry generation that lists the successor. For the length of the
rebuild the previous generation keeps serving the old ID, and the planner,
the Patrol broker and both dispatch gates read the row of that ID. The row
had gone, so a lock the operator set did not refuse an action planned or
dispatched against it. Every kind of succession reaches this window:
pin-driven, Proxmox guest eras, and the host and Docker reporter eras.

GetResourceOperatorState now follows the recorded successions when an ID
holds no row, in both stores, through one walker. It answers as the ID the
chain ends on would, with that row's remediation block alone, so a lock
the operator lifted on the successor stays lifted, a chain that loops
ends, and nothing is written. The SQLite walk is one transaction, because
a succession that commits between two of its statements can move the lock
onto an ID the walk has already passed.

Delaying the move was rejected: later steps of the same rebuild read the
store, and plans already issued against the old ID would not be
protected. Seven tests that used a missing read to prove a row had moved
now read the rows directly. An operator edit to the retired ID inside the
window still does not reach the successor; the contract names it as a
limit.
2026-10-10 14:47:45 +01:00
rcourtman b4cd2f5c9a Stop pin succession erasing an operator split
A source the operator split from a sibling that reports the same machine
(unlink or report-merge) lands on its own source-specific ID and pins the
shared machine key there. The store keeps one pin per key, so with the
sibling absent the source mints the sibling's ID and pin succession read
its pin as that ID's earlier era. Applying the pair rewrote the exclusion
(sibling, source) into (sibling, sibling), and the sibling's return merged
the two: the agent count across {both, source only, both} rebuilds was
2, 1, 1 instead of 2, 1, 2 (37 of 40 trials of the unforced sequence).

PersistIdentityPins now refuses a succession whose retired ID the operator
excluded from its successor, and drops the successions of one batch that
would retire two IDs the operator split from each other onto one
successor. A split that pairs the retired ID with a third resource still
follows the succession. Both checks read the registry's decisions, so a
rebuild that could not read them guards the split from the exclusions it
carried.

While the sibling is absent the split source is listed under the sibling's
ID, so a remediation lock set on its candidate ID does not cover it until
the sibling returns; before, the lock was carried onto the sibling's ID
and the split erased. The unified-resources contract records that boundary
and the limits of the check.
2026-10-10 14:46:41 +01:00
rcourtman a0e3a8b6c4 Merge remote-tracking branch 'origin/main' into refactor/workloads-state-options 2026-10-10 14:26:43 +01:00
rcourtman ac6bdb1822 Drop the dead inputs from useWorkloadsState
useWorkloadsState declared required vms, containers and nodes arrays and an
optional useWorkloads flag. vms and containers were never read. The only
production callers, the Proxmox and vSphere overview pages, passed empty
arrays and useWorkloads: true, so nodes only ever merged nothing into the
infrastructure nodes and the flag kept a disabled mode alive that nothing
could reach.

Remove all four. The state now always runs its workloads query, and its
Proxmox nodes come from the canonical resource snapshot alone, one node per
agent resource. The keyed merge that overlaid supplied nodes (ebc80160ca)
had nothing left to merge, since snapshot producers keep resource IDs
unique, so it becomes a plain filter and map.

The tuple test that fed real nodes into the hook now reaches the same
guarantee through the input production uses: two canonical agent resources
whose instance and node names join to one string must each keep their own
guests, in either snapshot order. It fails if parent lookup falls back to the
joined alias. A source pin and the Proxmox page contract keep the options from
coming back, and the contract prose no longer describes supplied nodes.

No behaviour change for any caller that existed: the built Proxmox and
vSphere chunks differ only by the removed option literals.
2026-10-10 14:22:55 +01:00
rcourtman 9aa7ce2c92 Build the mock linked view at the rebuild's link boundary (#2799)
## Why

PR 2793 made the monitor's rebuild judge chains of operator links once,
over the assembled estate: it holds the link pass back until the
snapshot and every record source except availability are ingested. The
mock-mode fixture build (`FixtureGraph.unifiedResourceSnapshot`) still
ran a link pass per ingest call, which 2793's contract named as a gap.
For a chain whose answer depends on the order its members' sources
arrive in, mock mode could settle on a different row than the live
rebuild. A Proxmox storage, a TrueNAS VM and a vSphere VM linked in a
cycle is the test estate: mock mode kept the vSphere VM where the
rebuild keeps the TrueNAS VM. PR 2732's invariant is that the mock
staged build projects like live.

## What

- `ResourceRegistry.DeferManualLinks()` and
`ApplyDeferredManualLinks(thresholds)` are exported from
`manual_links.go`. Deferral is opt-in, and a registry with no links
defers nothing, so the shared memoized (unlinked) snapshot is built
exactly as before. The apply takes the stale thresholds the ingests were
given (nil takes the registry's own), like the `Ingest*` calls.
- The fixture build defers, ingests the snapshot and the TrueNAS and
vSphere records, runs the held-back pass once, then ingests
availability, so a check linked to a folded resource still projects onto
its primary.
- `TestFixtureGraphAppliesManualLinksAtEachIngestStage` is renamed
`TestFixtureGraphAppliesManualLinksAtTheRebuildsBoundary` (the old name
would now state the opposite). It keeps the availability projection as a
subtest and adds the cycle on real fixture data, compared against an
actual `MonitorAdapter` rebuild. A per-call control in the test fails
loudly if a fixture change stops separating the two.
- Two unit tests in `registry_merge_policy_test.go` pin the exported
pair: the snapshot and record ingests wait, the pass runs once, and it
judges freshness by the thresholds it is given.
- Contracts: the mock-mode section of unified-resources.md, the clause
2793 wrote about the mock build, the monitoring.md test reference, and a
performance-and-scalability.md note. A stale line is dropped there too:
a check that names a folded resource by canonical ID follows the fold to
the primary
(`TestAvailabilityLinkToALinkFoldedResourceFollowsItsPrimary` pins it).

## Evidence

On pulse-dev against 2793's head:

- Negative control: with `internal/mock/platform_fixtures.go` back at
the base, the cycle subtest fails (`mock build kept [vm vm-aceb...
[proxmox truenas vmware]], want ... [vm vm-7f6e... ...]`), the
availability subtest and the shared-snapshot test still pass; restored,
all pass.
- Second control: ignoring the thresholds argument fails
`TestRegistryDeferredLinkPassJudgesFreshnessByTheCallersThresholds` (a
3-minute-old vSphere disk reading survives under 5-minute thresholds and
loses to the fresh agent under the default).
- Whole `internal/mock` and `internal/unifiedresources` packages pass,
plus the `internal/monitoring` mock-view tests.

Cost, measured with a scratch timing test on the default fixture estate:
the unlinked memoized build is unchanged (about 52 ms and 65 MB with or
without the change). A linked build, which only runs when the fixture
data version or the link list changes, pays one extra refresh round
after the deferred pass: about 3 ms and 5 MB more, the same round the
rebuild runs.

## Not in this change

The mock view's cache key (`sameManualLinks`,
`internal/monitoring/monitor.go`) ignores a link's `CreatedAt`, which
2793's chain precedence now reads, so a timestamp-only replacement of a
link would keep serving the cached row. That file is held by other work;
it is a separate follow-up.
2026-10-10 14:11:43 +01:00
rcourtman 02e4ce2bcc Require page-owned state on WorkloadsSurface (#2802)
`WorkloadsSurface` built its own workloads state when no `state` prop
arrived, and its props type extended every `useWorkloadsState` option.
Both production mounts (the Proxmox and vSphere overviews) already build
that state themselves, since it also drives their one `WorkloadsFilter`,
and pass it in. So the fallback only ran in tests, and the mounts passed
`vms`, `containers`, `nodes`, `useWorkloads` (and on Proxmox the
app-container exclusion) that the surface ignored once it had a state.

### What changed

- The surface requires `state` and accepts only `forcedPlatform`,
`forcedViewMode`, the empty-state copy and `tableTitle`. The hook's
options type is now `WorkloadsStateOptions`, so it no longer shares a
name with the surface's props.
- Both pages drop the ignored mount props. The hook-call options
(`excludedWorkloadTypes`, `showNestedExcludedWorkloads`, ...) stay where
they act.
- `WorkloadsSurface.k8s` and `.performance.contract` tests build state
through a `PageOwnedWorkloadsSurface` harness, as the pages do.
Stub-state renders just lose the dead props.
- New pins: the Proxmox page contract test asserts the exact props its
mount passes; the overview guardrails check that each surface repeats
the platform and view scope its page gave the state, and that the
surface imports only the state's type (AST, so formatting and aliasing
cannot get past it).
- `ProxmoxBackupsTable.test.tsx` loses its two pins on the removed JSX
attributes; the hook-call pins that protect backup coverage stay.
- Contract prose in performance-and-scalability, storage-recovery,
unified-resources and frontend-primitives.

### Bundle budget

Once the surface imports only the state's type, Rollup names the chunk
the Proxmox and vSphere pages share after its last module (`rollup`
`getFallbackChunkName` takes `orderedModules[last]`), which is now
`useWorkloadsState`, and the bundle check reported the tracked
`WorkloadsSurface` key as `MISSING`. It is the same code at the same
size, so `.bundlesize.json` renames the key and leaves the baseline and
limit alone. I did not pin the name through `manualChunks`: that would
change chunk composition for a naming concern.

### Evidence (pulse-dev, origin/main 9a32396fe2 vs this change)

- Unminified builds with the renamed chunk mapped back: the shared chunk
is identical except three lines (two minifier import-alias lines and
`props.state ?? useWorkloadsState(props)` -> `props.state`);
`Proxmox.js` and `Vmware.js` shrink by exactly the removed JSX props
(183 and 80 bytes); every other differing chunk differs in one import
line of minifier aliases, from a reordered export list of the same 608
names.
- Bundle check passes on both trees: 57.94 kB before, 57.63 kB after,
limit 70.23 kB.
- Browser pass of both overviews at 1440 and 390 wide against the mock
estate: 90 of 90 readings identical, in two repeated pairs (row and
group counts, titles, headers, filtered-empty copy, Back to top, inline
drawer, one toolbar outside the surface). My first probe could not open
the phone drawer on either build (a backup-status button overlaps the
chevron); the second one dispatches the click and covers it.
- `tsc` clean (pulse-dev, and in the pre-push hook on the merged tree).
The commit's pre-commit lint chain passed; the pre-push `eslint` was
cancelled by the local workload gate, so CI's Frontend job is the lint
of record for the merged tree. The pin tests (9 files) pass on both
trees. A full sweep of the Workloads, Proxmox, vSphere, platform-page,
shared, utils, hooks and Storage suites showed only load timeouts at
pulse-dev load ~120; every failing file passes alone on both trees (23
files, 718 tests).
- Negative controls: the new Proxmox test fails against the unchanged
page (extra props), and the new guardrail fails against the unchanged
tree and against the fallback surface restored over this change.
- Second opinion (Sol, read-only) in four rounds; each round's findings
are fixed: a spelling-based ratchet replaced by the AST check,
overclaiming contract sentences narrowed, and the `MISSING` remedy
limited to a confirmed rename.

### Notes

- `useWorkloadsState` itself never reads `vms` or `containers`, and both
pages pass `nodes: []` and `useWorkloads: true`. Main's `retains
separate supplied tuples sharing a native ID` test does pass real
`nodes`, so removing those options needs a deliberate decision; that is
a separate follow-up.
- Observed, not changed here: in the phone-width probe, a tap at the
centre of a Proxmox row's expand chevron was intercepted by the 44px
backup-status button next to it (Playwright reported that button as
intercepting pointer events), on both builds.
2026-10-10 14:08:56 +01:00
pulse-triage[bot] dcd6851220 Stop resident-memory diagnostic readers at their deadline
A TERM-resistant reader can survive the old timeout after its collection shell exits. Apply the eight-second hard deadline to the entire read-only group, buffer the complete sample and withhold raw reader errors on failure. Retain ordinary privileges, unavailable results and no workload mutation.

Contract-Neutral: Documentation and copied-recipe safety verification only; no runtime collection, memory accounting, service lifecycle or API contract changes.
Change-source: pulse-maintainer
2026-10-10 14:08:50 +01:00
pulse-triage[bot] eaa51d38fe Distinguish alert History read failures from empty results
Keep same-range saved entries under explicit incomplete-evidence guidance, exclude other-range snapshots and preserve live alerts. Offer one pending read-only retry without unsupported frequency or severity totals, and ignore retired-context read/clear completions.

Add connected error/retry and ownership controls with an own-parent desktop and phone browser receipt. Complete frontend suite, full lint, production build and installed notification recovery remain separate.

Change-source: pulse-maintainer
2026-10-10 14:06:02 +01:00