From 6d8dce2caeeab1ecb1e49b025240e6f7af368abc Mon Sep 17 00:00:00 2001 From: rcourtman <8825017+rcourtman@users.noreply.github.com> Date: Wed, 7 Oct 2026 16:35:14 +0100 Subject: [PATCH] Remove unread Patrol summaryStats and its audit rule usePatrolIntelligenceState() still returned summaryStats, a closure that counted critical, warning and active Patrol findings plus fixed outcomes. Its only reader was PatrolIntelligenceSummary.tsx, deleted in 27ba85ddc6 (2026-06-23). A type-checker scan of the whole program (src and tests) finds no reference to the member outside the hook's return object, and nothing spreads, iterates or index-reads the returned state, so the closure and its return entry go. The canonical-patrol/no-local-summary-card-presentation rule in canonical-platform-audit.mjs matched only text shaped like that card's tint ternaries on summaryStats().criticalFindings, warningFindings and fixedCount, and told authors to use patrolSummaryPresentation, which no longer holds any summary-card presentation. With the accessor gone the rule guards a name nothing can call, so it goes, along with the three not.toContain guards in frontendResourceTypeBoundaries.test.ts that pinned the same strings. patrolSummaryPresentation.ts also leaves the audit allowlist: its remaining recency and verification presenters trip no rule, and without the entry the audit scans the module like any other (a planted "Loading messages..." string is now reported). The JavaScript output changes only by the removed code. Of 187 dist files, 94 are byte-identical and 92 differ only in chunk-hash references and SRI strings. In the AIIntelligence chunk, after normalising hashes and short identifiers, the only residual is the summaryStats closure and its entry in the returned state. The bundle-size check passes. The Patrol Inbox, Activity and Protection tabs render without console errors at desktop and phone widths. The contracts now record that the hook derives no client-side severity tally, that the page's open-issue severity counts come from the This week card's digest read, and that the audit carries no Patrol summary-card rule. The patrol-intelligence section-owner sentence named a summary surface that no longer exists; it now names the live tab panels. Contract-Neutral: Only residual guard demand is the platform support manifest frontend projection proof that the canonical-platform-audit.mjs path policy names (resource.test.ts, sourcePlatformOptions.test.ts, sourcePlatforms.test.ts). This commit deletes a Patrol summary-card rule and one allowlist entry and leaves the projection sync check untouched, so editing those tests would be fabricated proof. patrol-intelligence, api-contracts and frontend-primitives carry substantive notes, and frontendResourceTypeBoundaries.test.ts is the staged patrol page-and-state proof. --- .../v6/internal/subsystems/api-contracts.md | 1 + .../subsystems/frontend-primitives.md | 10 +++++ .../subsystems/patrol-intelligence.md | 26 +++++++++--- .../f808a2b14f5d24e7.json | 41 +++++++++++++++++++ .../scripts/canonical-platform-audit.mjs | 8 ---- .../patrol/usePatrolIntelligenceState.ts | 19 --------- .../frontendResourceTypeBoundaries.test.ts | 9 ---- 7 files changed, 72 insertions(+), 42 deletions(-) create mode 100644 frontend-modern/browser-verification/f808a2b14f5d24e7.json 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'", );