diff --git a/docs/release-control/v6/internal/subsystems/ai-runtime.md b/docs/release-control/v6/internal/subsystems/ai-runtime.md index ca23f1367c..f20400b416 100644 --- a/docs/release-control/v6/internal/subsystems/ai-runtime.md +++ b/docs/release-control/v6/internal/subsystems/ai-runtime.md @@ -6857,8 +6857,8 @@ That posture snapshot must render redaction labels through the canonical unified-resource hint order, not alphabetically, so the AI summary, drawer, and any future policy surfaces all present the same redaction precedence. Its sensitivity and routing counts must also follow the canonical -unified-resource order and shared human-readable count summaries, so both the -backend summary and the frontend policy card stay aligned on the same +unified-resource order and shared human-readable count summaries, so the +backend summary and any frontend posture presentation stay aligned on the same presentation sequence. The unified AI resource data-governance block must also use the shared unified-resource redaction-label helper directly, so the same canonical diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index ece6cf813f..978b841439 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -3307,7 +3307,7 @@ a new API state machine, queue contract, or verification-accounting field. 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 Patrol page refresh lifecycle in `frontend-modern/src/features/patrol/usePatrolIntelligenceState.ts`, so slow or stalled secondary reads from that shared dashboard-load bundle may continue resolving in the background while the operator-facing Patrol refresh control remains generation-aware, timeout-bounded, and reusable once Patrol findings and status are already visible and the Patrol header support drawer in `frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx`, so API-owned Patrol status and trigger facts can feed a secondary Schedule & model surface without turning provider model, schedule, trigger tuning, or background-only runtime-policy pauses into the primary Patrol control decision - 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 any frontend presentation of the AI summary's `policy_posture` counts, which must render that canonical snapshot through one shared component instead of page-local posture loops; no frontend surface renders those counts today, and the resource drawer stays on per-resource policy lines 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 Watch-only forward-path handoffs owned by `frontend-modern/src/features/patrol/patrolControlPresentation.ts` together with the Actions inbox empty-state read of `GET /api/ai/patrol/autonomy`, so finding-level and inbox-level mode guidance consumes the canonical Patrol autonomy read/save contract through the Patrol state hook (`handleAutonomyChange`) instead of introducing a second mode mutation path or a page-local autonomy dialect and that same Patrol investigation-context owner, so the current Patrol @@ -7326,11 +7326,12 @@ transport or settings failures as stale-data state rather than throwing through the route: the page stays mounted, preserves any last-known Patrol evidence, and exposes a retry affordance while backend/API failures remain available to debug logging and API-level diagnostics. -The AI summary page now also renders the canonical -`frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` -card for policy posture, so sensitivity, routing, and redaction counts are -presented through one governed frontend component while the resource drawer -keeps only the per-resource policy lines. +No frontend surface currently renders the AI summary's `policy_posture` +counts; the shared posture card the Patrol page once mounted has been removed. +A surface that shows those counts again must present sensitivity, routing, +and redaction counts through one governed shared component fed by that +canonical payload, while the resource drawer keeps only the per-resource +policy lines. The unified action, lifecycle, and export audit reads now also clamp oversized `limit` requests to the governed maximum of `1000`, so the control-plane audit surface stays bounded even when callers ask for arbitrarily large history diff --git a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md index e9ec22a889..a35d6021e9 100644 --- a/docs/release-control/v6/internal/subsystems/patrol-intelligence.md +++ b/docs/release-control/v6/internal/subsystems/patrol-intelligence.md @@ -2123,10 +2123,10 @@ That same summary card now keeps recent changes and learned correlations primary while leaving the broader learning counters as backend coverage, so the page does not present telemetry-style counts as a headline intelligence story. -That Patrol summary card now also includes the canonical data-governance -posture snapshot from the shared AI summary payload, so the visible page can -show the same sensitivity, routing, and redaction distribution that the -runtime derives from unified resources. +The shared AI summary payload also carries the canonical data-governance +posture snapshot that the runtime derives from unified resources, but the +Patrol page does not render its sensitivity, routing, and redaction +distribution; the posture rule below governs any return of those counts. The resource drawer now carries canonical dependency and dependent correlation context plus canonical correlation evidence through the resource-intelligence payload, so the resource-level AI card can surface @@ -2198,13 +2198,14 @@ cards now own the canonical Infrastructure resource-link default, so the Patrol page and resource drawer inherit resource-filter href construction through the shared summary cards instead of rebuilding local wrappers in each surface. -The Patrol intelligence page now also renders the canonical -`frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` -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. +The Patrol intelligence page renders no data-governance posture counts: they +left the page with its supporting-context block, and the shared posture card +that rendered them has been removed. If Patrol shows posture counts again, it +must render them through one shared unified-resource component instead of a +Patrol-local count loop, and the resource drawer keeps only per-resource +policy lines. That same Patrol summary surface now keeps health and findings primary while -making recent changes, learned correlations, and policy posture available only +making recent changes and learned correlations available only through the on-demand `Details` context inspector, so expansion lane concepts stay available for deeper investigation without reading as the headline Patrol product story. diff --git a/docs/release-control/v6/internal/subsystems/registry.json b/docs/release-control/v6/internal/subsystems/registry.json index fe0f7b00c1..cb267e319a 100644 --- a/docs/release-control/v6/internal/subsystems/registry.json +++ b/docs/release-control/v6/internal/subsystems/registry.json @@ -8006,7 +8006,6 @@ "frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts", "frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx", "frontend-modern/src/components/Infrastructure/ResourceOperatorStateSection.tsx", - "frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts", @@ -8204,7 +8203,6 @@ "frontend-modern/src/components/Infrastructure/resourceDetailMappers.ts", "frontend-modern/src/components/Infrastructure/ResourceFacetSummary.tsx", "frontend-modern/src/components/Infrastructure/ResourceOperatorStateSection.tsx", - "frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDockerActionsState.ts", "frontend-modern/src/components/Infrastructure/useResourceDetailDrawerHistoryState.ts", @@ -8298,7 +8296,6 @@ "frontend-modern/src/components/Infrastructure/__tests__/resourceDetailMappers.test.ts", "frontend-modern/src/components/Infrastructure/__tests__/ResourceFacetSummary.test.tsx", "frontend-modern/src/components/Infrastructure/__tests__/ResourceOperatorStateSection.test.ts", - "frontend-modern/src/components/Infrastructure/__tests__/ResourcePolicySummary.test.tsx", "frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts", "frontend-modern/src/features/docker/__tests__/DockerAlertsTable.test.tsx", "frontend-modern/src/features/docker/__tests__/dockerContainerLifecycleActions.test.ts", diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 1400185281..f953b60411 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -833,7 +833,6 @@ Compact Coverage age cells use the existing metadata-size text and reclaim numer 48. `frontend-modern/src/components/Infrastructure/ResourceChangeSummary.tsx` 49. `frontend-modern/src/components/Infrastructure/ResourceCorrelationSummary.tsx` 50. `frontend-modern/src/components/Infrastructure/ResourceOperatorStateSection.tsx` - 50a. `frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` 57. `frontend-modern/src/components/Infrastructure/useResourceDetailDrawerDerivedState.ts` 58. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerServiceModel.ts` 59. `frontend-modern/src/components/Infrastructure/resourceDetailDrawerVmwareModel.ts` @@ -2372,12 +2371,13 @@ provider identity or governed safe-summary posture when that context helps an operator, but the rendered labels must stay product-neutral and use `Analysis`, `Analysis Reasoning`, and `Safe Summary` rather than reviving generic `AI` or `AI-Safe` branding inside the resource drawer or discovery -shell. 15. Keep shared policy-posture framing on the unified-resource card owner. -`frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` -may accept caller-owned subtitle or resource-count wording when Patrol or -another shared surface needs to explain how the same governed policy counts -should be read, but those framing lines must extend the shared card API -rather than spawning page-local policy summary shells. 16. Keep platform/runtime top-level route paths on the canonical resource-link +shell. 15. Keep governed policy-posture wording on the unified-resource +presentation owner. `frontend-modern/src/utils/resourcePolicyPresentation.ts` +owns the canonical sensitivity, routing, and redaction labels and their +orderings. No shared card renders policy-posture counts today; if one +returns, caller-owned framing such as subtitle or resource-count wording must +extend that one card's API rather than spawning page-local policy summary +shells. 16. Keep platform/runtime top-level route paths on the canonical resource-link helper. `frontend-modern/src/routing/resourceLinks.ts` owns the `STANDALONE_PATH`, `DOCKER_PATH`, `KUBERNETES_PATH`, `TRUENAS_PATH`, `VMWARE_PATH`, `PATROL_PATH`, `PATROL_CONTROL_ANCHOR`, @@ -5346,17 +5346,14 @@ labels, relationship labels, headline formatting, summary wording, and canonical relationship/correlation ordering. Correlation and relationship badge text must preserve the formatter's human-readable title case instead of forcing visual uppercase over enum-like payloads such as `ALERT → ALERT`. -`frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx` -is the canonical shared card for governed policy-posture counts, while `frontend-modern/src/utils/resourcePolicyPresentation.ts` owns the canonical -sensitivity, routing, and redaction labels and aggregate count summaries. -That shared policy card also owns caller-supplied framing lines such as -subtitle and resource-count wording, so Patrol or other shared surfaces may -clarify whether the same governed counts read as policy-covered-resource -context without rebuilding their own policy-posture card shell. New -shared-surface framing needs such as Patrol's `policy-covered resources` -count label or explanatory subtitle should extend that card API instead of -forking a second page-local policy summary. +sensitivity, routing, and redaction labels and their orderings for governed +policy posture. No shared unified-resource card renders policy-posture counts +today: the card that once did lost its last mount on the Patrol page and was +removed together with its aggregate count-summary helpers. If a shared +posture card returns, caller-supplied framing such as a subtitle or a +resource-count label must extend that one card's API instead of forking a +page-local policy summary. Future correlation or policy-posture wording changes should extend those unified-resource owners instead of drifting into page-local loops in AI, Patrol, or infrastructure surfaces. @@ -5784,8 +5781,8 @@ projection, so the AI summary and any other backend policy posture surface do not re-sort redaction labels locally. They also own the canonical sensitivity and routing order used to format policy-posture count summaries with human-readable labels, so the AI summary -and frontend policy card both read the same presentation sequence from the -shared resource model. +and any frontend posture presentation read the same presentation sequence +from the shared resource model. Canonical resources now carry first-class relationship, capability, and timeline fields: `Capabilities` (bounded action definitions with approval levels), `Relationships` (typed inter-resource links with direction and diff --git a/frontend-modern/browser-verification/267c46b38a3f8020.json b/frontend-modern/browser-verification/267c46b38a3f8020.json new file mode 100644 index 0000000000..0ce88d034d --- /dev/null +++ b/frontend-modern/browser-verification/267c46b38a3f8020.json @@ -0,0 +1,42 @@ +{ + "version": 1, + "base_sha": "dcd409f9da0a7a7f35a2a4ad5a4354e0b1d3ee44", + "verified_at": "2026-10-07T10:47:00Z", + "result": "passed", + "changed_paths": [ + "frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx", + "frontend-modern/src/utils/resourcePolicyPresentation.ts" + ], + "content_sha256": { + "frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx": "deleted", + "frontend-modern/src/utils/resourcePolicyPresentation.ts": "6737afe76a7c7cc10e8d57e133c4a1e08855bf01e762677bba871e91856b7535" + }, + "routes": [ + "/settings/security-data-handling", + "/patrol", + "/truenas/storage" + ], + "viewports": [ + { + "width": 1440, + "height": 900 + }, + { + "width": 390, + "height": 844 + } + ], + "states": [ + "Embedded production build of this tree (base dcd409f9da, PR 2649 head) served by a private PULSE_MOCK_MODE backend on :7687 with admin bypass and telemetry off; /api/resources/stats posture: 1773 resources, all four sensitivities, all three routing scopes and all five redaction hints", + "Production vite build of this tree is byte-identical to the build of the base commit (same asset names, diff -r clean), so the shipped bundle is unchanged; check-bundle-size.mjs passes with no .bundlesize.json change", + "Settings Resource Privacy (Data Handling) panel, the live consumer of the util's labels and orderings: sensitivity Public/Internal/Sensitive/Restricted, routing Cloud Summary/Local First/Local Only and redaction Hostname/IP Address/Platform ID/Alias/Path render in canonical order with the mock counts (1,773 governed)", + "Patrol page loads and contains no posture card text (Data Governance, governed resources, policy-covered resources)", + "TrueNAS Storage, pool archive (sensitive, local-first) resource drawer: Platform details expanded, Context shown, Governance rows read Sensitivity Sensitive, Routing Local First, Redactions 3, Redaction labels Hostname/Platform ID/Alias plus the governed Safe Summary", + "No page errors; console showed only CSP inline-style refusals that predate this change (the base lacks PR 2642, which moved those static styles to classes on main) and that occur identically on the byte-identical base build" + ], + "interactions": [ + "Direct navigation to /settings/security-data-handling and /patrol at each viewport", + "On /truenas/storage clicked the archive pool row to open the resource drawer, expanded Platform details via its summary, then clicked Show context", + "Repeated the full pass at 1440x900 and 390x844 with Playwright (headless Chromium)" + ] +} diff --git a/frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx b/frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx deleted file mode 100644 index 93e03a566f..0000000000 --- a/frontend-modern/src/components/Infrastructure/ResourcePolicySummary.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { For, Show, type Component } from 'solid-js'; -import type { IntelligencePolicyPostureSummary } from '@/types/aiIntelligence'; -import { - getResourcePolicyRedactionSummaries, - getResourcePolicyRoutingSummaries, - getResourcePolicySensitivitySummaries, -} from '@/utils/resourcePolicyPresentation'; - -interface ResourcePolicySummaryProps { - posture?: IntelligencePolicyPostureSummary | null; - title?: string; - subtitle?: string; - resourceCountLabel?: string; - class?: string; -} - -export const ResourcePolicySummary: Component = (props) => { - const posture = () => props.posture; - const className = () => props.class?.trim() ?? ''; - const sensitivitySummaries = () => getResourcePolicySensitivitySummaries(posture()); - const routingSummaries = () => getResourcePolicyRoutingSummaries(posture()); - const redactionSummaries = () => getResourcePolicyRedactionSummaries(posture()); - - return ( - - {(value) => ( -
-
-
-

- {props.title ?? 'Data Governance'} -

- - {(subtitle) =>

{subtitle()}

} -
-

- {value().total_resources} {props.resourceCountLabel ?? 'governed resources'} -

-
-
- -
- - {(item) => ( -
-
{item.label}
-
{item.count}
-
- )} -
- - {(item) => ( -
-
{item.label}
-
{item.count}
-
- )} -
-
- - 0}> -
- - {(item) => ( - - {item.label} {item.count} - - )} - -
-
-
- )} -
- ); -}; - -export default ResourcePolicySummary; diff --git a/frontend-modern/src/components/Infrastructure/__tests__/ResourcePolicySummary.test.tsx b/frontend-modern/src/components/Infrastructure/__tests__/ResourcePolicySummary.test.tsx deleted file mode 100644 index 7b7eaefe57..0000000000 --- a/frontend-modern/src/components/Infrastructure/__tests__/ResourcePolicySummary.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { render, screen } from '@solidjs/testing-library'; -import { describe, expect, it } from 'vitest'; - -import { ResourcePolicySummary } from '../ResourcePolicySummary'; - -describe('ResourcePolicySummary', () => { - it('renders governed resource posture counts and redaction hints', () => { - render(() => ( - - )); - - expect(screen.getByText('Data Governance')).toBeInTheDocument(); - expect(screen.getByText('5 governed resources')).toBeInTheDocument(); - expect(screen.getByText('Public')).toBeInTheDocument(); - expect(screen.getByText('Local Only')).toBeInTheDocument(); - expect(screen.getByText('Hostname 3')).toBeInTheDocument(); - expect(screen.getByText('IP Address 1')).toBeInTheDocument(); - }); - - it('supports caller-owned subtitle and resource count wording', () => { - render(() => ( - - )); - - expect(screen.getByText('Policy coverage')).toBeInTheDocument(); - expect(screen.getByText('Coverage posture for policy-covered resources.')).toBeInTheDocument(); - expect(screen.getByText('4 policy-covered resources')).toBeInTheDocument(); - }); -}); diff --git a/frontend-modern/src/utils/__tests__/resourcePolicyPresentation.test.ts b/frontend-modern/src/utils/__tests__/resourcePolicyPresentation.test.ts index af228c5403..d26be239f7 100644 --- a/frontend-modern/src/utils/__tests__/resourcePolicyPresentation.test.ts +++ b/frontend-modern/src/utils/__tests__/resourcePolicyPresentation.test.ts @@ -7,9 +7,6 @@ import { hasDefaultResourcePolicyPosture, getResourcePolicyGovernedSummary, getResourcePolicyDisplayLabel, - getResourcePolicyRedactionSummaries, - getResourcePolicyRoutingSummaries, - getResourcePolicySensitivitySummaries, getResourcePolicyRedactionLabels, getResourceRedactionHintLabel, getResourceRoutingScopeLabel, @@ -123,56 +120,6 @@ describe('resourcePolicyPresentation utils', () => { ); }); - it('formats canonical policy count summaries', () => { - expect( - getResourcePolicySensitivitySummaries({ - total_resources: 3, - sensitivity_counts: { - public: 1, - internal: 2, - }, - routing_counts: {}, - }), - ).toEqual([ - { label: 'Public', count: 1 }, - { label: 'Internal', count: 2 }, - { label: 'Sensitive', count: 0 }, - { label: 'Restricted', count: 0 }, - ]); - - expect( - getResourcePolicyRoutingSummaries({ - total_resources: 3, - sensitivity_counts: {}, - routing_counts: { - 'cloud-summary': 1, - 'local-first': 2, - }, - }), - ).toEqual([ - { label: 'Cloud Summary', count: 1 }, - { label: 'Local First', count: 2 }, - { label: 'Local Only', count: 0 }, - ]); - - expect( - getResourcePolicyRedactionSummaries({ - total_resources: 3, - sensitivity_counts: {}, - routing_counts: { - 'cloud-summary': 1, - }, - redaction_counts: { - hostname: 2, - path: 1, - }, - }), - ).toEqual([ - { label: 'Hostname', count: 2 }, - { label: 'Path', count: 1 }, - ]); - }); - it('exports canonical policy ordering', () => { expect(RESOURCE_POLICY_SENSITIVITY_ORDER).toEqual([ 'public', diff --git a/frontend-modern/src/utils/resourcePolicyPresentation.ts b/frontend-modern/src/utils/resourcePolicyPresentation.ts index bef68ec381..be19f8885e 100644 --- a/frontend-modern/src/utils/resourcePolicyPresentation.ts +++ b/frontend-modern/src/utils/resourcePolicyPresentation.ts @@ -6,7 +6,6 @@ import type { ResourceSensitivity, } from '@/types/resource'; import { requiresGovernedResourceDisplay } from '@/types/resource'; -import type { IntelligencePolicyPostureSummary } from '@/types/aiIntelligence'; type PolicyBadgePresentation = { label: string; @@ -14,11 +13,6 @@ type PolicyBadgePresentation = { className: string; }; -export type ResourcePolicyCountSummary = { - label: string; - count: number; -}; - export type ResourcePolicyDisplayResource = Pick< Resource, 'name' | 'displayName' | 'policy' | 'aiSafeSummary' @@ -128,58 +122,6 @@ export const getResourceRedactionHintLabel = (hint?: ResourceRedactionHint): str export const getResourcePolicyRedactionLabels = (policy?: ResourcePolicy): string[] => (policy?.routing.redact ?? []).map((hint) => getResourceRedactionHintLabel(hint)); -const buildCountSummaries = ( - counts: Partial> | undefined, - order: readonly T[], - labelFn: (value: T) => string, - includeZeroCounts: boolean, -): ResourcePolicyCountSummary[] => { - if (!counts) return []; - - const summaries: ResourcePolicyCountSummary[] = []; - for (const value of order) { - const count = counts[value] ?? 0; - if (!includeZeroCounts && count <= 0) { - continue; - } - summaries.push({ - label: labelFn(value), - count, - }); - } - return summaries; -}; - -export const getResourcePolicySensitivitySummaries = ( - posture?: IntelligencePolicyPostureSummary | null, -): ResourcePolicyCountSummary[] => - buildCountSummaries( - posture?.sensitivity_counts, - RESOURCE_POLICY_SENSITIVITY_ORDER, - getResourceSensitivityLabel, - true, - ); - -export const getResourcePolicyRoutingSummaries = ( - posture?: IntelligencePolicyPostureSummary | null, -): ResourcePolicyCountSummary[] => - buildCountSummaries( - posture?.routing_counts, - RESOURCE_POLICY_ROUTING_ORDER, - getResourceRoutingScopeLabel, - true, - ); - -export const getResourcePolicyRedactionSummaries = ( - posture?: IntelligencePolicyPostureSummary | null, -): ResourcePolicyCountSummary[] => - buildCountSummaries( - posture?.redaction_counts, - RESOURCE_POLICY_REDACTION_ORDER, - getResourceRedactionHintLabel, - false, - ); - const getConciseGovernedDisplaySummary = (summary: string): string => { const trimmed = summary.trim(); if (!trimmed) return '';