15653 Commits

Author SHA1 Message Date
pulse-triage[bot] 76da9cfbba Prepare v6.6.0
Promote v6.6.0-rc.6 to stable after its 24-hour soak. Only release metadata changes.

Contract-Neutral: Release-only v6.6.0 version, release notes and distribution metadata; no public install or runtime contract changes.
Change-source: pulse-maintainer
v6.6.0 helm-chart-6.6.0
2026-10-25 08:39:03 +00:00
pulse-triage[bot] f54647d7e9 Prepare v6.6.0-rc.6
Release candidate cut from main at 8f111cd43f. It carries every change merged since stable v6.5.0.

Contract-Neutral: Release-only v6.6.0-rc.6 version, release notes and distribution metadata; no public install or runtime contract changes.
Change-source: pulse-maintainer
v6.6.0-rc.6 helm-chart-6.6.0-rc.6
2026-10-24 05:19:48 +01:00
pulse-triage[bot] 8f111cd43f Keep SSO recovery feedback in the active editor (#3172)
**Keep SSO recovery feedback in the active editor**

Keep refused saves, clipboard outcomes and metadata failures beside
reachable editor actions. Preserve exact spaced Allowed Groups on no-op
and mapping-only saves for OIDC and SAML, without changing scope,
domain, email or secret-omission semantics. Keep retained notifications
below blocking dialogs while preserving inertness. Cover
correction/reopen, pending submit, clipboard refusal and modal
restoration through normal suites and source-bound desktop/phone browser
evidence.

**Align the SSO ownership guard with inline connection results**

Retain canonical result presentation and failure formatting without
requiring the background connection-test toast that the editor
intentionally no longer emits. The full frontend run exposed this stale
architecture assertion; normal UI tests already cover active-dialog
feedback and no duplicate toast.

**Keep modal failure notifications above their dialogs**

Lower only success-confirmation-only stacks; existing delete, role and
assignment dialogs still rely on foreground error, warning and
information feedback. Preserve SSO inline recovery, retained-success
reopening and exact spaced access groups. Keep the SSO header as a
non-scrolling flex sibling instead of redundant sticky positioning.

Add real provider save/reopen/delete consumption and severity
regressions, with desktop and 320px native pixel proof bound to this
parent. The earlier heading discrepancy remains undiagnosed; all
selected candidate frames are readable.

---
Fleet candidate `20261024T021747Z-web-product` from the `web-product`
lane. Reviewed tip `4b74dbd5f9da47557958baa762bda89f6c48ac52` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 03:12:15 +00:00
pulse-triage[bot] 3f5dde6218 Keep mock memory personas below physical capacity (#3170)
Bound the shared role-adjusted memory range before point and series
sampling, leaving cache/free headroom rather than clipping only one
generated host. Preserve ordinary ranges, Docker CPU semantics and the
original fixture assertions. Register fixed-week sampler and host
lifecycle accounting regressions.

---
Fleet candidate `20261024T015631Z-core-runtime` from the `core-runtime`
lane. Reviewed tip `10dd31b3dc2686e8b64645a83be2088d1a4ef059` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 02:56:50 +00:00
pulse-triage[bot] 4b74dbd5f9 Keep modal failure notifications above their dialogs
Lower only success-confirmation-only stacks; existing delete, role and assignment dialogs still rely on foreground error, warning and information feedback. Preserve SSO inline recovery, retained-success reopening and exact spaced access groups. Keep the SSO header as a non-scrolling flex sibling instead of redundant sticky positioning.

Add real provider save/reopen/delete consumption and severity regressions, with desktop and 320px native pixel proof bound to this parent. The earlier heading discrepancy remains undiagnosed; all selected candidate frames are readable.

Change-source: pulse-maintainer
2026-10-24 03:26:45 +01:00
pulse-triage[bot] f19424f51a Complete credited chart evidence reporting on current main (#3169)
Isolate the retained PR3095 completion (7f4ec192 and 93d87118) without
importing unrelated history. Keep the exact proposed Python classifier
and maintained chart shells, real CLI/workflow controls and dependent
contracts. Inconclusive capacity never establishes the 90 ms budget;
child, cleanup and malformed-proof failures remain errors.

Source-PR: https://github.com/rcourtman/Pulse/pull/3095
Source-Commit: b31fabd778
Co-authored-by: rcourtman <8825017+rcourtman@users.noreply.github.com>

---
Fleet candidate `20261024T010002Z-delivery-trust` from the
`delivery-trust` lane. Reviewed tip
`7e7cea87a6bce2e7a2e00e888a676c571e141d92` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 02:26:12 +00:00
pulse-triage[bot] 518fda75b3 Retain the SSO recovery candidate on current main
Compose the original implementation and test correction without reparenting; an additive follow-up will preserve modal-only error notifications.

Change-source: pulse-maintainer
2026-10-24 03:18:33 +01:00
pulse-triage[bot] 10dd31b3dc Keep mock memory personas below physical capacity
Bound the shared role-adjusted memory range before point and series sampling, leaving cache/free headroom rather than clipping only one generated host. Preserve ordinary ranges, Docker CPU semantics and the original fixture assertions. Register fixed-week sampler and host lifecycle accounting regressions.

Change-source: pulse-maintainer
2026-10-24 03:01:45 +01:00
pulse-triage[bot] f1567d6e97 Isolate guest fixtures and safely observe pinned loser closure (#3168)
**Isolate guest fixtures and safely observe pinned loser closure**

Keep typed guest-agent cases on the existing namespaced fixture identity
so recycled loopback ports cannot inherit a previous synthetic guest
cooldown. Add a forced shared-listener regression without clearing the
production admission registry.

Set the pinned loser peer deadline before cancellation can close the
pipe, exercise the already-closed ordering deterministically, and verify
that the winning connection stays usable. No runtime, transport policy,
cooldown or admission behaviour changes.

Contract-Neutral: Repository-run test fixture isolation and closure
observation only; production guest and pinned-dial contracts are
unchanged.

**Read native TrueNAS SCALE aggregate CPU usage, including idle zero**

The reporting.realtime payload in #3158 places the actual aggregate at
fields.cpu.cpu.usage alongside per-core entries. Accept that reading
without treating temperature, absent/null usage or a core average as a
sample. Existing flat and nested usage/total shapes keep their
precedence.

Cover parser presence, legacy compatibility and repeated real TLS
JSON-RPC subscription through provider/registry/wire projection, with
unavailable and recovery controls. Update the monitoring contract in the
same commit.

---
Fleet candidate `20261024T011100Z-core-runtime` from the `core-runtime`
lane. Reviewed tip `5a852abe6695992d72f7600f44bd870e4de7f710` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 01:54:37 +00:00
pulse-triage[bot] fa4726c9c4 Align the SSO ownership guard with inline connection results
Retain canonical result presentation and failure formatting without requiring the background connection-test toast that the editor intentionally no longer emits. The full frontend run exposed this stale architecture assertion; normal UI tests already cover active-dialog feedback and no duplicate toast.

Change-source: pulse-maintainer
2026-10-24 02:54:19 +01:00
pulse-triage[bot] 21db610f6f Detach the Unraid boot entry so later go lines run (#3162)
## Problem

On Unraid, `scripts/install.sh` appended this to `/boot/config/go`:

```
# Pulse Agent
bash /boot/config/plugins/pulse-agent/start-pulse-agent.sh
```

`start-pulse-agent.sh` is an endless watchdog loop, so that line never
returns. Unraid's `rc.local` runs `go` (as `/var/tmp/go`, after
`fromdos`) to completion, so every line after the Pulse block never
runs. On a maintainer host, a reboot on 21 Oct skipped swap, a memory
logger and a hosts entry the agent needed to reach its Pulse server.
Pulse showed the host as down for two days while it was fine.

The same block's cleanup, `sed -i '/pulse-agent/d'`, deleted every `go`
line that mentioned `pulse-agent`, including log directories, hosts
entries and a co-installed agent's launch.

## Change

- `render_unraid_go_launch` writes the entry detached: `bash <wrapper>
>> /var/log/pulse-agent.log 2>&1 &`, with both paths `%q`-quoted. Plain
`&` rather than `setsid -f`, which needs util-linux 2.32+ and so may be
missing on older Unraid releases. Redirecting the output means `go`
holds no boot output pipe open.
- `write_unraid_go_entry` removes only the `# Pulse Agent` marker and
command lines that name a `start-pulse-agent.sh` wrapper or this runtime
binary outside a comment. That covers the earlier foreground entry,
hand-edited detached copies (including ones glued to a redirect) and
old-location wrappers, so an upgrade leaves exactly one launch. Other
lines that mention pulse-agent, comments and co-installed agents stay. A
trailing comment is only set aside when the scan is certain it is one. A
backslash, any `$` expansion, a backquote or an unterminated quote
before it makes the line doubtful, and a doubtful line is still checked
for a launch, because leaving a launch behind is the worse failure.
"Names" means the file name appears as a whole word: only a neighbouring
file-name character (letter, digit, `.`, `_`, `-`) rules a match out, so
`w=start-pulse-agent.sh` and `…start-pulse-agent.sh${x:+}` count. A
launch reached only indirectly, such as a relative path after `cd`, is
not detected. A name continued by another character (for example an
editor backup, `pulse-agent~`) still counts as the file, as it did under
the old sed.
- Kept lines lose a trailing CR, as `fromdos` does at boot anyway, so
the removal filter can parse the file afterwards. If removing a line
would leave `go` unparseable (for example, the line opened an `if`), the
writer leaves `go` untouched and the installer prints the line to use.
- `filter_nas_agent_boot_file` (uninstall) admits the detached entry as
well as the earlier foreground form, so removal keeps working for new
and existing installs.
- No other non-init platform had this problem: QNAP's autorun block is
already backgrounded, and TrueNAS, Synology, OpenRC, rc.d and SysV hand
off to a service manager or background the process.

## Tests

- `TestInstallSHUnraidGoEntryDoesNotBlockLaterBootLines` executes the
`go` file the installer writes, with output on a pipe. It requires `go`
to return, release the pipe, run the line after the Pulse entry and
start the wrapper. A planted foreground entry has to hang the same
harness. This replaces `TestUnraidGoScriptCleanup`, which re-typed the
sed commands inline and so could not catch this.
- `TestInstallSHUnraidGoEntryReplacesOnlyPulseLaunches` runs the real
writer against legacy, hand-edited, CRLF, old-location and direct-binary
entries, a command whose comment names the wrapper, and a removal that
would break an `if`. It checks the kept lines, that a second run changes
nothing, and that the removal filter admits what was written.
- `agent_nas_teardown.py` renders its Unraid fixture through the real
emitter, as it already does for QNAP. It keeps an earlier-installer
entry in the duplicate case. All 23 controls pass.
- Mutation checks on pulse-dev: each removal fails its test.
  - The foreground line fails with "go did not return".
- Backgrounding without a redirect fails with "kept the boot output pipe
open".
- The blanket sed, the syntax guard and its CRLF normalization, the `<>`
boundary, CR stripping, comment stripping, and quote- and escape-aware
comment detection each fail their own case.
- The writer gives byte-identical output under gawk, mawk and busybox
awk. The full `scripts/installtests` package passes on pulse-dev.

These are private bash fixtures. This is not native Unraid boot
acceptance.

## Follow-ups, not in this PR

- `read_nas_agent_removal_processes` treats a live `/boot/config/go`
shell as an unknown boot owner. Unraid actually runs `/var/tmp/go`, so
on hosts installed before this fix, uninstall does not recognise the
stuck go shell.
- The removal filter refuses any command line that names the wrapper,
even in a trailing comment. The installer now keeps such lines.
- `remove_qnap_autorun_block` still deletes every `autorun.sh` line
containing `start-pulse-agent.sh`.
2026-10-24 02:51:42 +01:00
pulse-triage[bot] be8fadc421 Keep revised secondary issue topics in the review queue (#3167)
The existing synchronizer loses appended, replaced or revised topics
after an earlier disposition because both field classifications remain
true. Compare the current declared text while preserving quiet label
deltas, Community ownership and stale-event/pre-write checks. Add
registered regression controls, keep the served guide aligned, and
record desktop/320px proof through the real documentation consumer.

Contract-Neutral: Repair the existing issue-topic queue and its
guidance; no Pulse runtime, API, label ownership, navigation or
subsystem contract change.

---
Fleet candidate `20261024T004022Z-product-intelligence` from the
`product-intelligence` lane. Reviewed tip
`6ab763c2e6dd7e58ac8bfb2bc52678c90e8bacb8` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 01:47:23 +00:00
pulse-triage[bot] ac36a9a94c Keep SSO recovery feedback in the active editor
Keep refused saves, clipboard outcomes and metadata failures beside reachable editor actions. Preserve exact spaced Allowed Groups on no-op and mapping-only saves for OIDC and SAML, without changing scope, domain, email or secret-omission semantics. Keep retained notifications below blocking dialogs while preserving inertness. Cover correction/reopen, pending submit, clipboard refusal and modal restoration through normal suites and source-bound desktop/phone browser evidence.

Change-source: pulse-maintainer
2026-10-24 02:37:38 +01:00
pulse-triage[bot] a079862edc Qualify missing Coverage restore evidence with read quality (#3166)
Unreadable or partial provider history must not read as confirmed
absence. Keep missing ages unknown until raw and posture reads plus
provider history/access can support an empty observation, preserve known
success outside the raw list, and retain all independently visible
backup evidence. Cover consumed API failures, isolated retry, accepted
empty scope and provider quality controls.

Contract-Neutral: Enforces existing incomplete-history and
scope-evidence semantics in Coverage copy; no policy, API, permission or
recovery-model change.

---
Fleet candidate `20261024T001706Z-web-product` from the `web-product`
lane. Reviewed tip `1332c4bc80428ee051844e3e631be15e3d18147b` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 01:14:17 +00:00
pulse-triage[bot] 5a852abe66 Compose native SCALE CPU repair with current main
Retain main's guest-agent fixture isolation in the two overlapping hunks, the deterministic recycled-listener regression, and main's additional per-field fixture fix. Import the unchanged native aggregate CPU repair and pinned loser observation correction. No production guest safeguards change.

Change-source: pulse-maintainer
2026-10-24 02:12:05 +01:00
pulse-triage[bot] 7e7cea87a6 Reconcile chart reporting with current Patrol contracts
Compose retained 8478eac3 additively on current main. Preserve both independently appended contract sections at each of the three documentation conflicts; workflow, registered executable controls and classifier stay unchanged.

Change-source: pulse-maintainer
2026-10-24 02:02:24 +01:00
pulse-triage[bot] 6ab763c2e6 Keep revised secondary issue topics in the review queue
The existing synchronizer loses appended, replaced or revised topics after an earlier disposition because both field classifications remain true. Compare the current declared text while preserving quiet label deltas, Community ownership and stale-event/pre-write checks. Add registered regression controls, keep the served guide aligned, and record desktop/320px proof through the real documentation consumer.

Contract-Neutral: Repair the existing issue-topic queue and its guidance; no Pulse runtime, API, label ownership, navigation or subsystem contract change.
Change-source: pulse-maintainer
2026-10-24 01:59:27 +01:00
rcourtman 450ed161c6 Isolate guest-agent test fixtures from recycled ports (#3163)
Guest-agent fences intentionally outlive a closed fixture listener and
are keyed by endpoint URL. A test whose client used a fixture's raw
loopback URL therefore inherited an earlier case's cooldown whenever the
kernel handed its server a recycled port, and its first read came back
`agent-cooldown` instead of what the test expected. `Backend tests
(rest-0)` failed this way twice in a row on rcourtman/Pulse#3151
(`TestGuestAgentConfigResponseMustBeComplete`, then
`TestGuestAgentTypedSuccessDefersEveryRead`), and one in twelve local
runs of the guest-agent tests failed the same way.

- Single-server and per-case fixtures now use
`guestAgentFixtureEndpoint`, the package's existing per-case boundary
that the HTTP status and lock evidence tests already use. Clients within
one case still share their key.
- Tests whose clients share fences across raw endpoints by design (the
alias and cluster tests), and the older client and cluster API tests
that read guest data through helpers that own their server, start from
an empty guest registry (`isolateGuestAgentCapacity`) instead.
- The capacity tests keep raw endpoints: they seed protected work under
them and already reset the registry. Converting them breaks what they
prove.
- Inventory, node and config fixtures have no guest fences and are
unchanged, as is the untrusted-TLS test, which sends its requests
outside the guarded reads.

Testing
- On pulse-dev, before: 1 of 12 fresh-process runs of the guest-agent
tests failed. Converting only the two tests that failed in CI moved the
failure to a third test (`TestGuestAgentWireDoesNotFollowRedirect`).
After this change: 0 of 20 runs of the guest-agent, cluster-client and
client tests failed.
- `go test ./pkg/proxmox` passes with and without `-race`, gofmt and vet
are clean, and the registry, contract, completion and browser guards
pass on this commit.
2026-10-24 01:51:59 +01:00
rcourtman 871d94f53d Serialize Patrol action projection and finalize hung runs (#3151)
Three gaps remained in how Pulse projects a Patrol investigation's
action and closes its runs, after rcourtman/Pulse#3147.

- **Projections are serialized per investigation.** The lifecycle's
transition callback, an investigation read and a run's close each read
the action audit and apply it under the investigation's lock. A run's
close also finalizes before releasing it. A projection read from an
older state can no longer land after a newer one, and an approval can no
longer land between a run's reconciliation and its finalization.
- **The projection writes only what changed.** It records the changed
action and outcome through `SetAction` and `SetOutcome` when the store
offers `aicontracts.InvestigationActionStore` (a new optional interface;
a store without it keeps the old `Update`). It therefore cannot undo run
progress the orchestrator saved in between. The reference it writes
carries the broker's canonical result and verification status
(`dispositionFromRecord`) and keeps the model's causal attribution,
which the audit does not hold.
- **Runs that hang past their limit are finalized.** The stuck-run sweep
finalizes such a run with a copy of its session marked failed, leaving
the orchestrator's own session as it is. Once the orchestrator closes
the run itself, its stored session is finalized instead of the copy, and
a later refresh no longer reopens a record that finalization closed.
- **A recovered run keeps its own status** (second commit). The sweep
used to mark a run that a restart or its limit cut short after it
planned a fix as `completed`, because the finding already held the
action's outcome, while its session and record said `failed`. The
orchestrator records the same run as `failed` when it closes itself, and
every gate against a second plan keys on the `fix_queued` outcome, so
the sweep now keeps the outcome and the run's own status.

The matching pulse-enterprise change makes the orchestrator's saves keep
the action Pulse recorded, and gives its store `SetAction`. Each side
works without the other.

Testing
- New tests cover the per-investigation lock, field-only writes,
hydration returning the stored action after a refresh replaced the plan,
a close preferring the orchestrator's own close over the sweep's copy
and finalizing under the lock, a run past its limit finalized as given
up, a refresh not reopening a finalized record, the full action
reference, and a recovered run keeping the status it closed with.
Removing any one of the fixes fails its test.
- On pulse-dev: gofmt, vet, race runs of the targeted tests, and full
runs of `internal/ai/...`, `internal/api`, `internal/actionlifecycle`,
`internal/unifiedresources/...` and `pkg/aicontracts/...`. The registry,
contract, completion and browser guards pass on both commits.
- Live, on a private Pro stack built from this branch and
pulse-enterprise main, with a real model (Claude Sonnet through the
subscription CLI) investigating a Docker container whose web server had
died. In each run the model planned a container restart through
`patrol_propose_action`, and the next model turn was made to fail:
- **Provider error after the plan.** The run closed failed with the
error, `fix_queued` and the pending restart on the session, the finding
and its record. Re-investigation returned 409 `action_pending`, the
restart waited in Actions, and the finding's review showed the queued
fix with its plan. When the plan expired, the sweep published it, and
the session, finding and record moved to `needs_attention` with the
action expired.
- **Deadline after the plan.** With a 300-second limit and the next turn
hung, the run failed at the deadline with `fix_queued`, not `timed_out`,
and the next Patrol pass did not retry it.
- **Restart after the plan.** Pulse was killed mid-run. On boot the
stored session closed as interrupted with the plan kept,
re-investigation still returned 409, and once the grace passed a Patrol
pass recovered and finalized the run. With the second commit the finding
reads `failed`, matching its session and record; before it, it read
`completed`.
- Not observable on a local stack: the approval push, which needs a
relay client, and a run hanging past its limit without closing, since
the provider call is cancelled at the deadline. Unit tests cover both.
2026-10-24 01:39:10 +01:00
pulse-triage[bot] 1332c4bc80 Qualify missing Coverage restore evidence with read quality
Unreadable or partial provider history must not read as confirmed absence. Keep missing ages unknown until raw and posture reads plus provider history/access can support an empty observation, preserve known success outside the raw list, and retain all independently visible backup evidence. Cover consumed API failures, isolated retry, accepted empty scope and provider quality controls.

Contract-Neutral: Enforces existing incomplete-history and scope-evidence semantics in Coverage copy; no policy, API, permission or recovery-model change.
Change-source: pulse-maintainer
2026-10-24 01:27:22 +01:00
pulse-triage[bot] 72c5579c55 Deliver the credited VMware refresh budget on current main (#3160)
Isolate Core's completed 7b633c75 repair from its obsolete containing
history. Preserve the contributor's twenty-second default and positive
whole-refresh override without logging raw invalid environment input,
with registered lifecycle, cancellation, cached-inventory and
serial-dispatch controls plus configuration guidance. Current VMware
runtime and test files still match the original parent exactly; preserve
all later unrelated contracts and documentation.

Original-contribution: https://github.com/rcourtman/Pulse/pull/3042

---
Fleet candidate `20261023T224436Z-delivery-trust` from the
`delivery-trust` lane. Reviewed tip
`00c1f53272243d1ddb6911a898212524c82d2495` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 00:13:49 +00:00
pulse-triage[bot] abeb71321e Hide Re-investigate while a planned fix is queued (#3161)
**Hide Re-investigate while a planned fix is queued**

A Patrol investigation can plan a fix and then fail, normally on the
final summary call. The run then shows a queued fix with a failed
status, and the Investigation section offered Re-investigate because
it only looked at the status. Another run would plan a second action
for the same problem while the first still waits for approval.

The section now hides Re-investigate while the outcome is fix_queued,
as it already did for a completed run with a queued fix. The failed
run's note uses the muted style of an interrupted run, because the fix
awaiting approval is what the operator acts on, not the error. The
Pro orchestrator keeps the plan's outcome on such a run and refuses the
re-investigation request too.

**Keep snapshot warnings when creation dates are unknown**

Distinguish a listed snapshot with an unusable date from removal. Retain
the last trusted canonical occurrence and evidence after current policy
resolution, including Proxmox's missing-snaptime Unix epoch
representation. Add lifecycle, exclusion, identity and real-consumer
browser proof fixtures.

Contract-Neutral: Repair existing snapshot warning preservation on
incomplete evidence; no API, configuration, schema or canonical identity
change. Dedicated regression and browser-producer tests accompany the
internal contract clarification.

**Keep the global alert pause contract in snapshot regression**

The focused source proof shows that global alerting disablement pauses
evaluation without resolving existing incidents. Assert exact trusted
occurrence preservation for that case; retain clearing assertions for
snapshot/guest disablement, exclusions, threshold changes and removal.
No production source changes.

**Cover failed snapshot listings without treating errors as deletion**

Exercise both guest kinds, partial error results, same-VMID peer
isolation, warning continuity, accepted removal and explicit disablement
through the actual monitor. Include opt-in synthetic frames for the
production alert consumer.

**Use the served alert model in snapshot collection proofs**

The monitor exposes models.Alert, not the internal manager alert type.
Correct the fixture-only type mismatch before establishing the
unchanged-runtime control.

**Record the browser pass for the queued-fix investigation view**

The governance check needs a browser receipt for the final content of
InvestigationSection.tsx. A private Pro build of this content was
checked at 1024 and 375 wide with a finding whose run failed after
planning a restart: Fix queued, the run's note in the muted style, no
Re-investigate, and the restart awaiting approval below it.

**Keep known LXC snapshots when their listing is unavailable**

Treat every failed snapshot read as incomplete rather than interpreting
596 or unavailable text as an empty successful listing. Preserve known
membership and warnings while independent accepted listings and
confirmed removal continue normally. Correct the isolation fixture to
use cluster-unique VMIDs, keeping the same-VMID cross-instance control.

Contract-Neutral: Restores existing failed-listing semantics for LXC; no
public API, schema, configuration, identity or alert policy delta.
Dedicated collection and producer/browser regressions are outside the
registry's fixed verification filenames.

**Reproduce PVE partial-scan recovery history loss**

Drive ordinary backup polling through durable recovery ingest after live
inventory replacement, with partial, complete, isolation and
cancellation controls.

**Retain PVE recovery history across incomplete backup scans**

Use the poll's existing completeness evidence to admit destructive
reconciliation. Failed or skipped reads may update accepted rows without
deleting unseen durable facts, and cancellation cannot publish an empty
success. Complete deletions remain scoped and tested.

Contract-Neutral: Restore collection truth without changing APIs,
schemas, configuration or protection policy.

**Keep carried PVE rows from renewing recovery observations**

Retain their existing durable point evidence under the partial provider
observation instead of presenting a failed refresh as a fresh artifact
observation. Cover warm inventory alongside the cold-start
reconciliation regression.

Contract-Neutral: No API, schema, configuration or protection-policy
delta.

**Match the full workload identity in the PVE browser journey**

Use the production disclosure's guest name and VM identity, and bring
its provider evidence into the captured viewport. The initial selector
timeout is retained as a harness failure, not a product result.

**Avoid the published monitoring contract insertion conflict**

Relocate the PVE partial-backup collection contract without changing its
text so it composes with the published snapshot-listing contract at
15ea29fe2b. Runtime, regression tests and
browser-proof inputs remain unchanged.

**Finalize Patrol runs when they close, failed runs included**

A Patrol run that planned a fix and then failed, normally on its final
model call, was never finalized. Core's run goroutine returned on the
error before it rebuilt the investigation record, published the
unified finding and sent the approval push, so the record kept showing
the run as running and nobody was asked to approve the queued fix.
Manual re-investigations never reached that goroutine, even when they
succeeded, and a run a restart interrupted was only marked closed.

Core now finalizes a run when its close lands on the finding or when
the stuck-run sweep recovers it with a closed session. It first
reconciles the run's action from the action audit, because the
orchestrator writes the action back as planned and an approval or
expiry during the final model call may have overtaken it. For the same
reason a close no longer moves a finding back to fix_queued from an
outcome the action reached during the run. Finalization skips a run
the finding no longer names and asks for approval once per action. An
action's outcome keeps its single push from transition reconciliation,
so a close no longer sends a second fix-completed notification.

The action inbox and decision-queue expiry sweeps also publish the
plans they expire, as a single action's expiry already was, so a
Patrol finding's queued fix moves to needs_attention when its plan
expires. Reconciliation follows refresh lineage to ignore a transition
for a plan that the investigation's current action replaced, so the
expiry of a refreshed plan cannot take the investigation back.

**Refresh PBS inventory when its newest backup is removed**

A capped snapshot cache can keep the same retained count after pruning
its newest archive. Require the group's newest timestamp to match
exactly before reusing it, so the deleted recent archive cannot keep
stale backups looking protected. Exercise VM and container polling
through successful, unavailable and denied refreshes, unchanged-cache
reuse, recovery and confirmed removal.

**Preserve prior installer snapshots on retry**

Reconcile the retained snapshot publication repair with current main
without importing unrelated containing history. Unique private staging,
no-clobber publication and failure-before-mutation protect earlier
recovery copies; retain zero-retention and hardened-unit fallback
semantics.

Retained-source: 854ea3c81a94ffe88145d663d25d5040d4637a47,
91bd7393fc4413b6629ad851f90c2c7f6c2c0991

**Keep Patrol finding reviews in place across refreshes**

On the Patrol Activity tab, an open finding review threw the reader back
to the top of the page: on opening it, opening its thread, on
Re-investigate and on every 30s and 60s findings reload. Each reload
parsed new finding objects, so FindingsPanel rebuilt every row and
remounted the open review. The review read its investigation with
createResource, and the nearest Suspense boundary is the app's route
boundary: its "Loading view..." fallback replaced the whole page, the
scroll shell's content collapsed and its position clamped to 0.

The findings store now keeps unchanged findings, and the list, across
reads. FindingsPanel keeps display groups whose findings did not change,
settles sort ties by id because the API returns tied findings in no
fixed order, and gives the open review its related findings through a
getter. The review's investigation, governed action and thread, and a
run's tool calls, read through createNonSuspendingQuery.

Refreshes no longer remount the review, which they had also used to
re-read it and keep its ages current. The review re-reads on its own
(every 30s, every 5s while a run is active), so createNonSuspendingQuery
accepts a pollMs accessor. A failed read keeps the last answer,
Re-investigate waits for the first read, the thread follows its run's
session, and mounted ages read the shared relative-time clock.

Contract-Neutral: api-contracts: the findings store keeps its payloads,
normalization and routes; it only reuses unchanged finding objects
across reads

**Honour temperature preferences in platform tables**

Use the existing reactive display formatter for Docker and TrueNAS
readings and keep the TrueNAS phone unit heading and full value legible.
Preserve Celsius observations, thresholds, sorting and missing-reading
handling. Regressions cover live preference changes, round trips and
invalid readings; actual General settings and production HTTP/table
consumers are exercised at desktop and 320px.

Contract-Neutral: Apply the existing temperature display preference and
preserve missing-reading semantics; no API, stored Celsius value,
threshold, identity or platform scope change.

**Cover disk temperature preference and observation state**

Exercise the third shared formatter consumer with current and retained
disk readings, preserving last-known state and Celsius observations
while the display unit changes. Update the existing shared-primitive
guard to require the reactive formatter instead of its obsolete
Celsius-only helper name.

Contract-Neutral: Test-only support for the existing temperature
preference repair; no runtime, API or subsystem contract delta.

---
Fleet candidate `20261023T190350Z-web-product` from the `web-product`
lane. Reviewed tip `3e3fbae0892ec0990af8370dbcdf1b8b411dadc9` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-24 00:06:20 +00:00
pulse-triage[bot] e14229c4d6 Read native TrueNAS SCALE aggregate CPU usage, including idle zero
The reporting.realtime payload in #3158 places the actual aggregate at fields.cpu.cpu.usage alongside per-core entries. Accept that reading without treating temperature, absent/null usage or a core average as a sample. Existing flat and nested usage/total shapes keep their precedence.

Cover parser presence, legacy compatibility and repeated real TLS JSON-RPC subscription through provider/registry/wire projection, with unavailable and recovery controls. Update the monitoring contract in the same commit.

Change-source: pulse-maintainer
2026-10-24 00:59:18 +01:00
pulse-triage[bot] d6e72532e3 Isolate guest fixtures and safely observe pinned loser closure
Keep typed guest-agent cases on the existing namespaced fixture identity so recycled loopback ports cannot inherit a previous synthetic guest cooldown. Add a forced shared-listener regression without clearing the production admission registry.

Set the pinned loser peer deadline before cancellation can close the pipe, exercise the already-closed ordering deterministically, and verify that the winning connection stays usable. No runtime, transport policy, cooldown or admission behaviour changes.

Contract-Neutral: Repository-run test fixture isolation and closure observation only; production guest and pinned-dial contracts are unchanged.
Change-source: pulse-maintainer
2026-10-24 00:54:09 +01:00
rcourtman 8399207f91 Isolate guest-agent test fixtures from recycled ports
Guest fences intentionally outlive a closed fixture listener and are
keyed by endpoint URL, so a test whose client used a fixture's raw
loopback URL inherited an earlier case's cooldown whenever the kernel
handed its server a recycled port. Its first read then came back
agent-cooldown instead of what the test expected. That failed rest-0
twice in a row on one pull request, and one in twelve local runs of
the guest-agent tests.

Single-server and per-case fixtures now use guestAgentFixtureEndpoint,
the package's existing per-case boundary, as the HTTP status and lock
evidence tests already do. Tests whose clients share fences across raw
endpoints by design, the alias and cluster tests and the older client
API tests, start from an empty guest registry instead. The capacity
tests keep raw endpoints: they seed protected work under them and
already reset the registry.

None of twenty fresh-process runs failed afterwards, and the package
passes with and without the race detector.
2026-10-24 00:50:38 +01:00
rcourtman 0ff9b26fb1 Keep a recovered Patrol run's own status
The stuck-run sweep closed a run that a restart or its limit cut short
after it planned a fix as completed, because the finding already held
the action's outcome. That run's session and record say failed, and
the orchestrator records the same run as failed when it closes itself,
so the sweep path disagreed with every other surface about one event.

The coercion kept a queued fix blocking another investigation. That no
longer needs the status: Patrol's own gate, the orchestrator's re-run
check and the API's re-investigate check all key on the fix_queued
outcome or a running status. Recovery now keeps the action's outcome
and the status the run closed with.

Found in a live run on a private Pro stack, where a real model planned
a container restart and Pulse was killed before the run finished.
2026-10-24 00:41:23 +01:00
rcourtman 4f3e170612 Detach the Unraid boot entry so later go lines run
The Unraid installer appended `bash <wrapper>` to /boot/config/go. The
wrapper is an endless watchdog loop, so that line never returned, and
rc.local never reached anything the user had placed after the Pulse
block. On a maintainer host a reboot skipped swap, a memory logger and a
hosts entry the agent needed to reach its server, and Pulse showed the
host as down for two days while it was healthy.

The entry now backgrounds the wrapper with its output redirected, so go
returns and holds no boot output pipe open. Install-time cleanup used to
delete every go line containing "pulse-agent", including log directories,
hosts entries and a co-installed agent. It now removes only the
installer's marker and command lines that name a start-pulse-agent.sh
wrapper or this runtime binary outside a comment, so upgrades from the
foreground or hand-edited entry still leave exactly one launch. Kept
lines lose a trailing CR, as Unraid's fromdos does before running go, and
a removal that would leave go unparseable is refused with a warning.
Uninstall admits the detached entry as well as the earlier foreground
form.

The new tests execute the go file the installer writes and require it to
return, release its output pipe and run the next line, with a planted
foreground entry as the control, and pin the narrowed cleanup against
the removal filter. The NAS teardown controls now render their Unraid
entry through the same emitter.
2026-10-24 00:34:31 +01:00
pulse-triage[bot] beec19d2e1 Read declared issue versions from visible release-link text (#3159)
A version supplied as a Markdown release link currently becomes
needs-version-info even though its visible label states the version.
Read only that label and ordinary inline formatting; never mine
destinations, image text or literal Markdown for a version. Keep
current-report checks, silent metadata deltas and Community labels
unchanged.

Contract-Neutral: Recognise visible inline version values within the
existing issue metadata contract; no product runtime, API or label
ownership changes.

---
Fleet candidate `20261023T223549Z-product-intelligence` from the
`product-intelligence` lane. Reviewed tip
`d972257ce110aaddd99e58e2c6fa0c993418e5cf` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-23 23:30:40 +00:00
rcourtman 8fc2ef2931 Remove unreferenced one-off browser proof harnesses (#3155)
Fleet lanes committed a fixture page, build script and check script
alongside each fix to prove it once in a browser. This removes the 168
such files (about 19,850 lines) that nothing in the repository names: no
subsystem contract, release verification matrix, test, workflow, package
script or live harness. Harnesses the contracts and the high-risk
verification matrix describe, and the modules they import, stay. The
browser verification guard covers `frontend-modern/src` only, so no
receipt is involved.
2026-10-24 00:26:52 +01:00
pulse-triage[bot] 8478eac3ef Complete credited chart evidence reporting on current main
Isolate the retained PR3095 completion (7f4ec192 and 93d87118) without importing unrelated history. Keep the exact proposed Python classifier and maintained chart shells, real CLI/workflow controls and dependent contracts. Inconclusive capacity never establishes the 90 ms budget; child, cleanup and malformed-proof failures remain errors.

Source-PR: https://github.com/rcourtman/Pulse/pull/3095
Source-Commit: b31fabd778
Co-authored-by: rcourtman <8825017+rcourtman@users.noreply.github.com>
Change-source: pulse-maintainer
2026-10-24 00:26:11 +01:00
pulse-triage[bot] fe06bf7f83 Keep failed PBS namespace discovery out of complete backup evidence (#3157)
**Keep failed PBS namespace discovery out of complete backup evidence**

Carry namespace discovery errors with the immutable datastore snapshot
into background backup polling. Readable root history still updates, but
unknown child namespaces cannot delete durable facts or claim complete
protection. Cover real HTTP discovery through ingest, replacement state,
repeated failures, scoped removal, recovery and legacy root-only
support.

**Initialize the connection outcome map in namespace poll regressions**

The fixture drives the real connection poll and therefore needs its
ordinary poll-status map, in addition to backup scheduling and recovery
state. Keep the new lifecycle proof independent of fixture setup panics.

**Account for accepted new archives beside retained partial PBS
history**

The namespace lifecycle test advances the root archive timestamp. A
partial instance-wide scan must retain the two historical points and
accept that new root point, not replace all root history. Assert every
historical point remains byte-identical and exactly one independently
accepted new point is present.

**Distinguish failed PBS discovery from an empty backup scope**

Carry failed and accepted-empty discovery through ordinary serialized
backup polling. Preserve unreadable history, reconcile only confirmed
absence, and reject malformed datastore envelopes before destructive
reconciliation.

Contract-Neutral: Enforces existing PBS scope-evidence semantics; no
API, model, policy or permission change.

**Model an existing PBS interval in the skipped-scope regression**

The live interval setter deliberately schedules a catch-up poll. Test
not-due admission with an already-configured interval and its retained
completed-poll timestamp instead.

---
Fleet candidate `20261023T220242Z-core-runtime` from the `core-runtime`
lane. Reviewed tip `d1ecdcb93916f236f966897230541a66850fe3a5` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-23 23:18:11 +00:00
pulse-triage[bot] 3e3fbae089 Cover disk temperature preference and observation state
Exercise the third shared formatter consumer with current and retained disk readings, preserving last-known state and Celsius observations while the display unit changes. Update the existing shared-primitive guard to require the reactive formatter instead of its obsolete Celsius-only helper name.

Contract-Neutral: Test-only support for the existing temperature preference repair; no runtime, API or subsystem contract delta.
Change-source: pulse-maintainer
2026-10-24 00:11:30 +01:00
pulse-triage[bot] 7e2dcbbe36 Deliver the credited MCP boolean-filter repair on current main (#3156)
Reconstitute the exact runtime and executable proof from Core 4651beb4
without importing its broad unlanded history. The four affected source
files still match that fix parent byte-for-byte. Preserve current
subsystem contracts while inserting the existing typed-filter
obligations.
Boolean true and false survive MCP HTTP projection; path and private
metadata, supported value types, write bodies and authentication stay
bounded.

Original-contribution: https://github.com/rcourtman/Pulse/pull/3097
Reported-by: X1pheR (Pulse PR #3097)
Source-Commit: 4651beb4c8

---
Fleet candidate `20261023T215543Z-delivery-trust` from the
`delivery-trust` lane. Reviewed tip
`4081635115b8db4ae249977d8ef3e78ab44a1073` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-23 23:10:01 +00:00
pulse-triage[bot] d450e2894a Honour temperature preferences in platform tables
Use the existing reactive display formatter for Docker and TrueNAS readings and keep the TrueNAS phone unit heading and full value legible. Preserve Celsius observations, thresholds, sorting and missing-reading handling. Regressions cover live preference changes, round trips and invalid readings; actual General settings and production HTTP/table consumers are exercised at desktop and 320px.

Contract-Neutral: Apply the existing temperature display preference and preserve missing-reading semantics; no API, stored Celsius value, threshold, identity or platform scope change.
Change-source: pulse-maintainer
2026-10-24 00:03:22 +01:00
pulse-triage[bot] d972257ce1 Read declared issue versions from visible release-link text
A version supplied as a Markdown release link currently becomes needs-version-info even though its visible label states the version. Read only that label and ordinary inline formatting; never mine destinations, image text or literal Markdown for a version. Keep current-report checks, silent metadata deltas and Community labels unchanged.

Contract-Neutral: Recognise visible inline version values within the existing issue metadata contract; no product runtime, API or label ownership changes.
Change-source: pulse-maintainer
2026-10-23 23:49:45 +01:00
pulse-triage[bot] 00c1f53272 Deliver the credited VMware refresh budget on current main
Isolate Core's completed 7b633c75 repair from its obsolete containing history. Preserve the contributor's twenty-second default and positive whole-refresh override without logging raw invalid environment input, with registered lifecycle, cancellation, cached-inventory and serial-dispatch controls plus configuration guidance. Current VMware runtime and test files still match the original parent exactly; preserve all later unrelated contracts and documentation.

Original-contribution: https://github.com/rcourtman/Pulse/pull/3042
Change-source: pulse-maintainer
2026-10-23 23:48:47 +01:00
pulse-triage[bot] 4081635115 Deliver the credited MCP boolean-filter repair on current main
Reconstitute the exact runtime and executable proof from Core 4651beb4
without importing its broad unlanded history. The four affected source
files still match that fix parent byte-for-byte. Preserve current
subsystem contracts while inserting the existing typed-filter obligations.
Boolean true and false survive MCP HTTP projection; path and private
metadata, supported value types, write bodies and authentication stay bounded.

Original-contribution: https://github.com/rcourtman/Pulse/pull/3097
Reported-by: X1pheR (Pulse PR #3097)
Source-Commit: 4651beb4c8
Change-source: pulse-maintainer
2026-10-23 23:37:42 +01:00
rcourtman acd353f30f Keep Patrol finding reviews in place across refreshes (#3154)
## What was wrong

On the Patrol page's Activity tab (Finding options and history), an open
finding review threw the reader back to the top of the page:

- on opening the review (1389px to 0 at 1024x768),
- on opening its investigation thread,
- on Re-investigate and on each 5-second poll of a running
investigation,
- on every 30s app-shell and 60s Patrol-page findings reload (a reader
at 2715px dropped to 0 at about 25s and again at 55s).

Each reload parsed new finding objects, so `FindingsPanel` rebuilt every
row and remounted the open review. The review read its investigation
with `createResource`, and the nearest `<Suspense>` above it is the
route boundary in `AppLayout.tsx`. Its "Loading view..." fallback
replaced the whole page, so the scroll shell's content collapsed and its
position clamped to 0. The rebuild also closed the review's thread and
reset its inner scroll. Separately, the API returns tied findings in no
fixed order, so rows swapped places between polls.

## What changed

- **Findings store:** a read that returns a finding unchanged keeps its
object, and a read that changes nothing keeps the list.
- **FindingsPanel:**
- keeps display groups whose findings did not change
(`reuseIdenticalPatrolFindingDisplayGroups`);
  - breaks sort ties by finding id;
- passes the open review its related findings through a getter, so it
redraws only when its own finding changes;
- reads row and review ages from the shared relative-time clock (they
had only moved because rows were rebuilt).
- **InvestigationSection, ApprovalSection, InvestigationMessages:** read
through `createNonSuspendingQuery` instead of `createResource`.
Refreshes no longer remount them, so they re-read on their own:
- the investigation and its governed action every 30s, and the
investigation every 5s while it runs;
- the thread every 5s while the run is live, once more when it finishes,
and 30s after a failed read.

A failed read keeps the last answer; only a 404 means "no
investigation". Re-investigate waits for the first read. The thread is
keyed by the run's session and keeps unchanged message rows, along with
any expanded tool output.
- **`createNonSuspendingQuery`:** `pollMs` also accepts an accessor, so
a cadence can follow state through the hook's protected poll path, which
never replaces a slow read still in flight. The numeric path is
unchanged.
- **RunToolCallTrace:** moves to the same query. It did not suspend
before (its reads checked `loading` first), so this is for consistency,
not a fix.
- **Contracts:** notes in `patrol-intelligence.md` (Forbidden Paths 6
and a Current State note) and `frontend-primitives.md`.

## Verification

- **Browser.** Private Pulse Pro stack (enterprise binary built from
main, seeded Patrol findings and investigation sessions), headless
Chromium at 1024x768, 1440x900 and 390x844:
  - Opening a review keeps the page where it was.
- Over 70s with the review open (two 30s reloads, one 60s reload and a
30s re-read of the investigation) the page stays put: no fallback, no
row or review content rebuilt, rows in the same order, the thread still
open and the review's inner scroll unchanged.
  - A 150-second watch with unchanged findings moved no row.
- Main at 1024x768: 1389px to 0 on open, 2715px to 0 at about 25s and
55s, and the row and review content rebuilt three times in 70s.
- Receipt: `frontend-modern/browser-verification/14b0b05ccd7e4c6f.json`.
- **Tests.**
- New `FindingsPanel.refresh.test.tsx` drives the real store, panel and
review sections under a Suspense boundary.
- Also adds to the store, display-group, hook and four component tests.
- Negative controls: every new test fails against main's runtime files.
Reverting any one layer (store, panel or sections) fails the refresh
test. Main's hook fails the cadence, retry and live-thread tests.
Removing the message reuse or the final thread read fails their tests.
- **Checks.**
- Targeted suites (60 files, 1267 tests), type-check, the full `npm run
lint` chain and Prettier pass.
- The full frontend suite passed except one 5-second timeout in
`dependencyTooling.test.ts` under heavy load, which passes on its own.
  - Committed with the pre-commit hooks.

## Follow-ups, not changed here

- The API still returns tied findings in no fixed order:
`GetAllFindings` and `GetAllFindingsIncludingResolved` use `sort.Slice`
over map iteration. The panel no longer depends on that order.
- A change to the open finding itself still redraws its review, which
closes the thread, but the page no longer moves.
- Below 1280px the review renders under the whole findings list, so
opening one from a row far down leaves it out of view until the reader
scrolls.
2026-10-23 23:31:59 +01:00
pulse-triage[bot] 59625a2996 Preserve prior installer snapshots on retry (#3152)
Reconcile the retained snapshot publication repair with current main
without importing unrelated containing history. Unique private staging,
no-clobber publication and failure-before-mutation protect earlier
recovery copies; retain zero-retention and hardened-unit fallback
semantics.

Retained-source: 854ea3c81a94ffe88145d663d25d5040d4637a47,
91bd7393fc4413b6629ad851f90c2c7f6c2c0991

---
Fleet candidate `20261023T213224Z-delivery-trust` from the
`delivery-trust` lane. Reviewed tip
`b79496219fe66ccce551efcde4ee7c24f12ddc6d` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-23 22:21:42 +00:00
rcourtman 6a45411bd1 Remove unreferenced one-off browser proof harnesses
Fleet lanes committed a fixture page, build script and check script
alongside each fix to prove it in a browser once. 168 of those files,
about 19,850 lines, are named by nothing in the repository: no
subsystem contract, release verification matrix, test, workflow,
package script or live harness. Harnesses the contracts and the
high-risk verification matrix describe, and the modules they import,
stay. The lanes no longer commit proof-only fixtures.

Contract-Neutral: removes proof fixtures no contract, registry entry or test references; no runtime or contract change
2026-10-23 23:18:30 +01:00
pulse-triage[bot] d1ecdcb939 Model an existing PBS interval in the skipped-scope regression
The live interval setter deliberately schedules a catch-up poll. Test not-due admission with an already-configured interval and its retained completed-poll timestamp instead.

Change-source: pulse-maintainer
2026-10-23 23:13:40 +01:00
pulse-triage[bot] aaa6aaa807 Distinguish failed PBS discovery from an empty backup scope
Carry failed and accepted-empty discovery through ordinary serialized backup polling. Preserve unreadable history, reconcile only confirmed absence, and reject malformed datastore envelopes before destructive reconciliation.

Contract-Neutral: Enforces existing PBS scope-evidence semantics; no API, model, policy or permission change.
Change-source: pulse-maintainer
2026-10-23 23:11:44 +01:00
rcourtman c46dd92777 Keep Patrol finding reviews in place across refreshes
On the Patrol Activity tab, an open finding review threw the reader back
to the top of the page: on opening it, opening its thread, on
Re-investigate and on every 30s and 60s findings reload. Each reload
parsed new finding objects, so FindingsPanel rebuilt every row and
remounted the open review. The review read its investigation with
createResource, and the nearest Suspense boundary is the app's route
boundary: its "Loading view..." fallback replaced the whole page, the
scroll shell's content collapsed and its position clamped to 0.

The findings store now keeps unchanged findings, and the list, across
reads. FindingsPanel keeps display groups whose findings did not change,
settles sort ties by id because the API returns tied findings in no
fixed order, and gives the open review its related findings through a
getter. The review's investigation, governed action and thread, and a
run's tool calls, read through createNonSuspendingQuery.

Refreshes no longer remount the review, which they had also used to
re-read it and keep its ages current. The review re-reads on its own
(every 30s, every 5s while a run is active), so createNonSuspendingQuery
accepts a pollMs accessor. A failed read keeps the last answer,
Re-investigate waits for the first read, the thread follows its run's
session, and mounted ages read the shared relative-time clock.

Contract-Neutral: api-contracts: the findings store keeps its payloads, normalization and routes; it only reuses unchanged finding objects across reads
2026-10-23 23:08:34 +01:00
pulse-triage[bot] a57ec58ef6 Merge commit '5db3120f3645b68b3ec5cd27c187f8e95b666d06' into maintainer-async/20261023T220242Z-core-runtime/pulse
Change-source: pulse-maintainer
2026-10-23 23:04:09 +01:00
pulse-triage[bot] b79496219f Preserve prior installer snapshots on retry
Reconcile the retained snapshot publication repair with current main without importing unrelated containing history. Unique private staging, no-clobber publication and failure-before-mutation protect earlier recovery copies; retain zero-retention and hardened-unit fallback semantics.

Retained-source: 854ea3c81a94ffe88145d663d25d5040d4637a47, 91bd7393fc4413b6629ad851f90c2c7f6c2c0991
Change-source: pulse-maintainer
2026-10-23 22:39:08 +01:00
pulse-triage[bot] 5db3120f36 Account for accepted new archives beside retained partial PBS history
The namespace lifecycle test advances the root archive timestamp. A partial instance-wide scan must retain the two historical points and accept that new root point, not replace all root history. Assert every historical point remains byte-identical and exactly one independently accepted new point is present.

Change-source: pulse-maintainer
2026-10-23 22:30:36 +01:00
rcourtman a4bfca4d32 Serialize Patrol action projection and finalize hung runs
Three gaps remained in how Pulse projects a Patrol investigation's
action and closes its runs.

Projections of one investigation could interleave: the transition
callback, an investigation read and a run's close each read the action
audit and applied it, so one that read an older state could land after
one that read a newer state, and an approval could land between a
run's reconciliation and its finalization. All of them now read and
apply under the investigation's lock, and a close finalizes before it
releases it.

The projection replaced the whole session through Update, so it could
undo run progress the orchestrator saved in between. It now writes
only the changed action and outcome when the store offers SetAction
(aicontracts.InvestigationActionStore), and the reference it writes
carries the broker's canonical result and verification status and the
model's causal attribution, which the audit does not hold.

A run that hung past its limit without closing was recovered by the
stuck-run sweep but never finalized. The sweep now finalizes it with a
copy of its session marked failed, leaving the orchestrator's session
as it is. A later real close is finalized instead, and a refresh no
longer reopens a record that finalization closed.
2026-10-23 22:26:09 +01:00
pulse-triage[bot] 7b39194ae2 Refresh PBS inventory when its newest backup is removed (#3150)
A capped snapshot cache can keep the same retained count after pruning
its newest archive. Require the group's newest timestamp to match
exactly before reusing it, so the deleted recent archive cannot keep
stale backups looking protected. Exercise VM and container polling
through successful, unavailable and denied refreshes, unchanged-cache
reuse, recovery and confirmed removal.

---
Fleet candidate `20261023T202016Z-core-runtime` from the `core-runtime`
lane. Reviewed tip `1d1bf942e63f8cdd8c9fe050822ef96cff75a516` on `main`.

[How Pulse handles
triage](https://github.com/rcourtman/Pulse/blob/main/docs/AI_TRANSPARENCY.md)
2026-10-23 21:17:29 +00:00
pulse-triage[bot] 11d1620807 Initialize the connection outcome map in namespace poll regressions
The fixture drives the real connection poll and therefore needs its ordinary poll-status map, in addition to backup scheduling and recovery state. Keep the new lifecycle proof independent of fixture setup panics.

Change-source: pulse-maintainer
2026-10-23 22:02:03 +01:00
pulse-triage[bot] 7152d2b7ff Keep failed PBS namespace discovery out of complete backup evidence
Carry namespace discovery errors with the immutable datastore snapshot into background backup polling. Readable root history still updates, but unknown child namespaces cannot delete durable facts or claim complete protection. Cover real HTTP discovery through ingest, replacement state, repeated failures, scoped removal, recovery and legacy root-only support.

Change-source: pulse-maintainer
2026-10-23 21:57:43 +01:00