Commit Graph

10251 Commits

Author SHA1 Message Date
pulse-triage[bot] b710f3ea06 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-04 06:04:48 +01:00
pulse-triage[bot] d107a1e703 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-04 06:04:48 +01:00
pulse-triage[bot] 9b12b2fee8 Keep release-line refresh proof current
Backport the browser-only refresh proof corrections from main. Exercise the
always-present History tab, follow the Proxmox surface's source-scoped canonical
resource query, trigger the metadata refresh path, and await the completed
response before checking drawer and viewport stability.

Contract-Neutral: browser regression coverage only; no runtime or API change.
Change-source: pulse-maintainer

Backport-of: 07c2f9ffa0
Backport-of: cf5e363979
2026-09-04 05:52:46 +01:00
pulse-triage[bot] 37ce538806 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.

(cherry picked from commit 724a8a4960)
2026-09-04 05:05:29 +01:00
pulse-triage[bot] c1b671631d 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
(cherry picked from commit 384dc53608)
2026-09-04 03:25:23 +01:00
pulse-triage[bot] 54a0b953d5 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
(cherry picked from commit 9d5cdfa088)
2026-09-04 01:32:07 +01:00
pulse-triage[bot] e952418663 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
(cherry picked from commit da9800ff2f)
2026-09-04 01:32:07 +01:00
pulse-triage[bot] fbedb2d50d 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
(cherry picked from commit 59e9f5ea2b)
2026-09-04 01:29:09 +01:00
pulse-triage[bot] 562607f7a2 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.
Change-source: pulse-maintainer
2026-09-03 22:03:49 +01:00
pulse-triage[bot] b4a00e1ca5 Accept TrueNAS CORE 12 legacy REST versions
CORE 12 appliances can report versions such as TrueNAS-12.0-U5 without a CORE product marker, and they do not expose the JSON-RPC endpoint required by modern SCALE releases. The existing legacy check therefore rejected a supported REST-capable appliance before collection could begin.

Recognise the explicit TrueNAS 12 family while retaining the fail-closed boundary for modern releases, and cover negotiation with the reported version string.

Contract-Neutral: TrueNAS legacy-version negotiation fix; no wire, API, persisted-data, or subsystem contract change
Change-source: pulse-maintainer
2026-09-03 22:03:18 +01:00
Richard Courtman 1694e673a6 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-03 21:28:58 +01:00
rcourtman f2c76f1ab6 Merge pull request #1872 from rcourtman/claude/backport-qualification-v6.4
Backport: qualify release candidates against the branch that owns their version
2026-09-02 18:40:02 +01:00
Richard Courtman 85fb558f0a 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
(cherry picked from commit 79d87a4a2c)
2026-09-02 18:39:30 +01:00
rcourtman 0869ba406d Check out the repository before the Helm chart version step
de41ea1883 replaced the chart-version step's output echoes with
scripts/write_github_output.py, but that step runs before the repository
is checked out, so the script does not exist yet and the Helm publish
fails with "can't open file ... write_github_output.py" on every release.
The v6.4.3-rc.1 run 33580123246 hit it after all other staging succeeded.
Run the checkout first; nothing in it depends on the version outputs.
v6.4.3-rc.1 helm-chart-6.4.3-rc.1
2026-09-02 03:22:30 +01:00
rcourtman 198f0ca11a Map the v6.4.3 patch line to release/v6.4
The v6.4.3-rc.1 dispatch from main (run 33579042375) failed inside the
compiler dispatch: main advanced one minute after the release pipeline
pinned its source SHA, so the exact-SHA identity check on the compiler run
correctly refused the moved head. With the maintainer landing pull requests
every few minutes, a candidate dispatched from main cannot hold its SHA for
the minutes between prepare and compile.

Declare release/v6.4 for the 6.4.3 version prefix, created from main at the
exact-SHA-qualified commit 56e51e622e, so the workflow refuses a v6.4.3
dispatch from any other branch and the compiler binding stays exact. This is
the delivery contract's branch-per-train rule applied to the patch line that
predates the first train; earlier 6.4.x versions keep their historical main
mapping, and the 6.5 train mapping is unchanged.
2026-09-02 02:36:57 +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 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 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
rcourtman 500cc1bf17 Harden rootful qualification evidence boundary 2026-09-01 23:53:36 +01:00
rcourtman c022a0dfe6 Raise browserslist above security advisory floor 2026-09-01 23:33:55 +01:00
rcourtman b1044cd8a4 Let replayed request ids wait for the in-flight handler instead of dropping
Since 60d0651a88 every typed request registers a per-connection cancellable
slot that its handler goroutine releases in a deferred cleanup after sending
its result. The server replays a request id when it wants the durable receipt
again, and that replay can reach the reader before the previous handler's
deferred release runs. launchCancellableRequest treated that as a duplicate
and dropped it, so the server waited out the operation's full timeout for a
result the agent already held. The Linux x64 native-verification leg failed
this way on 12 of the last 25 main runs, always on a "replay 1" dispatch of
host update, storage cleanup, or Docker lifecycle.

Give each slot a done channel that closes on release. A replay whose id is
still registered on the same connection now waits for that release and then
runs, answering from the durable receipt. Invalid ids and over-capacity
requests are still dropped. A unit test pins the wait-then-run behaviour and
the agent-lifecycle contract records the replay rule.
2026-09-01 23:22:55 +01:00
rcourtman b763b80680 Build the FIFO lifecycle installer test only on unix
agent_state_dir_lifecycle_test.go calls syscall.Mkfifo, which does not
exist on Windows, so scripts/installtests has failed to compile in the
Windows leg of Unified Agent Native Verification since 53267e149d and the
install.ps1 contract tests there have not run. Every test in the file
drives install.sh through bash and systemd, so tag the file unix-only,
matching the other lifecycle lab files. GOOS=windows go vet now passes.
2026-09-01 23:05:22 +01:00
rcourtman e1e4c3e700 Wait for the server to observe runner disconnect before replay reconnect
The cancellation replay test stopped the first action runner and started a
second one as soon as the client goroutine exited. The server observes the
socket close on its own reader, so under GOMAXPROCS=1 with the race detector
the second startRunner saw the stale session as still connected and the
replay was dispatched to the dead socket, timing out after 30s on the
sharded release preflight while passing on multi-core hosts. Wait until the
server reports the agent disconnected before reconnecting, as the agentexec
server tests already do.
2026-09-01 22:51:56 +01:00
rcourtman 584cef81a1 Add rootful runtime qualification packet 2026-09-01 22:40:50 +01:00
rcourtman 813c2b6837 Match CI temp layout in the preflight worker and isolate agent helper tests
go test places t.TempDir under GOTMPDIR, and the preflight worker nested that
under its per-run directory, so unix socket fixtures in cmd/pulse-agent-helper,
internal/agenthelper, and internal/dockeragent exceeded the 108-byte sun_path
limit and failed with "bind: invalid argument". GitHub runners leave GOTMPDIR
unset, and the longest rootless Podman fixture sits exactly at the limit under
/tmp, so the worker now leaves GOTMPDIR unset by default and only honours an
explicit PULSE_RELEASE_PREFLIGHT_GO_TMP_DIR override.

Two cmd/pulse-agent helper tests ran without a state directory, so the agent
consulted the platform default /var/lib/pulse-agent. On a host where that
path exists as a private directory the pending-update handoff lookup fails
with permission denied. Give those tests an isolated state directory.
2026-09-01 22:35:55 +01:00
pulse-triage[bot] 020eb11edb Merge pull request #1821 from rcourtman/maintainer/20260901T212928Z
Keep release-candidate builds bound to the dispatched compiler run
2026-09-01 22:34:34 +01:00
pulse-triage[bot] 5fb7177b8a Require compiler dispatch run details
Change-source: pulse-maintainer
2026-09-01 22:26:46 +01:00
rcourtman 96a4dceb9d Pin umask 022 in installtests so trust fixtures are host-independent
install.sh refuses connection state, token, and lifecycle files whose
parent directory is group- or world-writable. The fixtures build those
parents with t.TempDir, which inherits the process umask, so on a worker
with umask 002 the state directory came out 775 and seven TestInstallSH
cases failed on the pulse-dev release preflight while passing on GitHub
runners and macOS. Set umask 022 for the package on unix so the tests
prove the installer contract rather than the host's umask.
2026-09-01 22:20:15 +01:00
pulse-triage[bot] 95b4eceaa0 Merge current origin/main before publication
Incorporate upstream rootless-runtime qualification evidence while preserving the reviewed maintenance batch and additive trust-gate correction.

Change-source: pulse-maintainer
2026-09-01 22:14:49 +01:00
pulse-triage[bot] 5b204cdc75 Close mirrored workflow scalar syntax gaps
Recognize bare sequence entries and every valid block scalar header across executable action inputs, run scripts, and step environment boundaries.

Change-source: pulse-maintainer
2026-09-01 22:13:37 +01:00
rcourtman 45dd45fe29 Record rootless runtime qualification evidence 2026-09-01 22:09:22 +01:00
pulse-triage[bot] 544b7fd575 Merge batch-start origin/main
Change-source: pulse-maintainer
2026-09-01 21:51:21 +01:00
pulse-triage[bot] f581f955e2 Merge remote-tracking branch 'origin/main'
Change-source: pulse-maintainer
2026-09-01 21:40:47 +01:00
rcourtman 152ef86d33 Activate mise shims in the release preflight worker
The exact-SHA preflight streams the worker script over a non-login ssh
shell, so /etc/profile.d/mise.sh is never sourced and a stale system Node
binary on the worker shadows the infra-managed mise toolchain. pulse-dev
carried a manually installed Node 20 symlink in /usr/local/bin, so the
worker failed its Node 24 check while a login shell resolved 24.19.0.
Activate mise shims when mise is installed so the worker uses the same
toolchain the bootstrap provisions, regardless of how it is invoked.
2026-09-01 21:40:05 +01:00
rcourtman 62bfdbca7e Prepare v6.4.3-rc.1 release
Open the v6.4.3 candidate line from main. The v6.4.2 tag was staged on
2026-08-31 but never activated: its release run was cancelled after the
private Pro build failed the compiler memory gate, so the latest published
stable is still v6.4.1. This candidate carries the complete v6.4.2 change
set plus the corrections landed since that tag, including the stale PBS
Backup Running state (#1815), the Windows Unified Agent auto-update 404
(#1820), and shared-token same-hostname agent identity collapse (#1753).

Packet: VERSION, compose and install-docker defaults, Helm chart metadata,
release notes with a declined visual plan, changelog, pointer docs and
the shipped docs mirror, and the deployment-installability cutoff note.
Rollback target is v6.4.1 and the mobile decision is no-mobile-impact.

Tests: the packet tests now describe the 6.4.3 train, v6.4.2 is recorded
as an unpublished stable so it is never derived as the previous stable or
rollback target, and the Python v6.4.2 notes expectation matches the
phrase the notes actually use.
2026-09-01 21:37:41 +01:00
pulse-triage[bot] 22cc59cc2c Reject template injection in executable action inputs
Change-source: pulse-maintainer
2026-09-01 21:35:35 +01:00
pulse-triage[bot] 73bd27a0f5 Merge remote-tracking branch 'origin/main'
Change-source: pulse-maintainer
2026-09-01 21:30:09 +01:00
rcourtman 60041ad9e6 Validate each rootless socket identity 2026-09-01 21:26:49 +01:00
pulse-triage[bot] a869475bf1 Reject flow-nested YAML trust aliases
Change-source: pulse-maintainer
2026-09-01 21:21:45 +01:00
pulse-triage[bot] 8cd5c2cae5 Reject hidden workflow trust structure
Change-source: pulse-maintainer
2026-09-01 21:18:26 +01:00
rcourtman b87ab75965 Limit planning status audit to tracked documents 2026-09-01 21:18:24 +01:00
rcourtman bc3dd3b704 Give rootless qualification hosts unique identities 2026-09-01 21:18:24 +01:00
pulse-triage[bot] 4fda8f146e Merge batch-start origin/main
Change-source: pulse-maintainer
2026-09-01 20:58:31 +01:00