Commit Graph

8080 Commits

Author SHA1 Message Date
rcourtman 66dc5fd7df Polish Actions layout and review details 2026-07-13 17:00:36 +01:00
rcourtman c471d8d201 Re-home guest alerts of every kind when a guest moves nodes
Guest alerts are keyed by node-scoped resource IDs, and only metric
threshold alerts were re-homed after a live migration. Lifecycle alerts
(powered-state and future guest kinds) stayed keyed to the old node
forever because their resolvers only look up the new-node key, node
existence GC never fires while the source node is still online, and
nothing else consumes the move.

Generalize the migration helper to match lifecycle spec IDs (which
embed the node-scoped resource ID) as well as node-independent metric
spec IDs, and call it from the canonical lifecycle and stateful
evaluators, the powered-off resolver, and the disabled-threshold early
returns in both metric paths. Guest-wide sweep clears (metric clear on
stop, per-disk cleanup, tag suppression) now match the stable
instance+vmid identity across nodes too.

Reported by Johannes Strasser: a VM live-migrated between Proxmox
nodes stranded its pre-existing active alert with no way to clear it.
2026-07-13 16:54:33 +01:00
rcourtman d6387b8241 Redesign the Actions inbox 2026-07-13 16:38:16 +01:00
rcourtman 4c3e0756e3 Guard the allowed_signers format in the install verification docs 2026-07-13 16:27:04 +01:00
rcourtman 4c073d6b17 Add mock action lifecycle data 2026-07-13 16:24:06 +01:00
rcourtman 95001df0bb Publish the sshsig key as an allowed_signers line so the documented verify command works
ssh-keygen -Y verify -f expects the principal in the FIRST field; the
docs published the key in authorized_keys order, so the documented
verification failed against a valid signature (customer-reported against
v6.0.5). Verified the new command against the live v6.0.5 release
artifacts.
2026-07-13 16:17:25 +01:00
rcourtman c5bf3adebf Fail closed instead of auto-downgrading, and back up config to a writable path under the hardened update unit
Customer report against v6.0.5 (2026-07-13):
- resolve_target_release fell back to hardcoded v4.5.1 when the GitHub API
  and the /releases/latest redirect both failed, silently downgrading a
  v5.0.17 install two major versions. Now: fail with --version guidance,
  and refuse any auto-resolved target older than the installed version.
- backup_existing wrote /etc/pulse.backup.<ts>, which is read-only under
  pulse-update.service (ProtectSystem=strict, ReadWritePaths=/opt/pulse
  /etc/pulse /tmp), so every unattended update on systemd/LXC failed at
  the backup step. Now: fall back to $INSTALL_DIR/config-backups when the
  config parent is not writable.
2026-07-13 16:13:29 +01:00
rcourtman f9f90a1a27 Redesign Patrol open work queue 2026-07-13 16:12:37 +01:00
rcourtman 2e9520da81 Bind governed action intent across Patrol and web 2026-07-13 16:05:44 +01:00
rcourtman 9286422263 Bind mobile actions to reviewed plans 2026-07-13 15:27:39 +01:00
rcourtman a10f309c95 Add Proxmox lifecycle Patrol detectors 2026-07-13 14:34:27 +01:00
rcourtman 5b4365853d Correct stale Settings/Alerts nav labels in user docs
Rename doc references to settings/alerts items that were renamed in the
shipped v6 IA, verified against current settingsNavCatalog.ts and
i18n/messages.ts:
  Settings > Relay             -> Settings > Remote Access
  Settings > Plans             -> Settings > Plans & Billing
  Settings > Security > Webhooks -> Settings > Security > Audit Webhooks
  Alerts > Notification Destinations -> Alerts > Notifications
  Settings > Reports           -> Settings > Data & Reports

Derived from the docs-rot audit; pure label renames only. Flow/route
rewrites (retired standalone pages, TrueNAS/Nodes relocation, i18n
copies) held for a supervised pass.
2026-07-13 13:47:13 +01:00
rcourtman f095da2fdb Wire production Proxmox action verification 2026-07-13 11:04:09 +01:00
rcourtman 5cde383b98 Adopt a member agent's connection address on cluster re-registration
The v6.0.5 host-adoption fix (f85009913) only applies when a
re-registering agent matches a top-level instance host, so an agent on a
non-primary cluster member never benefited: its registration created a
standalone instance that ConsolidatePVEInstances folded back into the
cluster, and the fill-empty endpoint merge silently discarded the agent's
fresh address. The member row kept showing the corosync short-DNS host
rebuilt on every re-discovery (the "Install issues with V6" support
thread; a reinstall on v6.0.5 still showed the stale name).

Canonical auto-register now matches cluster member endpoints directly:
address identity against the agent's candidate list first, then an
unambiguous corosync node-name match. The Pulse-verified selected host is
adopted as the member's IPOverride, the durable field re-discovery
preserves and polling prefers, plus the fingerprint captured from that
address. An admin-managed override absent from the candidate list is
preserved, mirroring shouldPreserveExistingAutoRegisterHost. Credential
writes stay restricted to a same-token-identity secret refresh (reinstall
rotates the agent's token in place, so the stored secret is already
invalid) and full promotion onto a credential-less cluster; a member's
distinct per-node token never replaces working cluster credentials, and
no standalone instance is created for consolidation to discard.
2026-07-13 09:59:18 +01:00
rcourtman e1720ca219 Use unavailable sentinel when VM guest agent disk query fails
When fetchVMFSInfo fails (agent not running, timeout, permission
denied, no filesystems), the builder passed the cluster/resources
numbers through — and PVE always reports 0 used for QEMU — so the UI
rendered a confident "0% (0 B/<allocated>)" for every affected VM
instead of the unavailable dash plus diskStatusReason tooltip. The
agent-disabled path already used the -1 sentinel for exactly this;
apply it on the error path too. The stabilizer can still replace the
sentinel with the previous good sample when recent agent evidence
exists.

Reported by Massimo Simoni (support, 2026-07-13): screenshot showed
every VM at 0% disk with only containers reporting real usage.
2026-07-13 09:41:11 +01:00
rcourtman 06a6028b29 test(coverage): pulse PURE branch-cov (15 presentation/model modules) 2026-07-13 08:25:53 +01:00
rcourtman 533c3392bd test(coverage): pulse PURE branch-cov (16 presentation/model modules) 2026-07-13 08:24:29 +01:00
rcourtman 8ffb56946e chore: seal Pulse Intelligence release gate 2026-07-13 01:14:19 +01:00
rcourtman a63b3eae2b Require independent evidence for verified findings 2026-07-12 23:15:54 +01:00
rcourtman 7d772acff3 feat(assistant): mid-turn steering of the running response
A follow-up sent during an active run now offers itself to the running
agentic loop via POST /api/ai/sessions/{id}/steer. Accepted steers join
the loop at its next turn boundary (the abort-check site) as plain user
messages, are announced with a steer_applied stream event so the drawer
settles the pending row, and persist through the end-of-run save. A
steer carries prompt text only: no route, control-level, or autonomy
changes, no turn-budget extension, system sessions rejected, and the
per-session inbox is bounded (steer_backlog overflow). Delivery is not
guaranteed by acceptance: a run that ends first discards the inbox and
the row drains as an ordinary queued turn, so pre-steering queue
semantics remain the fallback. Steering rows lose edit/remove once
accepted.
2026-07-12 23:01:40 +01:00
rcourtman 8676e5d5b9 Keep RG09 cache fixture across restart 2026-07-12 22:31:29 +01:00
rcourtman 42fe61493c feat(assistant): show estimated session cost in the last-turn summary
Chat turns, compaction, and title calls now stamp session_id on their
cost.UsageEvent, and the done event carries session_cost_usd summed from
the operator ledger (cost.Store.SessionCostUSD). The drawer's last-turn
summary appends '$0.12 session' with a sub-cent floor. The figure is
omitted whenever any of the session's models has unknown pricing, and
free local models price known-at-zero, so no figure is ever partial.
2026-07-12 22:13:23 +01:00
rcourtman 329039354d Use Development SSD for RG09 Go cache 2026-07-12 22:13:04 +01:00
rcourtman 373b491484 Fix durable APT drift receipts 2026-07-12 21:53:52 +01:00
rcourtman abcf7ec6ba test(coverage): harvest 20 stranded branch-coverage tests from 13:30 wave
Harvests the 13:30 c-wave's completed-but-unlanded branch-coverage tests
(315 cases across 20 PURE modules): url, settingsFeatureGates, alertDestinations,
storageAlertState, commandPalette, guestDrawer, nodeDrawer, navigation,
actionAuditPresentation, workloadTypePresentation, upgradeNavigation, and others.
Verified against current HEAD: vitest 20/20 green, full type-check clean, eslint
clean, GLM adversarial review 20 KEEP / 0 REJECT (no source edits).
2026-07-12 21:40:32 +01:00
rcourtman 8a0c6fc9a8 feat(assistant): collapse long pastes into composer chips
Pasting a log or config into the composer flooded the textarea. Pastes
over 8 lines or 800 chars now collapse into a removable chip; the full
text rejoins the prompt after the typed question at send time. Clicking
the chip body expands it back into the textarea for inline editing, and
a chip-only send is allowed.
2026-07-12 21:27:08 +01:00
rcourtman 1ff4c29cf7 Add Debian and Ubuntu APT certification 2026-07-12 21:25:48 +01:00
rcourtman 120c80239d feat(assistant): edit-and-resend pencil on the latest user prompt
The undo flow already restores the removed prompt into the composer for
editing, but the only entry points were the header undo button and /undo.
Surface it where users look for it: a hover pencil on the latest
non-queued user prompt that undoes the turn and prefills the composer,
riding the same last-turn undo boundary as regenerate.
2026-07-12 21:15:38 +01:00
rcourtman 6d1dced727 docs(readme): surface the Pulse MCP bring-your-own-agent path
The MCP server and its Settings setup panel existed but the README never
mentioned them, so users who prefer their own agent (Claude Code,
OpenCode) had no pointer to the supported path.
2026-07-12 21:05:25 +01:00
rcourtman 44e29e8e98 Merge branch 'main' of https://github.com/rcourtman/Pulse 2026-07-12 21:02:34 +01:00
rcourtman 60ce8924bb feat(assistant): retry re-runs the turn in place and the last answer gains regenerate
Retrying a failed turn re-sent the prompt without removing the persisted
turn, so session history double-recorded the prompt. Session undo now
accepts an expected-prompt guard (a stale retry can never remove a
different turn); retry drops the replaced turn server-side before
re-sending. The latest settled assistant answer gains a hover-revealed
Regenerate button that reuses the same path.
2026-07-12 20:58:20 +01:00
rcourtman b8df758ede Preserve RG06 evidence trust boundaries 2026-07-12 20:49:18 +01:00
rcourtman 8b2eef10b4 Normalize bounded APT agent clock skew 2026-07-12 20:44:21 +01:00
rcourtman 9981e370bb Align RG06 operator lock reason 2026-07-12 20:39:14 +01:00
rcourtman 399745fde5 Plan RG06 stale barrier explicitly 2026-07-12 20:35:47 +01:00
rcourtman 03cb85d85a Measure RG06 stale plan at dispatch 2026-07-12 20:30:28 +01:00
rcourtman 43accc722e Project RG06 resource staleness fully 2026-07-12 20:26:19 +01:00
rcourtman 4334535291 Invalidate RG06 registry on staleness 2026-07-12 20:22:53 +01:00
rcourtman 912f131cd0 Measure RG06 barriers before teardown 2026-07-12 20:19:03 +01:00
rcourtman 1ad813c2e7 Align RG06 emergency barrier reason 2026-07-12 20:15:25 +01:00
rcourtman 322f4fc50d Bind RG06 agent and host identity 2026-07-12 20:12:00 +01:00
rcourtman d518e7d33d Use canonical default tenant in RG06 2026-07-12 20:08:10 +01:00
rcourtman fedc77ef62 Route RG06 through canonical registry 2026-07-12 20:04:40 +01:00
rcourtman 06dcd59121 Bind RG06 report projection identity 2026-07-12 19:59:35 +01:00
rcourtman e0e6842cb5 Fix RG06 ext4 pressure fixture 2026-07-12 19:56:47 +01:00
rcourtman 7fd7e2b160 fix(frontend): key drawer guest web-interface URLs by the canonical workload id
The infrastructure resource drawer saved guest metadata (web interface
URLs) under the unified resource hash or the discovery resource id, a
keyspace no workloads table reads: the tables and the workloads drawer
resolve metadata by the canonical id (instance:node:vmid for PVE guests,
resource id otherwise), which is also the key v5 upgrades carry over in
guest_metadata.json. A URL saved from that drawer was stranded where
only the same drawer could read it back.

Route the drawer's guest metadata id through
getCanonicalWorkloadIdForResource, and canonicalize the resource type
inside that helper so lxc/oci-container/qemu spellings build the same
node-scoped id the workloads surfaces use.

Refs #1556
2026-07-12 19:52:21 +01:00
rcourtman 4eb7715171 fix(frontend): key drawer guest web-interface URLs by the canonical workload id
The infrastructure resource drawer saved guest metadata (web interface
URLs) under the unified resource hash or the discovery resource id, a
keyspace no workloads table reads: the tables and the workloads drawer
resolve metadata by the canonical id (instance:node:vmid for PVE guests,
resource id otherwise), which is also the key v5 upgrades carry over in
guest_metadata.json. A URL saved from that drawer was stranded where
only the same drawer could read it back.

Route the drawer's guest metadata id through
getCanonicalWorkloadIdForResource, and canonicalize the resource type
inside that helper so lxc/oci-container/qemu spellings build the same
node-scoped id the workloads surfaces use.

Refs #1556
2026-07-12 19:50:09 +01:00
rcourtman 10b73e1964 fix(docker-agent): preserve shared network namespaces through container updates
One-click updates recreated containers from the raw inspected config, so
Docker rejected the create for any container using network_mode:
container:<id> (compose service:<x>) or host with "conflicting options:
hostname and the network mode", leaving the workload stopped under its
_pulse_backup_ name until the rollback rename restored it. Docker fills
Config.Hostname with the namespace owner's ID on such containers, so the
verbatim copy always tripped the daemon validation (verified against a
real Docker 29.5.2 daemon: verbatim create 409s, sanitized create
succeeds and the replacement starts in the shared namespace).

Strip the namespace-owned settings before ContainerCreate: hostname and
domainname for container:/host modes, plus exposed/published ports,
links, DNS options, and extra hosts for container: mode, all of which
the daemon derives from the owning namespace. Bridge and user-defined
networks are untouched.

Refs #1564
2026-07-12 19:48:18 +01:00
rcourtman 99aa2bd6eb Add Colima autonomy release proof 2026-07-12 19:47:56 +01:00
rcourtman 2d47c12e6d Normalize Pulse Intelligence release gates 2026-07-12 14:42:07 +01:00