From 540e59cb4a6c77d93a2b6efcb1d2168cac027c5b Mon Sep 17 00:00:00 2001 From: rcourtman Date: Tue, 14 Apr 2026 23:58:04 +0100 Subject: [PATCH] narrow patrol to verification-first flow --- .../v6/internal/subsystems/api-contracts.md | 8 ++- .../subsystems/frontend-primitives.md | 28 ++++++++ .../subsystems/patrol-intelligence.md | 29 ++++---- .../AI/__tests__/FindingsPanel.test.ts | 8 +-- .../src/components/patrol/PatrolStatusBar.tsx | 4 +- .../__tests__/ActionRequiredPanel.test.tsx | 4 +- .../patrol/PatrolIntelligenceBanners.tsx | 2 +- .../patrol/PatrolIntelligenceHeader.tsx | 2 +- .../patrol/PatrolIntelligenceSummary.tsx | 6 +- .../patrol/PatrolIntelligenceWorkspace.tsx | 15 ++-- .../patrolInvestigationContextModel.test.ts | 2 +- .../patrol/patrolInvestigationContextModel.ts | 2 +- .../patrol/usePatrolIntelligenceState.ts | 21 ++++++ .../pages/__tests__/AIIntelligence.test.tsx | 69 ++++++------------- .../patrolSummaryPresentation.test.ts | 4 +- .../src/utils/aiFindingPresentation.ts | 2 +- .../src/utils/patrolSummaryPresentation.ts | 2 +- 17 files changed, 120 insertions(+), 88 deletions(-) diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index fad68db3f..c650ffa2d 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -180,11 +180,12 @@ when the disabled candidate no longer counts toward monitored-system capacity. and the learned-correlation payload loaded into the shared AI intelligence store, so the Patrol intelligence page and the AI summary page consume the same governed correlation slice instead of each page fetching its own copy and the shared dashboard-load bundle inside `frontend-modern/src/stores/aiIntelligence.ts`, so the page orchestration stays on the store-owned bundle instead of enumerating the AI fetches inline and the shared `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` card, so the AI summary page renders the governed policy-posture counts while the resource drawer stays on per-resource policy lines instead of carrying duplicate posture UI loops - and the dedicated `frontend-modern/src/features/patrol/patrolInvestigationContextModel.ts` owner, so recent-change, learned-correlation, and governed-resource summary text stays derived from the canonical AI payload in one place instead of as hook-local count and pluralization logic + and the dedicated `frontend-modern/src/features/patrol/patrolInvestigationContextModel.ts` owner, so recent-change, learned-correlation, and policy-coverage summary text stays derived from the canonical AI payload in one place instead of as hook-local count and pluralization logic and the dedicated `frontend-modern/src/stores/aiIntelligenceSummaryModel.ts` owner, so recent-change counts and governed policy-posture fallbacks normalize once at the shared store boundary instead of as Patrol-hook-local payload repair and the shared `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx` card, so learned correlations and correlation context stay rendered through one governed frontend card instead of separate page-local list loops and the same shared correlation card's ordering and truncation rule, so callers pass raw correlations instead of encoding their own top-N sort behavior and the shared `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx` and `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx` cards' infrastructure resource-link default, so the Patrol page, resource drawer, and problem-resource dashboard panels inherit the canonical resource-filter path construction instead of rebuilding infrastructure URLs inline + and the Patrol runtime-remediation destination shared with `/api/settings/ai`, so summary actions and runtime-finding actions may reuse the governed provider-settings route while still presenting that destination to Patrol operators as Patrol provider configuration instead of generic `AI Settings` copy 8. Route frontend API-client parsed error propagation, API-error-status fallback handling, allowed-status handling, custom status-specific error handling, command-trigger success envelope handling, shared response parsing pipelines, missing-resource lookup handling, metadata CRUD routing, stream event consumption, response status, collection normalization, scalar payload coercion, and structured error normalization through canonical shared helpers under `frontend-modern/src/api/` 9. Add or change API token scope, assignment, and revocation presentation through `frontend-modern/src/components/Settings/APITokenManager.tsx`, `frontend-modern/src/components/Settings/apiTokenManagerModel.ts`, and `frontend-modern/src/components/Settings/useAPITokenManagerState.ts` 10. Add or change infrastructure operations token generation, lookup, assignment, the pure unified-agent inventory/install model, the split infrastructure install/reporting state owners, the split direct-node/discovery infrastructure settings owners, the shared infrastructure-operations state provider/context shell, and reporting/install presentation through `frontend-modern/src/components/Settings/InfrastructureOperationsController.tsx`, `frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx`, `frontend-modern/src/components/Settings/useInfrastructureConfiguredNodesState.ts`, `frontend-modern/src/components/Settings/useInfrastructureDiscoveryRuntimeState.ts`, `frontend-modern/src/components/Settings/useInfrastructureInstallState.tsx`, `frontend-modern/src/components/Settings/useInfrastructureOperationsState.tsx`, and `frontend-modern/src/components/Settings/useInfrastructureReportingState.tsx` @@ -371,6 +372,11 @@ when the disabled candidate no longer counts toward monitored-system capacity. and the main Patrol page composition boundary, so once that governed secondary area exists inside the summary shell the same payloads must not also drive a second page-level status strip elsewhere on the route + and the Patrol supporting-context disclosure rule, so recent changes, + learned correlations, and policy coverage stay secondary explanatory context + that opens only when degraded verification, active findings, or selected-run + investigation makes that evidence relevant instead of advertising a parallel + Patrol workflow on otherwise healthy fully verified states 9. Keep AI settings setup transport vendor-neutral: `/api/settings/ai/update` must accept provider credentials or base URLs without a baked vendor model ID, resolve the effective BYOK `model` through the canonical runtime diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 4d104b11f..a2c9d2c0d 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -280,6 +280,22 @@ work extends shared components instead of creating new local variants. explicit disabled/null inputs to shared data owners when the selector is hidden instead of hydrating background summary data for chrome the page is not rendering. +18. Keep Patrol shell composition and product-first provider vocabulary on the + shared feature-presentation boundary. + `frontend-modern/src/features/patrol/PatrolIntelligenceSummary.tsx`, + `frontend-modern/src/features/patrol/PatrolIntelligenceWorkspace.tsx`, + `frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx`, + `frontend-modern/src/features/patrol/PatrolIntelligenceBanners.tsx`, + `frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts`, + `frontend-modern/src/features/patrol/patrolInvestigationContextModel.ts`, + and `frontend-modern/src/components/patrol/PatrolStatusBar.tsx` must keep + Patrol assessment, verification, and findings primary; surface recent + changes, learned correlations, and policy coverage only as explicitly + secondary supporting context when degraded or incomplete verification, + active findings, or selected-run investigation makes that evidence + relevant; and use Patrol/provider wording for the shared provider settings, + provider model, and provider circuit-breaker affordances instead of + generic AI labels inside Patrol-owned shells. ## Forbidden Paths @@ -1560,6 +1576,12 @@ should present alert-triggered and anomaly-triggered Patrol toggles as distinct controls, and `frontend-modern/src/components/patrol/PatrolStatusBar.tsx` should render compact activity breakdown and scoped-trigger-state copy from the shared transport rather than leaving busy Patrol periods as unexplained noise. +That same Patrol-facing primitive vocabulary must stay product-first. Patrol +summary actions, runtime banners, circuit-breaker copy, and Patrol +configuration controls may point at the shared provider settings route or model +catalog, but they should describe those controls as Patrol/provider surfaces +rather than falling back to generic `AI Settings`, `AI Model`, or `AI circuit +breaker` copy inside the Patrol shell itself. On the main Patrol page, though, that same governed activity context belongs inside `frontend-modern/src/features/patrol/PatrolIntelligenceSummary.tsx` alongside the verification readout rather than as a second full-width band @@ -1570,6 +1592,12 @@ That same composition rule applies to `frontend-modern/src/features/patrol/Patro once the summary shell carries the operator-facing verification and activity story, the workspace should move directly into findings and run history instead of repeating that same runtime context through a second pre-tab status strip. +Supporting context follows that same composition rule. Recent changes, learned +correlations, and policy coverage belong behind an explicitly secondary +supporting-context disclosure that only appears when Patrol has active +findings, degraded or incomplete verification, or a selected run that needs +explanation; healthy fully verified Patrol states must not advertise that +supporting evidence as a peer workflow. Shared primitive consumers that split status-dot tone and status-text tone must now keep both values routed through the same exported presentation helper. diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index 1b0a35ea2..feaabc07c 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -221,9 +221,9 @@ assessment copy should name the concrete runtime failure, such as runtime findings. That same runtime-owned assessment must expose the fix path directly. When the primary Patrol issue is a Patrol runtime/provider problem rather than an -infrastructure finding, the summary card should offer a direct `Open AI -Settings` action instead of making the operator dig through the findings list -to discover where to correct provider configuration. +infrastructure finding, the summary card should offer a direct `Open Patrol +provider settings` action instead of making the operator dig through the +findings list to discover where to correct provider configuration. That same runtime-versus-infrastructure distinction should route through the shared finding-presentation helper instead of being re-inferred separately by the summary card and the findings list. The active finding row should surface @@ -243,14 +243,14 @@ rather than repeating the product prefix as `Pulse Patrol: Insufficient API credits` once the surrounding UI already makes the Patrol context explicit. That same finding presentation contract should own the primary remediation path for Patrol-owned runtime findings as well. Expanded runtime-finding rows should -offer the same direct `Open AI Settings` action that the top assessment uses, -instead of falling back to only generic acknowledge, snooze, or dismiss -controls. +offer the same direct `Open Patrol provider settings` action that the top +assessment uses, instead of falling back to only generic acknowledge, snooze, +or dismiss controls. That same contract must fail closed on manual lifecycle controls too. Patrol runtime findings are Patrol-owned impairment signals, not ordinary estate findings, so the findings list must not offer generic acknowledge, snooze, dismiss, resolve, or suppress controls for them. The correct operator path is -to fix AI/provider configuration and rerun Patrol, optionally adding context +to fix Patrol provider configuration and rerun Patrol, optionally adding context notes, rather than hiding the runtime issue. That same runtime-versus-infrastructure split must carry through the summary metrics strip as well. When Patrol-owned runtime issues are active, the @@ -306,10 +306,13 @@ recent activity mix explicitly instead of leaving operators to reconcile a `Recently verified` headline with a busy Patrol strip elsewhere on the page. Fix-verification checks belong to that same explanation layer as targeted activity, not as evidence of a fresh full-estate sweep. -The same hierarchy applies to investigation context. Correlations, recent +The same hierarchy applies to supporting context. Correlations, recent changes, and policy posture are secondary evidence for deeper investigation, so -the `Investigation context` section belongs beneath the primary findings/history +the supporting-context disclosure belongs beneath the primary findings/history workspace rather than inside the assessment card itself. +When Patrol is healthy and fully verified, that supporting-context disclosure +should stay out of the main page flow instead of advertising a second parallel +Patrol workflow with nothing active to explain. That same operational context belongs inside the same secondary status area as verification, not as a separate full-width strip that competes with the findings workspace. `frontend-modern/src/features/patrol/PatrolIntelligenceSummary.tsx` @@ -572,10 +575,10 @@ card, so the data-governance posture counts stay rendered from one governed frontend component on the page instead of being duplicated in the resource drawer. That same Patrol summary surface now keeps health and findings primary while -rendering recent changes, learned correlations, and policy posture as -secondary investigation context behind an explicit disclosure, so expansion -lane concepts stay available for deeper investigation without reading as the -headline Patrol product story. +rendering recent changes, learned correlations, and policy posture only as +supporting context behind an explicit disclosure, so expansion lane concepts +stay available for deeper investigation without reading as the headline Patrol +product story. That secondary investigation-context summary now also routes through the dedicated `frontend-modern/src/features/patrol/patrolInvestigationContextModel.ts` owner, so the Patrol hook composes one canonical payload-to-summary derivation diff --git a/frontend-modern/src/components/AI/__tests__/FindingsPanel.test.ts b/frontend-modern/src/components/AI/__tests__/FindingsPanel.test.ts index e4d6c0561..9bb385e64 100644 --- a/frontend-modern/src/components/AI/__tests__/FindingsPanel.test.ts +++ b/frontend-modern/src/components/AI/__tests__/FindingsPanel.test.ts @@ -308,7 +308,7 @@ describe('aiFindingPresentation', () => { }); describe('findingPrimaryActionPresentation', () => { - it('offers AI settings as the primary action for Patrol runtime findings', () => { + it('offers Patrol provider settings as the primary action for Patrol runtime findings', () => { expect( getFindingPrimaryActionPresentation({ resourceId: 'ai-service', @@ -316,12 +316,12 @@ describe('aiFindingPresentation', () => { title: 'Pulse Patrol: Insufficient API credits', }), ).toEqual({ - label: 'Open AI Settings', + label: 'Open Patrol provider settings', href: '/settings/system-ai', }); }); - it('does not expose AI settings as the primary action for infrastructure findings', () => { + it('does not expose Patrol provider settings as the primary action for infrastructure findings', () => { expect( getFindingPrimaryActionPresentation({ resourceId: 'vm-101', @@ -339,7 +339,7 @@ describe('aiFindingPresentation', () => { title: 'Insufficient API credits', }), ).toEqual({ - label: 'Open AI Settings', + label: 'Open Patrol provider settings', href: '/settings/system-ai', }); }); diff --git a/frontend-modern/src/components/patrol/PatrolStatusBar.tsx b/frontend-modern/src/components/patrol/PatrolStatusBar.tsx index b9b9d99d5..5d31484a4 100644 --- a/frontend-modern/src/components/patrol/PatrolStatusBar.tsx +++ b/frontend-modern/src/components/patrol/PatrolStatusBar.tsx @@ -108,7 +108,7 @@ export const PatrolStatusBar: Component = (props) => {
- AI circuit breaker tripped — Patrol paused after{' '} + Provider circuit breaker tripped — Patrol paused after{' '} {circuitBreaker()!.consecutive_failures} consecutive failures
@@ -117,7 +117,7 @@ export const PatrolStatusBar: Component = (props) => {
- AI circuit breaker recovering — testing with next patrol run + Provider circuit breaker recovering — testing with next patrol run
diff --git a/frontend-modern/src/features/dashboardOverview/__tests__/ActionRequiredPanel.test.tsx b/frontend-modern/src/features/dashboardOverview/__tests__/ActionRequiredPanel.test.tsx index b0d438c80..ffd02ae68 100644 --- a/frontend-modern/src/features/dashboardOverview/__tests__/ActionRequiredPanel.test.tsx +++ b/frontend-modern/src/features/dashboardOverview/__tests__/ActionRequiredPanel.test.tsx @@ -62,7 +62,7 @@ describe('ActionRequiredPanel', () => { expect(actionRequiredPanelSource).toContain('{title}'); }); - it('uses Open AI Settings instead of rejected generic controls for Patrol runtime findings', () => { + it('uses the Patrol provider settings action instead of rejected generic controls for Patrol runtime findings', () => { expect( getFindingTitlePresentation({ resourceId: 'ai-service', @@ -79,7 +79,7 @@ describe('ActionRequiredPanel', () => { title: 'Pulse Patrol: Insufficient API credits', }), ).toEqual({ - label: 'Open AI Settings', + label: 'Open Patrol provider settings', href: '/settings/system-ai', }); expect( diff --git a/frontend-modern/src/features/patrol/PatrolIntelligenceBanners.tsx b/frontend-modern/src/features/patrol/PatrolIntelligenceBanners.tsx index 66ed5011f..8d802656d 100644 --- a/frontend-modern/src/features/patrol/PatrolIntelligenceBanners.tsx +++ b/frontend-modern/src/features/patrol/PatrolIntelligenceBanners.tsx @@ -87,7 +87,7 @@ export function PatrolIntelligenceBanners(props: { state: PatrolIntelligenceStat class="inline-flex items-center justify-center gap-2 px-3 py-1.5 text-xs font-semibold text-amber-900 dark:text-amber-100 bg-amber-100 dark:bg-amber-900 border border-amber-200 dark:border-amber-700 rounded-md hover:bg-amber-200 dark:hover:bg-amber-900 transition-colors" > - Open AI Settings + Open Patrol provider settings