diff --git a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md index 040f6287b9..351c8913c4 100644 --- a/docs/release-control/v6/internal/subsystems/agent-lifecycle.md +++ b/docs/release-control/v6/internal/subsystems/agent-lifecycle.md @@ -6828,3 +6828,17 @@ authority state. Agent lifecycle remains the sole owner of the relationship; alerts receives only a bounded presentation identity after that relationship has already been established. The reciprocal and fail-open cases are pinned in `internal/monitoring/monitor_host_agents_test.go`. + +### Proxmox package evidence does not create agent authority + +The PVE API poll may publish a node's bounded package-update evidence through +the shared node model whether or not a Unified Agent is linked to that node. +`pendingUpdates`, its last successful check time, and the fixed evidence status +and reason describe only the Proxmox package source. They cannot enroll, link, +command, update, revoke, remove, or re-enroll an agent, and a linked agent does +not become an alternative authority for the package count. + +The node drawer may use `Node.LinkedAgentID` to label Pulse coverage, but that +presentation does not combine agent lifecycle with package evidence. The +polling states are pinned independently in +`internal/monitoring/node_pending_updates_evidence_test.go`. diff --git a/docs/release-control/v6/internal/subsystems/ai-runtime.md b/docs/release-control/v6/internal/subsystems/ai-runtime.md index 86c45812fe..0d8540cd84 100644 --- a/docs/release-control/v6/internal/subsystems/ai-runtime.md +++ b/docs/release-control/v6/internal/subsystems/ai-runtime.md @@ -7774,3 +7774,17 @@ leaves page content visible and interactive below the backdrop while the Assistant is open, which is a modal-containment defect rather than a cosmetic one. The bar owns that value; see the frontend-primitives contract. Proofs live in `frontend-modern/src/components/AI/Chat/__tests__/AIChat.test.tsx`. + +### Mobile destination links do not expand AI authority + +`AppLayout` supplies the same remembered primary route to desktop and mobile +anchors. Converting the Patrol destination from a button to an anchor changes +browser navigation semantics only: native modified activation may open the +existing Patrol route in another browsing context, while plain activation uses +the existing shell callback. It does not start an investigation, approve an +action, invoke a tool, or create mutation authority. + +Menu-opening controls remain buttons, and the mobile bar continues to own +`--pulse-mobile-nav-height`; Assistant overlay containment and action approval +boundaries are unchanged. Mobile navigation and AppLayout tests pin the route +and focus behavior. diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index 0334403d9e..0d4c31f6f2 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -10154,3 +10154,20 @@ sharing mutable pointers; the frontend API type mirrors the optional wire shape exactly. `internal/alerts/correlation_test.go` and `frontend-modern/src/features/alerts/__tests__/useAlertOverviewState.test.tsx` are the focused transport-consumer proofs. + +### Proxmox package-update evidence is additive and closed + +Proxmox node payloads retain the numeric `pendingUpdates` field and may add +`pendingUpdatesCheckedAt`, `pendingUpdatesStatus`, and +`pendingUpdatesReason`. Status is the closed set `checked`, `stale`, +`unavailable`, or `not_checked`; reason, when present, is the closed set +`permission_denied`, `source_unavailable`, `check_failed`, or `node_offline`. +A checked zero is authoritative evidence rather than an absent capability. + +The reason is a server-owned diagnostic category and must never contain the +provider error text, endpoint, credential, or response body. Older clients may +ignore the additive fields, while newer clients retain legacy positive counts +that predate explicit status. Model, resource, and presentation contracts are +pinned by `internal/monitoring/node_pending_updates_evidence_test.go`, +`internal/unifiedresources/proxmox_update_evidence_test.go`, and +`frontend-modern/src/utils/__tests__/proxmoxUpdateEvidence.test.ts`. diff --git a/docs/release-control/v6/internal/subsystems/cloud-paid.md b/docs/release-control/v6/internal/subsystems/cloud-paid.md index 529efb7fc4..0d5a05fd7f 100644 --- a/docs/release-control/v6/internal/subsystems/cloud-paid.md +++ b/docs/release-control/v6/internal/subsystems/cloud-paid.md @@ -3440,3 +3440,12 @@ bootstrap group beside runtime branding, after authentication and organization context resolve. Hosted-mode, organization, license, entitlement, and presentation-policy behavior are unchanged. `reduceProUpsellNoise` remains a compatibility display flag; it does not grant or derive commercial capability. + +### Shell links do not bypass commercial capability checks + +The shared mobile shell now exposes destination `href` values, including for +native modified and context-menu navigation. Those links resolve only routes; +they do not enable a platform, grant an entitlement, select an organization, +change hosted mode, or bypass a server capability check. An unconfigured +platform still resolves to infrastructure settings, and paid destinations +continue to render and authorize from the existing runtime entitlement state. diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 5f7699224f..261024dd31 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -6819,3 +6819,23 @@ The labels retain singular/plural behavior, use the existing responsive button primitives, and introduce no parallel group component. Localization and presentation proofs live in `frontend-modern/src/i18n/__tests__/i18n.test.ts` and `frontend-modern/src/utils/__tests__/alertOverviewPresentation.test.ts`. + +### Mobile destinations are links and update evidence stays explicit + +Fixed mobile destinations and menu destinations that navigate are anchors with +real `href` values. Plain primary activation remains in the SPA so route +warming and per-platform route memory continue to apply; modified activation, +middle-click, and context-menu actions remain native browser behavior. +Platform and overflow controls that open menus remain buttons. Disabled +platform anchors resolve to infrastructure setup, and menu focus, Escape +return, active state, badges, and narrow-layout containment remain unchanged. +`MobileNavBar.test.tsx`, `mobileNavBarModel.branchcov0712.test.ts`, and +`AppLayout.test.tsx` pin those distinctions. + +The Proxmox node drawer presents checked zero, current positive, stale, +unavailable, and not-checked package evidence as a labelled detail row with +bounded copy and checked time. The compact table badge remains reserved for a +current positive observation. Provider errors are never rendered. Component +and browser evidence covers both 1440px and 390px layouts in +`NodeDrawerOverview.updateEvidence.test.tsx`, `ProxmoxNodesTable.test.tsx`, and +`frontend-modern/browser-verification.json`. diff --git a/docs/release-control/v6/internal/subsystems/monitoring.md b/docs/release-control/v6/internal/subsystems/monitoring.md index 154a782f24..ce317b659a 100644 --- a/docs/release-control/v6/internal/subsystems/monitoring.md +++ b/docs/release-control/v6/internal/subsystems/monitoring.md @@ -3476,3 +3476,19 @@ owns the correlation wire type and presentation semantics; monitoring owns the identity evidence that permits the host signal to join that system. `internal/monitoring/alert_correlation_test.go` pins reciprocal-link admission and every fail-open case. + +### Proxmox update polling preserves evidence quality + +The 30-minute node package poll treats one successful response, including an +empty package list, as `checked` and records its completion time. A failed read +must remain an error through the cluster-client fallback boundary. Monitoring +maps it to a fixed privacy-safe reason and reports `unavailable` when no prior +success exists or `stale` with the last successful count and timestamp when a +cache entry exists. An offline node is `not_checked` with `node_offline`. + +Failures never refresh the successful timestamp or convert permission and +reachability errors into zero. Cached successful observations remain checked +until their normal refresh boundary, and a failed refresh is retried without +discarding the cache. `node_pending_updates_evidence_test.go` and the cluster +client pending-update tests pin zero, permission, reachability, stale-cache, +and offline behavior. diff --git a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md index 79c49cfb29..8dd2c243f4 100644 --- a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md +++ b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md @@ -2908,3 +2908,12 @@ rebuilding the estate. `internal/mock/alert_incidents_test.go` proves bounded resource results and defensive-copy isolation, while `internal/api/alerting/alerts_test.go` proves that mock transport does not need the production incident store. + +### Update evidence reuses the bounded node observation + +Package evidence adds only one bounded status, one bounded reason, and the +existing count and timestamp to each Proxmox node facet. It introduces no new +poll loop, websocket channel, history series, or per-render scan: collection +retains the existing 30-minute cache and frontend projection/presentation is +constant work per rendered node. Failed refreshes reuse the single cached +successful observation rather than accumulating attempts or provider errors. diff --git a/docs/release-control/v6/internal/subsystems/storage-recovery.md b/docs/release-control/v6/internal/subsystems/storage-recovery.md index 7e3616f182..93b5c82034 100644 --- a/docs/release-control/v6/internal/subsystems/storage-recovery.md +++ b/docs/release-control/v6/internal/subsystems/storage-recovery.md @@ -5742,3 +5742,13 @@ inventory, a recovery point, restore evidence, retention state, or durable storage metadata. Mock notes must not be written to the production incident store or survive a fixture-graph rebuild, and real-mode incident persistence remains unchanged. The mock API and fixture tests pin that isolation. + +### Package-update evidence is not recovery state + +The Proxmox fields projected through `useUnifiedResources` and +`ResourceProxmoxMeta` describe the latest package-list observation only. A +count, checked time, stale marker, or failure category is not backup inventory, +a recovery point, retention evidence, restore authority, or permission to +install packages. Canonical merging may replace an older count with a checked +zero or clear inherited check metadata for unavailable evidence, but it must +not mutate storage or initiate remediation. diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 209c2ac9c9..1187cfe1a1 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -4868,3 +4868,19 @@ that agent view once instead of duplicating the PVE inventory. `frontend-modern/src/utils/__tests__/resourceStateAdapters.test.ts` pin collection continuity, canonical projection, and both unified and Proxmox-page drawer presentation. + +### Proxmox update evidence merges as one authoritative observation + +The Proxmox node facet carries `pendingUpdates`, `pendingUpdatesCheckedAt`, +`pendingUpdatesStatus`, and `pendingUpdatesReason` through adapters, registry +cloning, typed views, and frontend state adapters. When explicit status is +present, the incoming observation is authoritative as a unit: a checked zero +clears an older positive count, and unavailable or not-checked evidence cannot +inherit an older checked timestamp or status. Legacy status-less positive +counts remain compatible. + +Consumers must not infer a successful zero from absent fields. The node drawer +uses the explicit state for details, while the compact node-row badge requires +current evidence. `proxmox_update_evidence_test.go`, `views_test.go`, and the +frontend evidence presentation tests pin transport, merge, and consumer +behavior. diff --git a/frontend-modern/browser-verification.json b/frontend-modern/browser-verification.json index 024af55138..f596f82530 100644 --- a/frontend-modern/browser-verification.json +++ b/frontend-modern/browser-verification.json @@ -1,27 +1,39 @@ { "version": 1, - "base_sha": "d671f05408c909dc1e418ac149192c5db01d3d84", - "verified_at": "2026-08-29T17:05:39Z", + "base_sha": "5926b4dc38c84e086593476cec1d65c047ef1182", + "verified_at": "2026-08-29T20:31:20Z", "result": "passed", "changed_paths": [ - "frontend-modern/src/features/alerts/AlertOverviewActiveAlertsSection.tsx", - "frontend-modern/src/features/alerts/useAlertOverviewState.ts", - "frontend-modern/src/i18n/messages.de.ts", - "frontend-modern/src/i18n/messages.es.ts", - "frontend-modern/src/i18n/messages.ts", + "frontend-modern/src/AppLayout.tsx", + "frontend-modern/src/components/Workloads/NodeDrawerOverview.tsx", + "frontend-modern/src/components/shared/MobileNavBar.tsx", + "frontend-modern/src/components/shared/mobileNavBarModel.ts", + "frontend-modern/src/components/shared/useMobileNavBarState.ts", + "frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx", + "frontend-modern/src/hooks/useUnifiedResources.ts", "frontend-modern/src/types/api.ts", - "frontend-modern/src/utils/alertOverviewPresentation.ts" + "frontend-modern/src/types/resource.ts", + "frontend-modern/src/utils/proxmoxUpdateEvidence.ts", + "frontend-modern/src/utils/resourceStateAdapters.ts" ], "content_sha256": { - "frontend-modern/src/features/alerts/AlertOverviewActiveAlertsSection.tsx": "396b3b8eb61f094bf2c57a46d45751fd349469c0d153074f1af9b01742dfab78", - "frontend-modern/src/features/alerts/useAlertOverviewState.ts": "1eea31a6a8c4a6e181fa478c7b578590d26e1c9219b8ed9c3cfc8b76ae9bb370", - "frontend-modern/src/i18n/messages.de.ts": "c5e5f16d3e7c6d7a741e84fc2217e08aa6b257c4a6a7fa0e9009c993534eb8e7", - "frontend-modern/src/i18n/messages.es.ts": "048749c66b51d3c063e94b4742dfb21b20de70d13ad09216ef7d73b4fd98c13a", - "frontend-modern/src/i18n/messages.ts": "b1af1ee4f56039e6263685df8d776f7e14e32fe8fb7d84cd2f03992c01d09c18", - "frontend-modern/src/types/api.ts": "6f118c6c43b4ccc5cb11c440267797bee0c36a8e5ffa95aa94112419d28d56fa", - "frontend-modern/src/utils/alertOverviewPresentation.ts": "00aab1c0a3f2bdb78c4efb6effdb5fa4a95560f0f73f9e5e7f084e4db98e2057" + "frontend-modern/src/AppLayout.tsx": "db685903fef2a1509edd812acb3904f7ff3ffab0bbd1af2bff183c1b3ae8e373", + "frontend-modern/src/components/Workloads/NodeDrawerOverview.tsx": "15aa9c5dedebb2024f2548854996034672b35412708f28c276bd108892056e64", + "frontend-modern/src/components/shared/MobileNavBar.tsx": "27358f3e77267cc111ce97b32d715b0069e2d1370ddd2b3ecc4f2d34a3e6dac5", + "frontend-modern/src/components/shared/mobileNavBarModel.ts": "ab9e69d379579d02e33a2abd1224d8c13e6543dd6aa0ad40cc1f65697a289d04", + "frontend-modern/src/components/shared/useMobileNavBarState.ts": "74b5b354d302d5e5ea136131f06b3a8c1772a83a9903351737b9931f8136cc16", + "frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx": "bbc7047317b350074e2f234b2472f3a1b39b080e4ea4fddff706a866952f473f", + "frontend-modern/src/hooks/useUnifiedResources.ts": "cbf1b5a2fa2bdb51d582f699fef97203bac2d2ab559f29ea40821e2eb7c11ee3", + "frontend-modern/src/types/api.ts": "d3dfc213bc7b7c357954723a22a21744e6be5da9f34d97a1faf00f579efcb5fc", + "frontend-modern/src/types/resource.ts": "126473e475c63c995aa86c578e9bc8f115eaca98da6477545e4389fe96b43f68", + "frontend-modern/src/utils/proxmoxUpdateEvidence.ts": "3881918d823b440cf8739e14da780c7e98e871e71b9be490ca61a7fbc342c29e", + "frontend-modern/src/utils/resourceStateAdapters.ts": "6f077a5611c8563be3674d9b8baf2500c711494bae14fda697f56b1e00c2342b" }, - "routes": ["/alerts/overview"], + "routes": [ + "/proxmox/overview?status=running", + "/alerts", + "/actions" + ], "viewports": [ { "width": 1440, @@ -33,14 +45,13 @@ } ], "states": [ - "mock-backed Alerts Overview with forty or more active detector signals", - "same-resource alert group collapsed with the localized related-signal disclosure", - "same-resource alert group expanded with its underlying alert card and group acknowledgement action", - "desktop and narrow layouts with unacknowledged critical and warning cards" + "mock-backed Proxmox overview with multiple platform destinations and a remembered status query", + "mobile fixed navigation rail with the platform menu open", + "current NodeDrawerOverview mounted with synthetic checked-zero, stale-positive, and permission-unavailable update evidence fixtures" ], "interactions": [ - "opened Alerts Overview at 1440x1000 and confirmed active cards, group disclosure, and acknowledgement controls are aligned and unclipped", - "expanded and collapsed a two-signal alert group and confirmed the nested alert remains visually subordinate while retaining its own controls", - "repeated the expanded and collapsed group at 390x844 and confirmed the document and body remain exactly 390 pixels wide with no horizontal overflow" + "inspected destination hrefs, opened the platform menu, closed it with Escape and verified focus returned to its trigger", + "used a modified Alerts click to open a native browser tab while retaining the original Proxmox route, then used a plain Actions click for in-app navigation", + "mounted the current NodeDrawerOverview component in the Vite browser, checked all three evidence states at desktop width, then narrowed to 390 pixels and verified no horizontal overflow" ] }