Commit graph

4213 commits

Author SHA1 Message Date
rcourtman
2eeb643afe Prepare v6.3.0-rc.2 release
Some checks are pending
Public docs / check (push) Waiting to run
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
2026-08-18 13:31:21 +01:00
rcourtman
7cb3fc2e6b fix(actions): link reviews back to Patrol records 2026-08-18 13:23:02 +01:00
rcourtman
bcea56db97 fix(patrol): clarify decisions and action origins 2026-08-18 12:44:20 +01:00
rcourtman
ac4116e09b feat(patrol): simplify the decision workspace 2026-08-18 11:51:29 +01:00
rcourtman
95c253c486 feat(actions): make Actions a primary workspace 2026-08-18 11:11:21 +01:00
rcourtman
b396c93785 style(frontend): remove semicolons from product copy
Contract-Neutral: Customer-facing punctuation cleanup only; no API, lifecycle, policy, or subsystem behavior changed. The owning frontend copy contract and lint enforcement are updated.
2026-08-18 10:52:38 +01:00
rcourtman
fe9c64a4d3 Stop the authenticated bootstrap pulling the full state payload
The shell fetched `/api/state` on every sign-in for two reasons: to learn
whether the session was authenticated, and to have something to render
before the socket connected. On a real estate that is 96% resources, and
the fetch sat on the critical path ahead of the websocket, which could not
begin connecting until it returned.

Neither reason survives. The session probe is now `/api/state/summary`,
which passes the same auth and monitoring-read scope checks and returns a
few hundred bytes. Navigation resolves from the platform admission facet.
Nothing else in the shell read the payload: `state()` had exactly three
consumers, two of them navigation and one an alert-count badge that the
socket supplies.

`/api/state` itself is untouched. It is the recovery path for a snapshot
too large for a websocket frame, which is precisely what the largest
estates need, so shrinking the endpoint would have removed the escape
hatch for the installs that depend on it most.

The seeded bootstrap state is gone rather than left empty. Keeping it
would have been actively unsafe: `runtimeStateResolved()` meant "a payload
arrived", so a bootstrap that no longer carried resources would still read
as resolved and navigation would classify an empty estate, hiding every
platform tab. Resolution is now websocket-backed, and an empty estate is
answered by the facet admitting nothing, which is a resolved answer rather
than an absent one.

Verified against two backends built from this change: a populated estate
resolves all six tabs, lands on the Proxmox overview and renders every
platform page, and an estate with zero resources shows no platform tabs in
the navigation landmark and lands on the infrastructure settings page.
Both issue one summary probe and zero `/api/state` requests.

Known regression: the alert-count badge is empty for the sub-second window
before the socket delivers alerts, where the bootstrap payload used to
supply it. The alerts store is not socket-synced, so reading the badge
from it would have traded a brief empty count for a lastingly stale one.

Tests that pinned the old mechanism were rewritten to their subjects: the
SSO and proxy-auth cases keep their auth assertions, and the empty-estate
distinction now asserts the facet admitting nothing rather than a
full-state payload arriving.

Contracts: performance-and-scalability records that the bootstrap is
estate-independent and that `/api/state` stays the recovery path rather
than a bootstrap surface; cloud-paid records the tenant probe.
2026-08-18 10:33:11 +01:00
rcourtman
ed5d983e8e feat(patrol): simplify the operator workspace 2026-08-18 10:25:01 +01:00
rcourtman
e1427e66f8 Keep platform admission fresh across tenant switch and reconnect
The shell read the admission facet once at sign-in, which left two ways
for navigation to be wrong.

Switching organization was the serious one, and it was a defect in the
change that introduced the facet. The switch clears bootstrap state but
left admission untouched, so between the switch and the arrival of the new
tenant's runtime state the shell would render the outgoing tenant's
platform tabs. Admission is now cleared synchronously on the switch,
before anything can render from it, and refetched for the new tenant.

The second is a reconnect: an estate can gain or lose a platform while the
socket is down, so admission is refetched when the socket comes back. That
was verified by stopping the backend mid-session and restarting it, not by
toggling the browser offline, which leaves an established socket open and
silently proves nothing.

No polling is introduced. Live runtime state stays authoritative once it
arrives and tracks the estate itself, so a steady-state session makes no
further admission requests; that was measured rather than assumed.

I dropped a periodic backstop I had planned for sessions whose websocket is
blocked. The premise was wrong: such a session still receives runtime state
from the REST bootstrap, so navigation is already resolved and the timer
never fires. Verified against a socket-blocked session before removing it,
rather than shipping a session-long timer for a case that does not occur.

Contracts: cloud-paid gains the tenant-switch rule, and
performance-and-scalability records that admission refresh is event-driven
rather than polled.
2026-08-18 10:11:02 +01:00
rcourtman
0da7092991 feat(patrol): guide operators through the decision queue 2026-08-18 09:31:14 +01:00
rcourtman
4606b31836 Resolve shell navigation from the canonical admission facet
The shell decided which platform pages exist by classifying every resource
in the runtime state payload, so navigation could not resolve until an
estate-sized payload had arrived. It now reads the platformAdmission facet
from the canonical resource contract, which answers the same question from
a one-resource request: 2KB against 314KB on the estate this was verified
on.

Precedence is deliberate. Live runtime state stays authoritative once it
arrives, because it tracks an estate that can gain a platform after the
facet was read; the facet only resolves navigation before that payload
exists. The two agree by construction, so the handover is not visible. A
server that does not report the facet keeps the previous behaviour rather
than hiding platforms the estate really has, and a partial facet payload is
rejected outright, since a missing flag would read as a hidden platform.

Verified against a backend built from the commit that publishes the facet:
navigation resolves with all six tabs, every platform page renders its own
rows, and the shipped facet equals the client classifier over the same
658-resource estate.

The architecture pin on the authenticated bootstrap gains the new loader.
Its invariant, that branding loads inside that bootstrap, is unchanged.

This does not yet free the state payload: it remains the steady-state
source for navigation, and other shell surfaces still read it.

Contracts: performance-and-scalability gains the rule that admission cost
must not scale with estate size; cloud-paid, ai-runtime and
storage-recovery gain the matching clauses for tenant scoping, hosted
surfaces and TrueNAS visibility.
2026-08-18 09:22:40 +01:00
rcourtman
ebba3c493b feat(patrol): turn attention into a daily briefing 2026-08-18 07:49:30 +01:00
rcourtman
c6cafa6e25 feat(patrol): surface pending operator decisions 2026-08-18 07:48:45 +01:00
rcourtman
eb279d089b Report platform admission on the canonical resource aggregations
The app shell decides which primary platform pages exist by classifying
every resource in the legacy full-state payload, which is why it has to
download that payload before it can render navigation. This publishes the
same answer as a `platformAdmission` facet on the canonical resource
aggregations, so admission has one definition instead of two that can
drift.

Counts cannot answer it, which is the whole reason this is a facet rather
than a client-side tally over `bySource`. A TrueNAS or Proxmox host
reports through the agent source and carries the "agent" platform scope,
so a count-based derivation admits the standalone page for an estate that
has no Pulse agent in it at all. Ownership is per-resource evidence, so it
is evaluated per resource here.

Verified against the live client classifier over real estates rather than
by inspection: the facet and the classifier agree on all six pages across
ten estates each for a 652-resource and a 216-resource estate, including
the cases that break a count-based derivation (Proxmox-only, TrueNAS-only,
vSphere-only and provider-owned-agents-only estates all correctly withhold
the standalone page, while a genuine Pulse agent admits it).

The tenant-fallback contract pin gains the new field. Its invariant, that
an empty tenant returns no resources rather than seeding from the raw
snapshot, is unchanged and still pinned; an empty estate admits nothing.

No caller reads the facet yet. Moving the shell onto it is a separate
change, gated by the same parity comparison.

Contracts: unified-resources and api-contracts gain the facet and its
derivation rule; agent-lifecycle and storage-recovery gain the ownership
clause, since an agent-typed host owned by a provider must not admit the
standalone page.
2026-08-18 07:35:34 +01:00
rcourtman
3088f52dc0 feat(patrol): redesign the operator workbench
Some checks are pending
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
2026-08-17 23:41:03 +01:00
rcourtman
2c59fb742e Prepare v6.3.0-rc.1 release
Some checks failed
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Patrol Qualification Regression / Catalog, scorer, and replay regression (push) Has been cancelled
2026-08-17 22:04:54 +01:00
rcourtman
ba90475e59 Fix subscription agent timeout cleanup 2026-08-17 21:34:30 +01:00
rcourtman
b1fc6c7dca Prepare v6.2.2-rc.3 release 2026-08-17 20:57:22 +01:00
rcourtman
6ab45a900c Reject contradicted Patrol causal proposals 2026-08-17 20:02:48 +01:00
rcourtman
da8537b8e6 Reject incoherent Patrol finding identities 2026-08-17 20:02:47 +01:00
rcourtman
a83e15176a Bound app-container Patrol scope to selected workloads 2026-08-17 20:02:47 +01:00
rcourtman
9f23d40c43 Canonicalize container health finding vocabulary 2026-08-17 20:02:47 +01:00
rcourtman
21e0b40363 Preserve Patrol controls during full-mode activation
Contract-Neutral: Patrol autonomy behavior is documented in its primary ai-runtime and shared api-contracts contracts plus the relevant agent-lifecycle boundary; storage-recovery only has a broad internal/api extension reference and does not own this wire contract
2026-08-17 20:02:47 +01:00
rcourtman
59b0ee188b Repair scoped Patrol finding identity safely 2026-08-17 20:02:47 +01:00
rcourtman
39ceeda604 Scope Patrol health queries to resources 2026-08-17 20:02:47 +01:00
rcourtman
31f89bb430 Canonicalize app container health check findings 2026-08-17 20:02:47 +01:00
rcourtman
5eae262363 Canonicalize unhealthy container finding keys 2026-08-17 20:02:47 +01:00
rcourtman
7422da843a Enable Patrol qualification on fresh installs 2026-08-17 20:02:47 +01:00
rcourtman
8dc771f09b Keep default monitor token inventory current 2026-08-17 20:02:47 +01:00
rcourtman
f6aa8db93a Bound Patrol subscription continuation latency 2026-08-17 20:02:47 +01:00
rcourtman
37d6e29e84 Preserve partial-response byte ranges 2026-08-17 20:02:47 +01:00
rcourtman
91737ee207 Canonicalize Patrol health finding keys 2026-08-17 20:02:47 +01:00
rcourtman
90081dacb4 Handle informational and bodyless gzip responses
Some checks are pending
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Patrol Qualification Regression / Catalog, scorer, and replay regression (push) Waiting to run
Public docs / check (push) Waiting to run
Core E2E Tests / Validate E2E tier selection (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Blocked by required conditions
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Blocked by required conditions
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run
2026-08-16 17:24:01 +01:00
rcourtman
2e4e85d9af docs: add focused security review packet 2026-08-16 17:13:02 +01:00
rcourtman
a682af0b7b Harden large-estate response and polling limits 2026-08-16 17:07:59 +01:00
rcourtman
d08ca46194 Expose Docker health-check dependencies to Patrol 2026-08-16 16:58:44 +01:00
rcourtman
25368abcdb Merge remote-tracking branch 'origin/main' 2026-08-16 16:52:08 +01:00
rcourtman
bbd67e0ae0 docs: clarify security review scope 2026-08-16 16:28:19 +01:00
rcourtman
40897a46e6 Preserve causal evidence in Patrol conclusions 2026-08-16 15:48:03 +01:00
rcourtman
ab34168731 Disambiguate Docker service evidence for Patrol 2026-08-16 15:26:12 +01:00
rcourtman
17ccb8240d Preserve causal evidence before Patrol proposals 2026-08-16 11:45:24 +01:00
rcourtman
64db84a7e3 Clarify governed restart proposal policy 2026-08-16 11:32:36 +01:00
rcourtman
870e2f7f97 Rewire Patrol investigation after chat restart
Contract-Neutral: AI chat lifecycle rewiring does not change dependent agent, performance, security, or storage contracts
2026-08-16 11:15:16 +01:00
rcourtman
4195943300 Harden Patrol investigation evidence traversal 2026-08-16 10:56:34 +01:00
rcourtman
85db074eff copy: simplify security guidance punctuation
Contract-Neutral: Punctuation-only copy revision with no lifecycle behavior or contract change
2026-08-16 10:36:54 +01:00
rcourtman
787a191a5e docs: publish security review evidence 2026-08-16 10:23:15 +01:00
rcourtman
effcf2d50c Harden Patrol qualification lifecycle 2026-08-16 10:14:16 +01:00
rcourtman
88c380b514 docs: add production security deployment guide 2026-08-16 09:21:36 +01:00
rcourtman
d0e7c747d9 Clear stale ZFS alerts when a storage loses its pool attachment
checkZFSPoolHealth only runs while storage.ZFSPool is attached, so its
clear paths never fire once the attachment goes away and previously raised
zfs-pool-state, zfs-pool-errors, and zfs-device alerts linger until the
multi-day stale cleanup. Clear them on the spot when a storage is checked
without a pool. This is the recovery path for the shared storages that were
wrongly matched to a node-local pool before the matcher's type gate.

Refs #1731
2026-08-16 06:27:23 +01:00
rcourtman
1ff0680c75 Stop attaching node-local ZFS pools to shared storages
matchZFSPoolForStorage's sole-pool fallback attached the node's only ZFS
pool to every storage the per-node endpoint returned, including NFS, CIFS,
PBS, and RBD entries that can never be backed by a local pool. One failing
device then raised a duplicate ZFS device alert per shared storage. Gate
the matcher on isInherentlySharedStorageType so remote-backed storages are
never matched, while dir-type storages keep the single-pool fallback.

Refs #1731
2026-08-16 06:22:55 +01:00