mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
Remove the unreachable resource policy summary card
ResourcePolicySummary lost its last production mount when 27ba85ddc6
reworked the Patrol workspace, which also added a guard asserting the
workspace no longer contains it. A TypeScript import graph rooted at
src/index.tsx (static and dynamic imports) reaches no importer, its own
test was the only caller, and none of its literals appear in a
production build.
The count-summary helpers in resourcePolicyPresentation.ts (the
sensitivity, routing and redaction summaries, buildCountSummaries and
ResourcePolicyCountSummary) served only that card, so they go with it.
The label helpers and canonical orderings stay: the Settings resource
privacy panel and the resource drawer still use them.
Registry ownership and the resource-consumers proof entry drop the
deleted files. Contract prose that described the card as rendered on
the AI summary or Patrol page now says nothing renders the AI summary's
posture counts, and what a returning posture surface must reuse.
A production build is byte-identical before and after, so no chunk
names or bundle baselines change.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)"
|
||||
]
|
||||
}
|
||||
@@ -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<ResourcePolicySummaryProps> = (props) => {
|
||||
const posture = () => props.posture;
|
||||
const className = () => props.class?.trim() ?? '';
|
||||
const sensitivitySummaries = () => getResourcePolicySensitivitySummaries(posture());
|
||||
const routingSummaries = () => getResourcePolicyRoutingSummaries(posture());
|
||||
const redactionSummaries = () => getResourcePolicyRedactionSummaries(posture());
|
||||
|
||||
return (
|
||||
<Show when={posture()}>
|
||||
{(value) => (
|
||||
<div class={`rounded-md border border-border-subtle bg-surface p-4 ${className()}`.trim()}>
|
||||
<div class="flex flex-wrap items-start justify-between gap-2">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-base-content">
|
||||
{props.title ?? 'Data Governance'}
|
||||
</h3>
|
||||
<Show when={props.subtitle}>
|
||||
{(subtitle) => <p class="mt-1 text-xs text-muted">{subtitle()}</p>}
|
||||
</Show>
|
||||
<p class="mt-1 text-xs text-muted">
|
||||
{value().total_resources} {props.resourceCountLabel ?? 'governed resources'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl class="mt-3 grid grid-cols-2 gap-2 text-sm">
|
||||
<For each={sensitivitySummaries()}>
|
||||
{(item) => (
|
||||
<div class="rounded-md bg-surface px-3 py-2">
|
||||
<dt class="text-xs uppercase tracking-wide text-muted">{item.label}</dt>
|
||||
<dd class="mt-1 font-semibold text-base-content">{item.count}</dd>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
<For each={routingSummaries()}>
|
||||
{(item) => (
|
||||
<div class="rounded-md bg-surface px-3 py-2">
|
||||
<dt class="text-xs uppercase tracking-wide text-muted">{item.label}</dt>
|
||||
<dd class="mt-1 font-semibold text-base-content">{item.count}</dd>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</dl>
|
||||
|
||||
<Show when={redactionSummaries().length > 0}>
|
||||
<div class="mt-2 flex flex-wrap gap-1">
|
||||
<For each={redactionSummaries()}>
|
||||
{(item) => (
|
||||
<span class="inline-flex items-center rounded-sm bg-surface-alt px-1.5 py-0.5 text-[10px]">
|
||||
{item.label} {item.count}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResourcePolicySummary;
|
||||
-57
@@ -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(() => (
|
||||
<ResourcePolicySummary
|
||||
posture={{
|
||||
total_resources: 5,
|
||||
sensitivity_counts: {
|
||||
public: 1,
|
||||
internal: 2,
|
||||
sensitive: 1,
|
||||
restricted: 1,
|
||||
},
|
||||
routing_counts: {
|
||||
'cloud-summary': 2,
|
||||
'local-first': 2,
|
||||
'local-only': 1,
|
||||
},
|
||||
redaction_counts: {
|
||||
hostname: 3,
|
||||
'ip-address': 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
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(() => (
|
||||
<ResourcePolicySummary
|
||||
title="Policy coverage"
|
||||
subtitle="Coverage posture for policy-covered resources."
|
||||
resourceCountLabel="policy-covered resources"
|
||||
posture={{
|
||||
total_resources: 4,
|
||||
sensitivity_counts: {},
|
||||
routing_counts: {},
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
expect(screen.getByText('Policy coverage')).toBeInTheDocument();
|
||||
expect(screen.getByText('Coverage posture for policy-covered resources.')).toBeInTheDocument();
|
||||
expect(screen.getByText('4 policy-covered resources')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -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',
|
||||
|
||||
@@ -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 = <T extends string>(
|
||||
counts: Partial<Record<T, number>> | 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 '';
|
||||
|
||||
Reference in New Issue
Block a user