diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index f6943a66d2..642c0cce28 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -3325,6 +3325,7 @@ a new API state machine, queue contract, or verification-accounting field. action artifact so the Assistant runtime can refresh finding and action posture from IDs and safe summaries instead of relying on pasted chat text 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 Patrol findings list the state hook reads from `aiIntelligenceStore.patrolFindings`, so `usePatrolIntelligenceState.ts` does not re-derive page-level severity tallies from it: the hook's unread `summaryStats` tally has been removed, the hook derives no client-side severity tally from that list today (the backend's per-severity `findings_count` reaches it only inside the AI summary payload it passes through), and the Activity tab's `This week` card takes its open-by-severity counts from `GET /api/ai/patrol/digest` instead of recounting that list 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' optional `buildResourceHref` input, so a surface that needs resource-filter links passes them into the shared card instead of rebuilding infrastructure URLs inline; the cards carry no link default since the cross-resource drilldown links were retired, and render resource labels as plain text diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 7707b99374..0f17f03c2b 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -4901,6 +4901,16 @@ or not fully verified. The same empty-state helper must consume Patrol trust-history evidence so a historical regression reads as history review context, not as a current issue and not as a healthy all-clear. +`frontend-modern/scripts/canonical-platform-audit.mjs` carries no Patrol +summary-card rule. Its `canonical-patrol/no-local-summary-card-presentation` +regex matched only text shaped like the deleted summary card's tint ternaries +on `summaryStats().criticalFindings`, `warningFindings` and `fixedCount`. The +Patrol state hook's `summaryStats` accessor has had no reader since that card +was deleted, so the accessor and the rule are both gone. +`frontend-modern/src/utils/patrolSummaryPresentation.ts` is off the audit +allowlist too: it now holds only the recency and verification presenters, +which no audit rule needs to exempt, so the audit scans it like any other +module. The Patrol page renders no summary shell today. If one returns, the same hierarchy applies inside it: once its primary assessment strip states Patrol's current risk and verification basis, diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index 92f205d675..2b4ede7b5d 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -1508,15 +1508,29 @@ provider/settings affordances. The feature surface now also keeps the same shell/runtime split internally: `frontend-modern/src/features/patrol/PatrolIntelligenceSurface.tsx` owns feature composition, the Patrol-owned section files under -`frontend-modern/src/features/patrol/` own the header, banner, summary, and -workspace render surfaces, and -`frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts` owns Patrol -state, transport, polling, and effect lifecycle. The shell and section surfaces -must not re-accumulate Patrol API calls, timer orchestration, or store refresh -semantics, and `frontend-modern/src/stores/aiIntelligenceSummaryModel.ts` now +`frontend-modern/src/features/patrol/` own the header, banner, workspace, and +tab-panel render surfaces (the Inbox attention list, Protection objectives, and +the Activity tab's `This week` digest and recent work), and +`frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts` owns the +page's shared Patrol state, transport, polling, and effect lifecycle. The shell +and the header, banner, and workspace surfaces must not re-accumulate those +Patrol API calls, timer orchestration, or store refresh semantics; the +attention list, objectives, recent work, and `This week` panels each own their +own typed read and refresh instead, and `frontend-modern/src/stores/aiIntelligenceSummaryModel.ts` now owns the canonical summary normalization so Patrol consumers inherit one governed recent-change and policy-posture snapshot instead of reintroducing hook-local fallback logic. +The hook no longer returns the summary card's severity and fix tallies. Its +`summaryStats` accessor (critical, warning, and total active findings plus +fixed outcomes, counted client-side over the Patrol findings list) lost its +only reader when the Patrol summary card was deleted and has been removed, +together with the `canonical-patrol/no-local-summary-card-presentation` rule in +`frontend-modern/scripts/canonical-platform-audit.mjs`, whose regex matched only +text shaped like the deleted card's tint ternaries on +`summaryStats().criticalFindings`, `warningFindings` and `fixedCount`. The open-issue severity counts the page shows today come from +the Activity tab's `This week` card, which reads them from +`GET /api/ai/patrol/digest` (see that card's section below); a count surface +that returns must not revive a hook-local tally of the findings list. That same Patrol hook boundary now consumes shared AI settings/model truth through `frontend-modern/src/stores/aiRuntimeState.ts` instead of mounting its own `/api/settings/ai` or `/api/ai/models` reads. Patrol-specific state still diff --git a/frontend-modern/browser-verification/f808a2b14f5d24e7.json b/frontend-modern/browser-verification/f808a2b14f5d24e7.json new file mode 100644 index 0000000000..f825bc20b6 --- /dev/null +++ b/frontend-modern/browser-verification/f808a2b14f5d24e7.json @@ -0,0 +1,41 @@ +{ + "version": 1, + "base_sha": "421d1e5423e5b83b7923ea8c3b133ad520cd5f60", + "verified_at": "2026-10-07T15:23:47Z", + "result": "passed", + "changed_paths": [ + "frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts" + ], + "content_sha256": { + "frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts": "0fef3795b2a6e2bcb1a979583deceeabd8536868adcb08eb383425dd97bb85ad" + }, + "routes": [ + "/patrol", + "/patrol (Activity tab)", + "/patrol (Protection tab)", + "/patrol?attention=" + ], + "viewports": [ + { + "width": 1440, + "height": 900 + }, + { + "width": 390, + "height": 844 + } + ], + "states": [ + "Embedded production build of this tree (vite build into internal/api/frontend-modern/dist, go build) on a private DEMO_MODE mock stack (port 7693, fresh data dir, telemetry off).", + "Inbox: tablist Inbox/Protection/Activity with Inbox selected; '50 decisions need you' attention list with 5 rows (for example 'Open clickhouse-2 · Docker container memory limit'); header recency line 'Last check: just now · checked 1377 resources'.", + "Activity: 'This week' digest card ('Patrol runs 11', 'New issues 6', '6 still open (2 critical, 4 warning)') above Recent work; Protection panel renders.", + "No console errors and no page errors at either width.", + "Dist proof against the parent tree (built at 333e51d2de, whose frontend-modern tree is identical to the parent 421d1e5423): 187 files each side; 94 byte-identical, 92 differ only in chunk-hash references and SRI strings; only assets/AIIntelligence-*.js differs in code, and after normalising hashes and 1-3 character identifiers its only residual is the removed summaryStats closure and its summaryStats entry in the returned state object. check-bundle-size.mjs passes." + ], + "interactions": [ + "Demo auto sign-in, navigate to /patrol, read the header and Inbox attention rows.", + "Click the first attention row: URL gains ?attention= and the detail panel opens; Escape, reload /patrol.", + "Click Activity, Protection, then Inbox tabs; Inbox re-selects.", + "Same script in a 1440x900 desktop context and a 390x844 phone context; screenshots of Inbox, opened row and Activity inspected at both widths." + ] +} diff --git a/frontend-modern/scripts/canonical-platform-audit.mjs b/frontend-modern/scripts/canonical-platform-audit.mjs index 1a4eb28ebd..8ec3b24526 100644 --- a/frontend-modern/scripts/canonical-platform-audit.mjs +++ b/frontend-modern/scripts/canonical-platform-audit.mjs @@ -130,7 +130,6 @@ const ALLOWLIST = new Set([ 'src/utils/remediationPresentation.ts', 'src/utils/patrolEmptyStatePresentation.ts', 'src/utils/patrolRunPresentation.ts', - 'src/utils/patrolSummaryPresentation.ts', 'src/utils/rbacPresentation.ts', 'src/utils/rbacPermissions.ts', 'src/utils/systemLogsPresentation.ts', @@ -581,13 +580,6 @@ const HELPER_RULES = [ message: 'Do not define local AI quickstart credits badge presentation in page code. Use @/utils/aiQuickstartPresentation instead.', }, - { - rule: 'canonical-patrol/no-local-summary-card-presentation', - regex: - /\bsummaryStats\(\)\.criticalFindings\s*>\s*0\b[\s\S]{0,320}bg-red-50[\s\S]{0,320}text-red-600|\bsummaryStats\(\)\.warningFindings\s*>\s*0\b[\s\S]{0,320}bg-amber-50[\s\S]{0,320}text-amber-600|\bsummaryStats\(\)\.fixedCount\s*>\s*0\b[\s\S]{0,320}bg-green-50[\s\S]{0,320}text-green-600/g, - message: - 'Do not define local patrol summary card presentation in page code. Use @/utils/patrolSummaryPresentation instead.', - }, { rule: 'canonical-patrol/no-local-empty-state-copy', regex: diff --git a/frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts b/frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts index d2eb5d4dd6..d28c5edc99 100644 --- a/frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts +++ b/frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts @@ -916,24 +916,6 @@ export function usePatrolIntelligenceState() { window.history.replaceState(window.history.state, '', nextPath); } - const summaryStats = () => { - const patrolFindings = allPatrolFindings(); - const activeFindings = patrolFindings.filter((finding) => finding.status === 'active'); - - return { - criticalFindings: activeFindings.filter((finding) => finding.severity === 'critical').length, - warningFindings: activeFindings.filter((finding) => finding.severity === 'warning').length, - totalActive: activeFindings.length, - fixedCount: patrolFindings.filter( - (finding) => - finding.investigationOutcome === 'fix_verified' || - finding.investigationOutcome === 'fix_executed' || - finding.investigationOutcome === 'resolved', - ).length, - hasAnyPatrolFindings: patrolFindings.length > 0, - }; - }; - const activePatrolFindings = () => allPatrolFindings().filter((finding) => finding.status === 'active'); const shouldShowPatrolSetupOnly = createMemo(() => { @@ -1134,7 +1116,6 @@ export function usePatrolIntelligenceState() { showBlockedBanner, showReadinessBanner, shouldShowPatrolSetupOnly, - summaryStats, triggerPatrolDisabledReason, }; } diff --git a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts index f7d939d3cb..fb348d44de 100644 --- a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts +++ b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts @@ -3649,15 +3649,6 @@ describe('frontend resource type boundaries', () => { 'historicalRegressionCount={state.historicalRegressionCount()}', ); expect(patrolIntelligenceBannersSource).not.toContain('trackUpgradeClicked'); - expect(patrolIntelligenceSurfaceSource).not.toContain( - "summaryStats().criticalFindings > 0\n ? 'bg-red-50 dark:bg-red-900/25 border-red-200 dark:border-red-800'", - ); - expect(patrolIntelligenceSurfaceSource).not.toContain( - "summaryStats().warningFindings > 0\n ? 'bg-amber-50 dark:bg-amber-900/25 border-amber-200 dark:border-amber-800'", - ); - expect(patrolIntelligenceSurfaceSource).not.toContain( - "summaryStats().fixedCount > 0\n ? 'bg-green-50 dark:bg-green-900/25 border-green-200 dark:border-green-800'", - ); expect(patrolIntelligenceSurfaceSource).not.toContain( "(patrolStatus()?.quickstart_credits_remaining ?? 0) > 0\n ? 'bg-blue-50 dark:bg-blue-950/25 border-blue-200 dark:border-blue-800 text-blue-700 dark:text-blue-300'", );