Commit Graph

10340 Commits

Author SHA1 Message Date
pulse-triage[bot] 724a8a4960 Keep same-name Proxmox sites separate in realtime
The browser compatibility coalescer could undo the server's provider-scoped split after websocket reconciliation. Honour machine and provider identity before merging same-hostname rows.
2026-09-04 01:32:20 +01:00
pulse-triage[bot] 53ea86983c Merge commit '9d5cdfa088797d72ced422c81e1d5765d2200a75'
Change-source: pulse-maintainer
2026-09-04 00:38:03 +01:00
pulse-triage[bot] 52e0b0d72e Merge frozen Pulse upstream for coordination batch
Change-source: pulse-maintainer
2026-09-04 00:38:03 +01:00
rcourtman 7af561cf37 Merge pull request #1877 from rcourtman/claude/notification-failure-classification
Classify notification failures from senders, not error prose
2026-09-04 00:33:09 +01:00
pulse-triage[bot] a7406876c0 Merge commit 'da9800ff2f6d1064556c25394e976c9cfa40dc64'
Change-source: pulse-maintainer
2026-09-04 00:30:59 +01:00
pulse-triage[bot] a8e0639db6 Merge commit '6f3c43612178c4f647b3a6443ee120a2ee567f4f'
Change-source: pulse-maintainer
2026-09-04 00:30:59 +01:00
pulse-triage[bot] 9d5cdfa088 Collect disks from the agent mount namespace
gopsutil defaults to PID 1's mount table, which differs from the namespace created for Pulse's hardened systemd service. Enumerate the collector's own mount table so explicitly included tmpfs and bind mounts are discoverable, while preserving deliberate host-proc overrides for container deployments.

Change-source: pulse-maintainer
2026-09-04 00:28:55 +01:00
pulse-triage[bot] da9800ff2f Keep infrastructure dialog dismissal in place
The stable-row focus fallback ran after the shared dialog cleanup and could scroll a lower Manage trigger into view. Preserve the existing focus return without changing the operator's viewport, and cover the real desktop and narrow browser path.

Contract-Neutral: frontend focus-restoration bugfix; no API or persisted-data change.

Change-source: pulse-maintainer
2026-09-04 00:26:02 +01:00
pulse-triage[bot] 6f3c436121 Stop retrying unchanged credential blocks
A committed release with an unchanged operator-owned containment failure cannot converge through unattended retries. Classify that evidence without weakening the block, and rearm only when the relevant private inputs or public controls change.

Change-source: pulse-maintainer
2026-09-04 00:14:40 +01:00
pulse-triage[bot] 0329437fc6 Merge commit '384dc53608eebca505b77119fdfc1af891edd6be'
Change-source: pulse-maintainer
2026-09-04 00:04:17 +01:00
Richard Courtman 47b8664f0b Classify notification failures from senders, not error prose
Terminal notification failures are 36% of resolved delivery outcomes
fleet-wide (126,337 dead-lettered against 224,692 delivered in the week
to 2026-09-03, over 6,668 clean installs), and the category breakdown
could not say why: unknown was the modal bucket at 31,218.

The class was being derived by substring-matching the Go error message.
That fails in two ways. Any failure whose text carries none of the ~50
recognised tokens falls through to unknown, which is most of what SMTP
produces: net/smtp reports the server's verdict as a reply code, and only
535 was ever matched, so a 550 relay refusal and a 451 temporary failure
both recorded as unknown. Worse, the text being matched includes the
destination's own response body, so a third party can choose the reason
code Pulse records and shows the operator - a 500 whose body contains
"rate limit" was recorded as rate_limited rather than server_error.

Senders now declare the class where they already know it, and the
classifier reads Go's own error types before it reads any prose:
*textproto.Error for SMTP reply codes, x509 and tls for certificate
failures, net.DNSError and timeouts for connectivity. HTTP status codes
set the class at the five sites that build a status error, so the
response body is preserved for the operator's audit row but can no longer
influence the classification. Prose matching remains only as the last
resort for paths that declare nothing.

SMTP 5xx is deliberately not mapped the way HTTP 5xx is: a 550 is the
destination refusing the message, not the destination breaking, so only
the transient 4xx replies count as server_error.

Registers the wider finding as a coverage gap. The 36% is concentration,
not breadth - 72 installs that delivered nothing at all in seven days
account for half of all terminal failures, and Pulse neither backs off
nor tells those operators the destination has never once succeeded.
2026-09-03 23:42:50 +01:00
pulse-triage[bot] 384dc53608 Keep repeated Proxmox cluster labels provider-scoped
A cluster name is an operator-selected display label, not global machine identity. Two independently configured estates using the same label could share one linked agent and then collapse into one presentation row after delayed cluster detection. Require node identity, exact endpoint, or host corroboration across provider instances instead.

Refs #1753

Change-source: pulse-maintainer
2026-09-03 23:41:31 +01:00
pulse-triage[bot] fa006785c5 Merge commit '59e9f5ea2bfafa13c54bc374ed16cb997748c806' Change-source: pulse-maintainer
Change-source: pulse-maintainer
2026-09-03 22:40:19 +01:00
pulse-triage[bot] 59e9f5ea2b Keep inline drawers from moving the viewport
Shared subtabs used scrollIntoView for horizontal visibility, allowing remounted or programmatically changed drawers to scroll the whole application shell. Reuse the horizontal rail controller instead, and restore disclosure focus without scrolling when a detail row closes.

Record the shared primitive contract and its registry-approved guardrails, with desktop and narrow Backups browser coverage for visible Overview and Manage states, off-screen tab changes, and non-scrolling focus return.

Change-source: pulse-maintainer
2026-09-03 22:34:11 +01:00
pulse-triage[bot] 12180d11a2 Merge commit '9d1deb2a0a53454fbf771894e51ac910ef24e68f'
Change-source: pulse-maintainer
2026-09-03 22:29:14 +01:00
pulse-triage[bot] 9d1deb2a0a Allow TrueNAS CORE 12 legacy REST negotiation
CORE 12 reports versions such as TrueNAS-12.0-U5, which the legacy transport gate did not recognise. Admit that bounded version family and retain redirect negotiation coverage for both supported CORE generations.

Change-source: pulse-maintainer
2026-09-03 21:57:07 +01:00
pulse-triage[bot] 93c765a7ec Keep infrastructure edits mounted across polls
Connection-ledger refreshes replaced the selected connection snapshot and re-ran the credential slot renderer, destroying unsaved form state despite the node-level dirty guard. Keep the editor owner bound to the selected source while allowing the surrounding live row and dialog description to refresh.

Regression coverage advances both lastSeen and the endpoint, proving the form mounts once and retains input while live context updates.

Contract-Neutral: frontend state-lifetime bugfix; no API or persisted-data change.
2026-09-03 21:15:14 +01:00
rcourtman 54d58a7406 Merge pull request #1874 from rcourtman/claude/telemetry-consent-2
Make telemetry consent a setup choice and retire the payload banner
2026-09-02 19:46:12 +01:00
Richard Courtman a70c96c8a3 Make telemetry consent a setup choice and retire the payload banner
The setup wizard's telemetry card only ever offered a way out: it led
with "enabled by default", gave no reason the data exists, and told the
reader to set PULSE_TELEMETRY=false before starting a process that had
already sent its first ping two minutes after boot. The payload-update
banner paired "we now collect more" with a one-click Disable button, was
keyed to schema v2 from July and never re-triggered across fifteen later
bumps, and its text was rewritten in August so anyone who had dismissed
it never saw the new wording. Nothing on either surface said what the
data is for or what it is never used for. No GitHub issue or discussion
has ever complained about the default-on posture, so the defensive
framing was answering a question nobody asked while quietly nudging
people to opt out.

Setup now leads with what the daily summary is for (development effort
follows real use; the features and platforms the operator relies on get
priority), names concrete exclusions (hostnames, credentials, IP
addresses), states what it is never used for (not sold or shared, not
used for advertising, not linked to a Pulse account or license), and
puts a real Usage statistics toggle on the admin-account step. The
toggle defaults to on and, when switched off, is applied through the
canonical system-settings endpoint once the admin token exists, so there
is no setup-only side channel and the account is created either way.
Neither setup screen tells the reader how to turn it off; the switch is
the control. The env-var instruction moves to PRIVACY.md where a reader
can still act on it, alongside a note that the first ping fires about
two minutes after start.

The payload-update banner is retired along with its telemetryAction deep
link that changed the preference on arrival. Payload changes are now
disclosed in a dated changelog in PRIVACY.md (back-filled from schema v2
to v17 from the telemetry package's own version notes) and in release
notes; an in-app notice is reserved for a change in kind. PRIVACY.md
gains a "What it is not used for" section whose statements are facts
about the license-server path, which never joins telemetry rows to
license or customer records; the contract treats any change to that path
as a change in kind. Settings leads with what the data is for and makes
Preview payload the primary action, because the exact runtime payload is
the disclosure an operator can verify. The security-privacy,
deployment-installability, and frontend-primitives contracts record the
new rules. Telemetry and i18n proof tests pin the setup choice, the
purpose-first and never-sold wording in every locale, and the changelog
row for the current schema so a future bump cannot land undisclosed.

Demand ledger: repos/pulse-pro FEATURE_REQUESTS.md "Telemetry consent as
a real setup choice" (named bet, pulse-pro PR #40). Supersedes the
three-commit branch behind Pulse PR #1873, rebuilt on current main.
2026-09-02 19:04:54 +01:00
rcourtman ef460aa654 Merge pull request #1871 from rcourtman/claude/helm-pages-immutable-retry
Make Helm Pages convergence idempotent on immutable chart releases
2026-09-02 19:02:52 +01:00
rcourtman 38ef8224e7 Merge pull request #1870 from rcourtman/claude/release-line-qualification
Qualify release candidates against the branch that owns their version
2026-09-02 18:30:16 +01:00
Richard Courtman 6385122400 Keep the chart release existence check on gh release view
The release promotion policy test pins every chart release command to
gh release with an explicit --repo, so the existence and prerelease
reads stay on gh release view; only the asset digest, which the CLI does
not expose, comes from the REST payload.

Contract-Neutral: Helm Pages release existence check keeps the gh release view shape the policy test pins; no product or contract change
2026-09-02 17:58:44 +01:00
Richard Courtman b6e78c18ae Make Helm Pages convergence idempotent on immutable chart releases
A convergence retry re-uploaded the chart with --clobber, which deletes
the existing asset first; GitHub refuses that on an immutable release, so
every retry for v6.4.3-rc.1 failed the Helm Pages surface after the chart
had already been published correctly. Recognise the exact chart already
held by the release through its asset digest, upload only when the asset
is missing, and refuse a different chart rather than replace it.

The same step also marked every chart release a prerelease, including the
stable helm-chart-6.4.0 and 6.4.1, which the release steward reports as a
release-state incident. The flag now follows the chart version.

Contract-Neutral: Helm Pages convergence idempotent on immutable chart releases; no product or contract change
2026-09-02 17:55:04 +01:00
Richard Courtman 79d87a4a2c Qualify release candidates against the branch that owns their version
The secure-runtime qualification required the candidate commit to be an
ancestor of origin/main. Under the release train a patch candidate is cut
from release/vX.Y and is not on main at all, so v6.4.3-rc.1's
qualification failed at "Verify detached release source" before running
any check, and the steward correctly refused to treat the candidate as
stable-capable. Resolve the branch the control plane maps the version to,
fetch that, check ancestry against it, and hand the same ref to the
attestation so the receipt records the line it was verified against.

Refs #1753

Contract-Neutral: release qualification checks ancestry against the mapped release branch; no product or contract change
2026-09-02 17:52:33 +01:00
pulse-triage[bot] ac3dc2e953 Merge pull request #1862 from rcourtman/maintainer/20260902T090949Z
Improve accessibility, TrueNAS readiness, and release reliability
2026-09-02 17:01:21 +01:00
pulse-triage[bot] 111b1bd251 Merge current Pulse upstream for publication
Incorporate the metrics startup hook capture from PR #1868 while retaining
the reviewed causal cleanup and barrier-ordering coverage. This advances the
open publication proposal without rewriting any accepted commit.

Change-source: pulse-maintainer
Contract-Neutral: Integration reconciliation only; no additional public contract delta.
2026-09-02 16:09:57 +01:00
rcourtman 5715b1032e Merge pull request #1868 from rcourtman/claude/metrics-startup-flake
Capture the metrics startup hook per store to end a test flake
2026-09-02 15:21:13 +01:00
Richard Courtman 1ac94620eb Capture the metrics startup hook per store to end a test flake
TestNewStoreDefersStartupMaintenance bounded NewStore at 200ms. On a
slow CI disk that bound tripped, the test failed, and its NewStore
goroutine kept running: the maintenance worker it spawned read the
package-level startupMaintenanceHook after the next test had installed
its own closure, closed that test's started channel a second time, and
panicked the whole rest-1 shard (run 33630289317, attempt 1).

Capture the hook once in NewStore so a store can only ever call the
hook that was installed when it was built. Prove deferral by ordering
instead of wall clock: the hook parks the worker, and NewStore must
return while it is parked. A regression to inline maintenance now
blocks that receive until the package timeout instead of flaking.
Cleanup releases the worker and waits for NewStore before restoring
the hook, so a failed run cannot leak a parked store either.

Contract-Neutral: behavioral test-flake fix with no public contract delta
2026-09-02 14:32:23 +01:00
pulse-triage[bot] d076775ee6 Merge current Pulse upstream for publication
Change-source: pulse-maintainer
2026-09-02 14:28:44 +01:00
rcourtman e6a5b8184f Merge pull request #1867 from rcourtman/claude/issue-1837-max-completion-tokens-v2
Send max_completion_tokens to GPT-5 models and learn it from the API
2026-09-02 14:24:32 +01:00
pulse-triage[bot] 10550628f9 Keep E2E validation isolated from rate limits
Core E2E shards aggregate many isolated browser sessions behind one Docker
bridge address, which can exhaust the production-sized general API limiter and
hide later assertions behind HTTP 429 responses. Raise only the explicitly
enabled development harness limit and keep malformed, unsafe, or production
overrides fail-closed.

Align the offline Proxmox journey with the reviewed native-button disclosure
ownership so adjacent links are checked against the actual disclosure control.

Change-source: pulse-maintainer
Contract-Neutral: Development-only validation isolation and regression alignment; production API limits and product behavior are unchanged.
2026-09-02 14:22:23 +01:00
Richard Courtman 9a8873c7a4 Send max_completion_tokens to GPT-5 models and learn it from the API
Patrol's readiness probe against a GPT 5.6 Luna deployment on Azure AI
Foundry failed with "Unsupported parameter: 'max_tokens' is not supported
with this model. Use 'max_completion_tokens' instead." The provider only
switched fields for o1/o3/o4 models, and only on api.openai.com, so the
GPT-5 family was sent max_tokens everywhere and Azure hosts were treated
as generic OpenAI-compatible endpoints.

Treat the GPT-5 family as reasoning models on the official and Azure
OpenAI hosts. Because an Azure deployment name need not reveal the model,
also act on the API's own instruction: when a 400 names max_completion_
tokens, re-send once in that form, drop the non-default temperature those
models refuse, and remember the answer for the life of the client so later
requests, including Patrol's streaming probes, go straight out correctly.

Refs #1837

Contract-Neutral: OpenAI provider parameter selection fix for issue 1837; no wire, API, or subsystem contract change
2026-09-02 13:29:53 +01:00
pulse-triage[bot] 492b1a1667 Merge reviewed platform table accessibility repair
Retain the exact browser-verified web candidate commit and integrate native disclosure ownership across platform tables.

Change-source: pulse-maintainer
2026-09-02 13:28:10 +01:00
pulse-triage[bot] b3716e0113 Merge reviewed metrics test stabilization
Retain the exact core-runtime candidate commit and integrate its deterministic maintenance-worker ordering and cleanup coverage.

Change-source: pulse-maintainer
2026-09-02 13:28:10 +01:00
pulse-triage[bot] 0a08c01132 Merge reviewed release convergence reconciliation
Retain the exact delivery-trust candidate commit and integrate its current-control retry and scheduled reconciliation behavior on canonical main.

Change-source: pulse-maintainer
2026-09-02 13:28:10 +01:00
pulse-triage[bot] da8731fd80 Merge frozen Pulse upstream for coordination
Incorporate the origin/main state captured at the start of the candidate batch while preserving unpublished canonical integration history. Keep the newer local browser receipt where the two already-verified lines overlap.

Change-source: pulse-maintainer
2026-09-02 13:27:45 +01:00
pulse-triage[bot] d7fdafcca5 Keep platform table disclosures on native buttons
Remove duplicate keyboard and disclosure semantics from static data-table rows across the platform surfaces. Preserve whole-row pointer convenience while leaving focus, accessible naming and expanded state on each existing native disclosure button.

The populated TrueNAS browser check also exposed an invalid combobox role on inline-only search completion, so keep it as a native textbox with aria-autocomplete.

Change-source: pulse-maintainer

Contract-Neutral: Accessibility semantics and regression coverage only; no product or API boundary changed
2026-09-02 13:17:07 +01:00
pulse-triage[bot] c09f0c07f2 Stabilize metrics startup maintenance tests
The race-enabled suite can take longer than the tests' fixed sleeps while opening SQLite stores. Assert worker ordering through channels instead, and always release and join blocked maintenance workers so a failed assertion cannot contaminate the following test.

Change-source: pulse-maintainer
2026-09-02 13:14:47 +01:00
pulse-triage[bot] f74de141b4 Reconcile convergence with current controls
GitHub reruns preserve the failed workflow SHA, so a repaired convergence control cannot resolve an already committed release. A missed workflow_run event can also leave mutable aliases stranded without another attempt.

Change-source: pulse-maintainer
2026-09-02 13:11:04 +01:00
rcourtman ff1d584a42 Merge pull request #1864 from rcourtman/claude/patrol-telemetry-provider-cost-v4
Add schema v17 Patrol provider, cost, autonomy, and outcome telemetry
2026-09-02 12:48:28 +01:00
pulse-triage[bot] 7e1543b2d6 Merge current Pulse upstream for publication
Incorporate the landed Patrol digest email work that reached origin/main after publication preflight while preserving reviewed tip 3016bc72a0 unchanged in local history. The upstream and reviewed file sets do not overlap.

Change-source: pulse-maintainer
2026-09-02 12:15:41 +01:00
rcourtman 3950e54060 Merge pull request #1865 from rcourtman/claude/patrol-digest-email
Email the Patrol weekly summary as a report schedule kind
2026-09-02 12:14:21 +01:00
pulse-triage[bot] 3016bc72a0 Merge commit '6399653836ef943bcfd4569ee992f9310513d323'
# Conflicts:
#	frontend-modern/browser-verification.json

Change-source: pulse-maintainer
2026-09-02 12:05:07 +01:00
pulse-triage[bot] 16f63e4864 Merge closed-PR CI capacity reclamation
Integrate the reviewed delivery-trust candidate while preserving commits af0e8f8d39 and 6575ebd928 unchanged in history.

Change-source: pulse-maintainer
2026-09-02 11:56:53 +01:00
rcourtman 4b4db5155a Merge origin/main into claude/patrol-telemetry-provider-cost-v4
Keeps this change's browser verification receipt; main's receipt belonged to the
navigation change that landed in #1863.
2026-09-02 11:52:20 +01:00
pulse-triage[bot] 48761126bd Merge current Pulse upstream for publication
Incorporate the landed docs-mirror race fix and exact PR #1863 merge while preserving reviewed local tip 93ed81e7e9 unchanged in history.

Change-source: pulse-maintainer
2026-09-02 11:51:45 +01:00
rcourtman 2f0ae671b0 Merge pull request #1863 from rcourtman/claude/core-e2e-nav-links
Restore Core E2E on main: link navigation, Patrol controls, badge contrast
2026-09-02 11:50:11 +01:00
pulse-triage[bot] 6399653836 Fix Assistant autocomplete screen reader focus
The composer kept DOM focus in its textarea while only visually indicating the active autocomplete option. Screen reader users therefore could not identify the controlled listbox or follow mention and slash-command selection changes.

Change-source: pulse-maintainer

Contract-Neutral: Assistant autocomplete accessibility and responsive presentation only; no API or subsystem contract changed
2026-09-02 11:32:57 +01:00
rcourtman cfac267563 Merge pull request #1859 from rcourtman/claude/docs-mirror-test-gc-race
Stop background git from racing test_docs_mirror temp cleanup
2026-09-02 11:27:52 +01:00
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