Commit Graph

10276 Commits

Author SHA1 Message Date
rcourtman eecf45fafd Email the Patrol weekly summary as a report schedule kind
The customers most likely to lapse are the ones who stopped opening Pulse,
so the in-app "This week" card cannot reach them. Report schedules gain a
kind: the default resources kind is the existing PDF or CSV performance
report, and patrol_digest emails the same weekly "what Patrol did for you"
rollup the Patrol page shows, in plain language, for the whole workspace.
It reuses the existing scheduler, cadence, recipients, tenant email config,
and Pro advanced-reporting entitlement rather than adding a second
scheduler. Digest schedules are weekly and email-only; the server fixes
scope, format, and attachments, and a run with no AI service, no Patrol, or
no email destination records a last_error that names the missing piece
instead of sending an empty week. Nothing is written to disk.

The digest assembly moves from the HTTP handler into
AISettingsHandler.BuildPatrolDigest so the schedule runner and the
endpoint share one code path. docs/MSP.md documents the kind for
providers; the Settings > Reporting "Report type" selector follows in a
separate change once it has a Pro-licensed browser pass.
2026-09-02 11:15:22 +01:00
rcourtman b5f694b216 Merge pull request #1860 from rcourtman/claude/patrol-value-digest-card-r2
Show what Patrol did this week on the Patrol page
2026-09-02 10:52:02 +01:00
rcourtman 5476288a4e Merge pull request #1840 from rcourtman/claude/helm-publish-checkout-order
Fix the release preflight worker and Helm publish, and map v6.4.3 to release/v6.4
2026-09-02 10:10:19 +01:00
rcourtman 123e342c6c Show what Patrol did this week on the Patrol page
A paying customer had no place in Pulse that added up Patrol's work: runs
were per run, findings per finding, actions under Actions, spend on the AI
cost dashboard. The Activity tab now opens with a "This week" card that
reads GET /api/ai/patrol/digest and shows six things in plain language:
Patrol runs, new issues and how many are still open, issues resolved,
investigations, fixes run, and estimated spend. Tile copy is mode-aware, so
a watch-only install reads that nothing was investigated because Patrol is
watch only, and the card links to Actions only when Patrol-origin fixes are
waiting for approval.

The card never recomputes counts client-side and keeps forensic vocabulary
out; a failed load says the summary is unavailable rather than showing
zeros, and a truncated run history says since when the numbers hold. The
page header already states the Patrol mode sentence, so the card does not
repeat it. Second slice of the "Patrol weekly digest" named bet in the
pulse-pro demand ledger; browser proof in browser-verification.json covers
the Activity tab at 1280 and 375 pixels against an isolated mock-mode
backend.
2026-09-02 09:57:22 +01:00
pulse-triage[bot] facee87bb4 Merge pull request #1852 from rcourtman/maintainer/20260902T060320Z
Fix empty-slot alerts and make keyboard navigation accessible
2026-09-02 09:52:36 +01:00
rcourtman 40a371ac48 Merge pull request #1856 from rcourtman/claude/patrol-value-digest-r3
Add the Patrol weekly digest endpoint
2026-09-02 09:39:09 +01:00
rcourtman 22b9abf231 Fix the release preflight worker and Helm publish, and map v6.4.3 to release/v6.4
Three defects that the v6.4.3-rc.1 cut exposed, each with its contract note
and proof:

1. scripts/release-preflight-worker.sh published the smoke stack on host port
   7655 and probed it directly. pulse-dev also hosts the maintainer's dogfood
   Pulse instance on 127.0.0.1:7655 and a second instance on 17655, so the
   release smoke failed to start after every other stage had passed on the
   qualified head. The worker now honours PULSE_RELEASE_PREFLIGHT_E2E_PORT,
   otherwise takes the first candidate host port pair it verifies free and
   fails fast when none is, probes health and update status on that port, and
   hands Playwright the same base URL.

2. .github/workflows/publish-helm-chart.yml resolved the chart version before
   checking out the repository, but de41ea1883 made that step call
   scripts/write_github_output.py, so every chart publish since failed with
   "can't open file". Run 33580123246 hit it after all other staging passed;
   the checkout now precedes the resolver.

3. docs/release-control/control_plane.json maps the 6.4.3 version prefix to
   release/v6.4. Run 33579042375, dispatched from main, failed inside the
   compiler dispatch because main advanced one minute after the pipeline
   pinned its source SHA; with pull requests landing every few minutes a
   candidate cannot hold its SHA between prepare and compile. release/v6.4
   was created from main at the exact-SHA-qualified commit 56e51e622e and
   carries these same fixes; v6.4.3-rc.1 published from it.

The deployment-installability contract records the worker port and toolchain
rules and the Helm checkout order; release_preflight_test.py and
build_release_assets_test.go pin them.
2026-09-02 09:32:40 +01:00
pulse-triage[bot] 6ab16f9f35 Merge current Pulse upstream for publication
Preserve the reviewed command-palette browser receipt as the current singleton receipt while retaining the upstream Patrol setup proof in its unchanged parent history.

Change-source: pulse-maintainer
2026-09-02 08:56:06 +01:00
rcourtman 8ea94d792b Add the Patrol weekly digest endpoint
Nothing in Pulse showed a paying customer what Patrol had done for them:
about 164 runs a month per install, findings raised, investigations and
fixes, and none of it summarised. GET /api/ai/patrol/digest rolls the last
N days (default 7, max 30) up from records Pulse already keeps: run
history, the findings store, Patrol-origin action audits, and the usage
cost store. It adds no telemetry and persists nothing. The payload
reports when the bounded run history no longer covers the window and when
model pricing is unknown, rather than quietly under-counting.

This is the first slice of the "Patrol weekly digest" named bet in the
pulse-pro demand ledger; the in-app "This week" card follows once its
browser pass is recorded. docs/PATROL_WEEKLY_DIGEST.md holds the design
note and the honest limits of each line.

status.json registers the patrol-value-visibility coverage gap, the
candidate lane, and its work claim. It also drops the second, identical
copy of the ai-provider-guided-setup coverage gap that landed with #1853;
the duplicate id fails the status audit on main for every pull request.
2026-09-02 08:53:50 +01:00
rcourtman b1d1a03643 Merge pull request #1853 from rcourtman/claude/ai-provider-guided-setup-v5
Guide the Patrol model choice with cost preview and budget pause
2026-09-02 08:49:12 +01:00
pulse-triage[bot] 6c5af82b52 Merge reviewed command palette accessibility fix
Change-source: pulse-maintainer
2026-09-02 08:32:12 +01:00
pulse-triage[bot] 23a59f17a1 Fix command palette active option accessibility
Keep DOM focus on the command search input while exposing the visually
selected result through combobox active-descendant semantics. Remove results
from the Tab sequence, keep keyboard selection visible in long lists, and
cover populated and empty states.

Change-source: pulse-maintainer
2026-09-02 08:16:13 +01:00
rcourtman ac328e1eee Guide the Patrol model choice with cost preview and budget pause
Forty-two percent of paying installs never get Patrol running, and the
support and issue evidence says the wall is model choice and cost: a
prospect asked which models Pulse recommends before trying, a Pro customer
picked Flash-Lite to save money and Patrol then could not file verdicts,
and a mispriced Opus row tripped the 20 USD budget so the circuit breaker
disabled Patrol with nothing but a log line (#1789).

Answer both questions at the point of choice. The Patrol and shared
default model pickers pin guided models in a "Suggested for Patrol"
section with recommended / suggested / caution markers and a one-line
reason: the Ollama preflight blessing is the only recommended entry,
Gemini Flash-Lite the only caution, and cloud starting points are labelled
price-driven and unqualified until this install's own readiness pass
upgrades them to "Verified on this install". A server-computed cost
preview sits under the model choice: monthly estimate from Pulse's price
table, the schedule, and the install's own median full run once three
priced runs exist (a measured 104,528-in / 4,491-out run otherwise), with
the assumption stated and a token explained once, plus 30-day spend
against budget. Each schedule preset is priced in the Schedule select.

When a per-token model is picked while the schedule is still the 6-hour
default, the cost model proposes the slowest preset that keeps scheduled
runs under half the budget (20 USD reference when none is set) and says
what the slower check costs in detection delay; a schedule the install
already chose is never changed.

A used-up budget is now a spending decision, not a provider fault: the
budget refusal is a typed sentinel with the figures, classified as the
budget_exhausted cause, excluded from circuit-breaker accounting, and
promoted into the Patrol block state, so the Patrol page shows "Patrol
paused" with the spend and limit and a "Raise the cost budget" action
instead of "Check Patrol model".
2026-09-02 07:55:42 +01:00
pulse-triage[bot] 1bdc3b0e37 Merge captured Pulse upstream
Capture the origin/main state that predated this coordination batch while preserving the locally reviewed integration history. Keep the first-parent receipt at the merge tip; each exact non-merge UI commit retains its own parent-bound browser proof.

Change-source: pulse-maintainer
2026-09-02 07:46:39 +01:00
rcourtman fd36b3124c Merge pull request #1848 from rcourtman/claude/patrol-findings-hygiene-v3
Fold alert-mirroring findings, collapse flapping, surface lasting decisions
2026-09-02 07:40:04 +01:00
pulse-triage[bot] 23b3893ae8 Fix discovery scan scope keyboard navigation
Keep only the selected scan scope in the page Tab order and make all arrow keys move focus and selection with wrapping. Retain radio focus when Custom mode normally moves focus into the subnet field, and cover the behavior in component and desktop/mobile browser tests.

Change-source: pulse-maintainer

Contract-Neutral: Accessibility bug fix restores the documented radio-group keyboard behavior without changing product contracts.
2026-09-02 07:11:14 +01:00
rcourtman a10c175f31 Fold alert-mirroring findings, collapse flapping, surface lasting decisions
Two users (discussions #1623, #1699) could not find Patrol's durable
outcomes: the Needs attention detail offered only Acknowledge and
Suppress, and Remember as expected, Dismiss: Not an issue, Dismiss:
Later, and Create rule sat two levels below it. The same screenshot
showed an alert with eleven open/resolved transitions in a day listed as
eleven timeline rows, and Patrol findings restating an active alert
appearing beside it. Telemetry shows most findings are seen and ignored.

Backend: findings_alert_mirror.go is the single matcher for a finding
that restates an active alert (explicit alert identifier, or same
canonical resource plus the same condition class). Patrol stamps
mirrors_alert_id/mirrors_alert_type after every real and demo cycle and
clears them when the alert resolves; the finding is kept, not dropped.
The storm throttler now also owns flap detection: four or more
open/resolved transitions in 24h mark a finding flapping, and the store
maintains one collapsed lifecycle row with the count instead of a row per
transition, hydrating the window from persisted lifecycle after restart.
The attention projection applies the same threshold to alert timelines
and exposes a flapping summary while keeping the full timeline.

Frontend: the attention detail gains a Lasting decisions section that
offers the four Patrol decisions on the finding mirroring the alert, each
with one line saying what it does and how long it lasts, an inline
confirmation, and a remembered-decision state with Reopen. An alert with
no mirrored finding says Patrol has nothing to remember and points at
alert thresholds. Flapping items carry one label and a summarised
timeline with every transition under an expansion. FindingsPanel demotes
active alert-mirroring findings into a collapsed group. The Patrol
surface loads dismissed findings so remembered decisions are visible.
2026-09-02 07:02:51 +01:00
pulse-triage[bot] 77bf2a27c6 Merge reviewed shared subtab keyboard fix
Integrate the exact browser-verified accessibility candidate after the previously reviewed storage correction; the histories and changed paths are independent.

Change-source: pulse-maintainer
2026-09-02 06:50:00 +01:00
pulse-triage[bot] 29ceb25a1e Fix shared subtab keyboard navigation
Only the selected subtab participates in the normal Tab order, so without arrow-key handling keyboard users cannot reach the other tabs. Centralizing manual focus movement in the shared control restores expected tab-list interaction across every caller while leaving activation explicit.

Contract-Neutral: Accessibility bug fix restores expected keyboard behavior without changing the component API or product contract.
Change-source: pulse-maintainer
2026-09-02 06:34:01 +01:00
pulse-triage[bot] 803d217e18 Apply Unraid empty-slot semantics in storage health
Use the reviewed placeholder-filesystem rule when storage health assesses structured Unraid state, while retaining explicit DISK_NP_MISSING members as critical evidence.

Change-source: pulse-maintainer

Contract-Neutral: Unraid fsType=auto placeholder normalization is applied consistently at storage-health assessment without changing contracts
2026-09-02 06:29:41 +01:00
pulse-triage[bot] 00ed84f472 Merge reviewed Unraid empty-slot fix
Integrate the reviewed false-alert regression fix without re-parenting its verified candidate commit.

Change-source: pulse-maintainer
2026-09-02 06:27:42 +01:00
pulse-triage[bot] d7a4dcf8e7 Merge pull request #1842 from rcourtman/maintainer/20260902T030649Z
Make release checks reliable and dialogs accessible
2026-09-02 06:04:27 +01:00
pulse-triage[bot] ace72f8442 Ignore Unraid auto filesystem on empty slots
Treat Unraid's fsType=auto value as a placeholder rather than disk assignment evidence at both agent collection and server ingestion boundaries. Preserve real assigned and explicit missing members.

Change-source: pulse-maintainer
Contract-Neutral: Unraid fsType=auto placeholder normalization fixes false missing-slot alerts without changing wire or subsystem contracts
(cherry picked from commit fd843da7dfc51bba29bb5ddcbcdb4480d0e67079)
(cherry picked from commit 7a6456969d)
2026-09-02 05:40:46 +01:00
pulse-triage[bot] d680c339d9 Require accessible names for shared dialogs
Make the shared Dialog prop contract require exactly one accessible-name strategy so new modal call sites cannot silently omit screen-reader context. Record the canonical primitive contract, preserve existing runtime behavior, and cover both valid strategies plus invalid unnamed and ambiguous props.

Change-source: pulse-maintainer
2026-09-02 04:40:19 +01:00
pulse-triage[bot] 9fba43ffed Merge reviewed shell and settings accessibility fixes
Integrate the exact browser-verified candidate after the captured runtime changes; the histories touch disjoint paths.

Change-source: pulse-maintainer
2026-09-02 04:04:36 +01:00
pulse-triage[bot] 9bda0b30db Fix shell and settings accessibility regressions
Keyboard and screen-reader users could not reliably bypass the shell or identify two General Settings toggles, while the RC badge failed the maintained contrast contract. Restoring explicit focus transfer, accessible control names, and compliant badge contrast keeps core navigation and settings usable without changing product scope.

Change-source: pulse-maintainer
Contract-Neutral: Accessibility regression fix only; no public contract or subsystem boundary changes.
2026-09-02 04:00:04 +01:00
pulse-triage[bot] 45532a0bd0 Merge core runtime retry test stabilization
Integrate the exact reviewed candidate after the captured upstream mainline.

Change-source: pulse-maintainer
2026-09-02 03:53:54 +01:00
pulse-triage[bot] 9edc8bcea3 Merge captured Pulse upstream
Incorporate the batch-start upstream rootful qualification fixes without re-parenting canonical maintainer commits.

Change-source: pulse-maintainer
2026-09-02 03:48:55 +01:00
pulse-triage[bot] bdf84e8adc Stabilize agent module retry lifecycle tests
The Docker and Kubernetes retry tests used a whole-runtime timeout as both
their synchronization point and shutdown trigger. Under suite load, startup
could consume that deadline before the second module initialization, producing
a false failure unrelated to retry behavior.

Wait for the successful retry explicitly, then cancel and verify clean
shutdown so the tests measure the lifecycle contract deterministically.

Change-source: pulse-maintainer
2026-09-02 03:45:22 +01:00
rcourtman e0bd9206fa Merge pull request #1839 from rcourtman/pulse/fix-rootful-systemd-readiness
Fix rootful qualification readiness
2026-09-02 03:39:50 +01:00
pulse-triage[bot] 44e274e5b3 Merge captured Pulse upstream
Change-source: pulse-maintainer
2026-09-02 02:52:13 +01:00
rcourtman e93ee38084 Tighten rootful runtime cleanup proof
Rootful evidence must reject non-running systemd states and prove no unrelated container runtime survives. Require exact manager state, mask distro containerd, recheck readiness after scenarios, and remove the complete Podman socket boundary.
2026-09-02 02:52:13 +01:00
rcourtman e836dfda13 Fix rootful qualification readiness
The disposable host reached multi-user.target in a degraded state because distro Podman housekeeping used overlay storage inside the outer container. Mask unused units, force VFS for explicit daemons, and fail closed on systemd unit failures so retained rootful evidence remains trustworthy.
2026-09-02 02:43:30 +01:00
rcourtman 6d0b33fee6 Merge pull request #1834 from rcourtman/pulse/fix-rootful-attestation-import
Fix isolated rootful attester loading
2026-09-02 02:31:37 +01:00
pulse-triage[bot] 5017c599f0 Bind rootful attestation to recovery test
The pre-batch upstream rootful source-closure manifest was created before the local Unix recovery test. Include that compiled installer input so merged qualification evidence remains bound to the complete harness.

Change-source: pulse-maintainer
(cherry picked from commit f5ad4e343e)
2026-09-02 02:28:15 +01:00
pulse-triage[bot] f503b13442 Bind release dispatches to the admitted commit
A workflow dispatch by branch can resolve after that branch moves, allowing an unreviewed tip to enter the release pipeline. Require every publishing dispatch to name its expected source SHA and make the workflow reject a different source or workflow commit before checkout.

Change-source: pulse-maintainer
(cherry picked from commit a461fc9c0a)
2026-09-02 02:28:14 +01:00
pulse-triage[bot] 63267f57e1 Restore Windows installer test compilation
The Unix agent-ID recovery test imported syscall.Mkfifo from a generic test file. Go must compile that file before its runtime skip can run, so Windows CI could no longer build the installer test package. Keep the security regression on supported Unix targets while restoring the Windows delivery signal.

Change-source: pulse-maintainer
(cherry picked from commit c0ca94ee9f)
(cherry picked from commit d2cc6b4884)
2026-09-02 02:28:14 +01:00
rcourtman 419a368323 Merge pull request #1828 from rcourtman/topic/transparency-delivery
Describe how changes land and ship, and hold human commits to the standard
2026-09-02 02:22:14 +01:00
rcourtman 56e51e622e Merge pull request #1825 from rcourtman/topic/release-train
Adopt the release train: promote the soaked candidate, not the branch tip
2026-09-02 02:08:32 +01:00
rcourtman 0e6d99af65 Fix isolated rootful attester loading 2026-09-02 01:53:48 +01:00
rcourtman 5f0a487404 Merge pull request #1823 from rcourtman/pulse/secure-runtime-rootful-qualification
Add rootful runtime qualification packet
2026-09-02 01:48:16 +01:00
rcourtman 9e33945b55 Merge pull request #1833 from rcourtman/claude/assistant-bias-to-act-v2
Submit advertised lifecycle actions through pulse_control, not prose
2026-09-02 01:36:05 +01:00
rcourtman 6d0ce0b45e Describe how changes land and ship, and hold human commits to the standard
The transparency page told evaluators that routine changes may merge
without line-by-line review, but not what governs a landing or a
release, so the commit stream was the only evidence and it read badly
(discussion #1539, the release-reliability ledger entry). The page now
states the delivery contract in public terms: every writer lands through
a pull request that auto-merges on green required checks with no bypass,
the maintainer's pull requests carry outcome, reason, and validation, and
releases run on a train with a soaked, exact-content candidate, with the
rules in RELEASE_PROMOTION_POLICY.md. The shipped docs mirror is updated
with it.

The maintainer's candidate preflight already refuses a commit without a
body or with a subject over 72 characters. The new husky commit-msg hook
holds human and interactive-agent commits in this checkout to the same
standard, exempting merge, fixup, squash, and revert messages.
2026-09-02 01:17:38 +01:00
rcourtman df7ad9be43 Adopt the release train: promote the soaked candidate, not the branch tip
Stable promotions built whatever the dispatch branch was at that second.
The resolver checked that HEAD descends from the promoted release
candidate but never that its content matches, so v6.4.0 shipped 64
changed files, including product code, that v6.4.0-rc.12 had not
soaked. Every v6 version was mapped to main, which now moves every few
minutes under the autonomous maintainer, so each fix to a candidate
brought everything landed since and stable was never an exact soaked
commit. Five of six stable minor releases shipped under version-bound
owner exceptions that waived the soak.

From v6.5.0 the release train applies (RELEASE_PROMOTION_POLICY.md,
"Release Train"): a two-week train sized to measured velocity, a
release/v6.N branch per train declared in the control plane so the
workflow refuses a dispatch from anywhere else, a stable promotion that
may differ from its candidate only in release metadata unless
hotfix_exception names active customer harm, and a seven day soak for
minor releases. The 6.4.x line stays on main so the v6.4.3-rc.1
candidate already prepared there is unaffected. The gap is registered
as coverage gap release-train-exact-candidate-promotion.
2026-09-02 01:17:38 +01:00
rcourtman 7996848b64 Submit advertised lifecycle actions through pulse_control, not prose
A paying operator asked the Assistant to reboot five Proxmox VMs matching
a name pattern (GitHub #1782, support mail 2026-08-26 and 2026-08-29). The
model resolved the VMs and then ended with a report that invented a
prerequisite: a QEMU guest agent on 6.3.2, a "discovery binding" on stable
6.4.0. It never planned the action. Three defects made the governed path
fail whenever the model did try it, and nothing refused the prose ending
when it did not:

- pulse_control handed the session-scoped id (vm:<node>:<vmid>) to the
  action lifecycle, whose registry keys on canonical unified ids, so a
  Proxmox guest plan could never resolve.
- pulse_control gated the action on the legacy per-executor action list,
  which never carried the canonical "reboot" capability Proxmox guests
  advertise, so "reboot" was refused as not permitted before planning.
- A reference absent from the session context was refused with "resource
  discovery is required" even when the unified inventory resolved it.

pulse_control now binds its target to the canonical unified resource
(session alias first, then a unique inventory match, refusing ambiguity
with candidate ids and naming the pulse_query recovery on a miss), passes
the canonical id to the planner, and answers "not available" only from
the resource's current advertised capabilities. The FSM ordering block
and the shared operating instructions state that a recoverable block is
not a limitation to report, and the instructions require the governed
action tool for advertised capabilities. The agentic loop adds a bounded
advertised-action gate: when the operator asked for a lifecycle action,
pulse_control was offered but never submitted, and a session-resolved
resource advertises the action, a tool-free final answer is refused once
with the exact per-target calls.

Covered by tools and loop unit tests (the #1782 transcript against a
scripted provider fails on the previous code with the two exact errors
above), a prompt-contract test, and the live eval scenario
ProxmoxBulkLifecycleActionScenario.
2026-09-02 00:19:08 +01:00
rcourtman 14a7d0bdaf Merge action-runner replay and Windows portability fixes 2026-09-02 00:16:26 +01:00
rcourtman d3ea482827 Merge branch 'main' into pulse/secure-runtime-rootful-qualification 2026-09-02 00:14:50 +01:00
rcourtman 2de076a9bc Merge main into claude/hostagent-replay-reconnect-wait
Pick up the browserslist advisory fix (#1827) so the required Frontend
dependency audit can pass on this pull request.
2026-09-02 00:14:41 +01:00
rcourtman 62003caaea Merge pull request #1827 from rcourtman/pulse/fix-browserslist-audit
Raise browserslist above security advisory floor
2026-09-02 00:13:37 +01:00
rcourtman 73b3d8eff3 Complete rootful artifact source closure 2026-09-02 00:07:39 +01:00