mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Downplay infrastructure policy chips
This commit is contained in:
@@ -971,8 +971,10 @@ surplus width is redistributed across peer metric, source, uptime, and action
|
||||
columns instead of being dumped into the first column and wasting operator
|
||||
visible table density.
|
||||
That hot-path contract now includes policy badge rendering on resource rows.
|
||||
Policy-rich table rows must collapse to a single inline policy summary chip
|
||||
instead of stacking sensitivity and routing badges inside the resource column.
|
||||
Policy-rich table rows must only surface a single inline summary chip for
|
||||
blocking `local-only`/`restricted` posture; non-blocking `sensitive` +
|
||||
`local-first` and redaction-only metadata belongs in Data Handling, detail, and
|
||||
AI/governance surfaces instead of spending default row visual budget.
|
||||
Agentless availability evidence belongs on that same bounded row path.
|
||||
Infrastructure `network-endpoint` rows may replace otherwise empty host metric
|
||||
slots with one compact inline target/result readout, while the System column
|
||||
|
||||
@@ -955,9 +955,12 @@ per-resource policy lines instead of reconstructing a separate
|
||||
The infrastructure host-table shell now treats the default
|
||||
`Internal` + `Cloud Summary` posture as canonical policy metadata that should
|
||||
stay available in the drawer and AI/governance surfaces without being promoted
|
||||
to always-on row chrome. Inline row badges are reserved for non-default policy
|
||||
states so the canonical resource surface does not imply that every host carries
|
||||
an operator-actionable governance exception.
|
||||
to always-on row chrome. Inline row badges are reserved for blocking policy
|
||||
states such as `local-only` routing or `restricted` sensitivity, while
|
||||
`sensitive` + `local-first` and redaction-only posture remains visible in Data
|
||||
Handling, detail, and AI/governance surfaces. The table must not imply that
|
||||
ordinary sensitive resources carry an operator-actionable infrastructure
|
||||
exception.
|
||||
The resource drawer now applies the same rule to its investigation-context
|
||||
governance block: the default posture remains part of the canonical policy
|
||||
contract, but the drawer only surfaces the governance section when the policy
|
||||
@@ -1603,10 +1606,11 @@ performance proof route. The shared resource table and resource drawer now
|
||||
surface compact timeline summary chips, so facet presentation changes must
|
||||
continue to flow through the same governed resource-row surface rather than
|
||||
inventing a separate ad hoc summary path. Dense table rows must bound those
|
||||
chips with an explicit visible limit and overflow label, while policy-rich
|
||||
table rows collapse sensitivity/routing posture into a single inline summary
|
||||
chip so mock-rich canonical resources cannot stack badges inside the resource
|
||||
column. Those row summaries now prefer canonical `facetCounts` on the
|
||||
chips with an explicit visible limit and overflow label, while row-level
|
||||
policy chips are limited to blocking `local-only`/`restricted` posture so
|
||||
mock-rich canonical resources cannot stack or overstate governance badges
|
||||
inside the resource column. Those row summaries now prefer canonical
|
||||
`facetCounts` on the
|
||||
resource object when available, so the backend list/read shapes remain the
|
||||
source of truth instead of forcing the frontend to infer totals only from
|
||||
loaded slices. The drawer now fetches those facets
|
||||
|
||||
+14
-5
@@ -659,7 +659,7 @@ describe('UnifiedResourceTable performance contract', () => {
|
||||
expect(matchesSearch(governedResource, 'secret-host-9')).toBe(false);
|
||||
});
|
||||
|
||||
it('suppresses the default policy posture badges in host-table rows while preserving exceptional policy badges', async () => {
|
||||
it('suppresses non-blocking policy posture in host-table rows while preserving blocking policy badges', async () => {
|
||||
const resources = [
|
||||
makeResource(0, {
|
||||
name: 'default-policy-host',
|
||||
@@ -677,6 +677,14 @@ describe('UnifiedResourceTable performance contract', () => {
|
||||
routing: { scope: 'local-first', redact: ['hostname'] },
|
||||
},
|
||||
}),
|
||||
makeResource(2, {
|
||||
name: 'local-only-host',
|
||||
displayName: 'Local Only Host',
|
||||
policy: {
|
||||
sensitivity: 'restricted',
|
||||
routing: { scope: 'local-only', redact: ['hostname'] },
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const { container } = render(() => (
|
||||
@@ -698,10 +706,11 @@ describe('UnifiedResourceTable performance contract', () => {
|
||||
|
||||
expect(bodyQueries.queryByText('Internal')).not.toBeInTheDocument();
|
||||
expect(bodyQueries.queryByText('Cloud Summary')).not.toBeInTheDocument();
|
||||
const policyBadge = bodyQueries.getByText('Sensitive');
|
||||
expect(policyBadge).toBeInTheDocument();
|
||||
expect(policyBadge.closest('[title]')?.getAttribute('title')).toContain('Local First');
|
||||
expect(bodyQueries.queryByText('Sensitive')).not.toBeInTheDocument();
|
||||
expect(bodyQueries.queryByText('Local First')).not.toBeInTheDocument();
|
||||
const policyBadge = bodyQueries.getByText('Local Only');
|
||||
expect(policyBadge).toBeInTheDocument();
|
||||
expect(policyBadge.closest('[title]')?.getAttribute('title')).toContain('Restricted');
|
||||
});
|
||||
|
||||
it('renders facet summary badges without changing the Profile S row budget', async () => {
|
||||
@@ -935,7 +944,7 @@ describe('UnifiedResourceTable performance contract', () => {
|
||||
expect(rowCount).toBeLessThanOrEqual(140);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(getAllByText('Restricted').length).toBeGreaterThan(0);
|
||||
expect(getAllByText('Local Only').length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(getPreferredInfrastructureDisplayName(resources[0]!)).toBe('Sensitive Host');
|
||||
});
|
||||
|
||||
+14
-3
@@ -58,7 +58,7 @@ const resource: Resource = {
|
||||
};
|
||||
|
||||
describe('UnifiedResourceTable governance presentation', () => {
|
||||
it('surfaces canonical policy badges in the resource row', () => {
|
||||
it('surfaces blocking policy posture in the resource row', () => {
|
||||
const { getByText, queryByText } = render(() => (
|
||||
<UnifiedResourceTable
|
||||
resources={[resource]}
|
||||
@@ -68,17 +68,28 @@ describe('UnifiedResourceTable governance presentation', () => {
|
||||
/>
|
||||
));
|
||||
|
||||
expect(getByText('Restricted')).toBeInTheDocument();
|
||||
expect(getByText('Local Only')).toBeInTheDocument();
|
||||
expect(queryByText('Restricted')).toBeNull();
|
||||
expect(getByText('Sensitive Host')).toBeInTheDocument();
|
||||
expect(queryByText('restricted host summary safe for remote AI consumption')).toBeNull();
|
||||
expect(queryByText('(sensitive-host)')).toBeNull();
|
||||
});
|
||||
|
||||
it('surfaces resource facet counts in the resource row', () => {
|
||||
const facetResource: Resource = {
|
||||
...resource,
|
||||
policy: {
|
||||
sensitivity: 'sensitive',
|
||||
routing: {
|
||||
scope: 'local-first',
|
||||
redact: ['hostname'],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { getByText, queryByText } = render(() => (
|
||||
<UnifiedResourceTable
|
||||
resources={[resource]}
|
||||
resources={[facetResource]}
|
||||
expandedResourceId={null}
|
||||
onExpandedResourceChange={vi.fn()}
|
||||
groupingMode="flat"
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
RESOURCE_POLICY_REDACTION_ORDER,
|
||||
RESOURCE_POLICY_ROUTING_ORDER,
|
||||
RESOURCE_POLICY_SENSITIVITY_ORDER,
|
||||
hasBlockingResourcePolicyPosture,
|
||||
hasDefaultResourcePolicyPosture,
|
||||
getResourcePolicyTableBadges,
|
||||
getResourcePolicyGovernedSummary,
|
||||
@@ -37,7 +38,7 @@ describe('resourcePolicyPresentation utils', () => {
|
||||
).toEqual(['Hostname', 'IP Address']);
|
||||
});
|
||||
|
||||
it('suppresses the default internal cloud-summary posture in table rows', () => {
|
||||
it('keeps non-blocking policy posture out of table rows', () => {
|
||||
expect(
|
||||
hasDefaultResourcePolicyPosture({
|
||||
sensitivity: 'internal',
|
||||
@@ -57,14 +58,14 @@ describe('resourcePolicyPresentation utils', () => {
|
||||
).toEqual([]);
|
||||
|
||||
expect(
|
||||
getResourcePolicyTableBadges({
|
||||
hasBlockingResourcePolicyPosture({
|
||||
sensitivity: 'sensitive',
|
||||
routing: {
|
||||
scope: 'local-first',
|
||||
redact: ['hostname'],
|
||||
},
|
||||
}).map((badge) => badge.label),
|
||||
).toEqual(['Sensitive']);
|
||||
}),
|
||||
).toBe(false);
|
||||
expect(
|
||||
getResourcePolicyTableBadges({
|
||||
sensitivity: 'sensitive',
|
||||
@@ -72,8 +73,8 @@ describe('resourcePolicyPresentation utils', () => {
|
||||
scope: 'local-first',
|
||||
redact: ['hostname'],
|
||||
},
|
||||
})[0]?.title,
|
||||
).toContain('Local First');
|
||||
}).map((badge) => badge.label),
|
||||
).toEqual([]);
|
||||
expect(
|
||||
getResourcePolicyTableBadges({
|
||||
sensitivity: 'internal',
|
||||
@@ -82,7 +83,7 @@ describe('resourcePolicyPresentation utils', () => {
|
||||
redact: ['hostname'],
|
||||
},
|
||||
}).map((badge) => badge.label),
|
||||
).toEqual(['Redacted']);
|
||||
).toEqual([]);
|
||||
|
||||
expect(
|
||||
hasDefaultResourcePolicyPosture({
|
||||
@@ -95,6 +96,30 @@ describe('resourcePolicyPresentation utils', () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('surfaces blocking local-only policy posture in table rows', () => {
|
||||
expect(
|
||||
hasBlockingResourcePolicyPosture({
|
||||
sensitivity: 'restricted',
|
||||
routing: {
|
||||
scope: 'local-only',
|
||||
redact: ['hostname'],
|
||||
},
|
||||
}),
|
||||
).toBe(true);
|
||||
|
||||
const badges = getResourcePolicyTableBadges({
|
||||
sensitivity: 'restricted',
|
||||
routing: {
|
||||
scope: 'local-only',
|
||||
redact: ['hostname'],
|
||||
},
|
||||
});
|
||||
|
||||
expect(badges.map((badge) => badge.label)).toEqual(['Local Only']);
|
||||
expect(badges[0]?.title).toContain('Restricted');
|
||||
expect(badges[0]?.title).toContain('Redacts Hostname');
|
||||
});
|
||||
|
||||
it('uses concise governed labels for redacted resources', () => {
|
||||
expect(
|
||||
getResourcePolicyDisplayLabel({
|
||||
|
||||
@@ -95,12 +95,6 @@ const routingPresentation: Record<
|
||||
},
|
||||
};
|
||||
|
||||
const redactionBadgePresentation: Pick<PolicyBadgePresentation, 'label' | 'title' | 'className'> = {
|
||||
label: 'Redacted',
|
||||
title: 'Resource identity fields are redacted before governed handling.',
|
||||
className: `${badgeBaseClass} bg-slate-200 text-slate-700 dark:bg-slate-800 dark:text-slate-300`,
|
||||
};
|
||||
|
||||
const redactionLabels: Record<ResourceRedactionHint, string> = {
|
||||
hostname: 'Hostname',
|
||||
'ip-address': 'IP Address',
|
||||
@@ -122,25 +116,22 @@ export const hasDefaultResourcePolicyPosture = (policy?: ResourcePolicy): boolea
|
||||
(policy.routing.redact?.length ?? 0) === 0,
|
||||
);
|
||||
|
||||
export const hasBlockingResourcePolicyPosture = (policy?: ResourcePolicy): boolean =>
|
||||
Boolean(policy && (policy.sensitivity === 'restricted' || policy.routing.scope === 'local-only'));
|
||||
|
||||
export const getResourcePolicyTableBadges = (
|
||||
policy?: ResourcePolicy,
|
||||
): PolicyBadgePresentation[] => {
|
||||
if (!policy) return [];
|
||||
|
||||
if (hasDefaultResourcePolicyPosture(policy)) {
|
||||
if (!hasBlockingResourcePolicyPosture(policy)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sensitivity = sensitivityPresentation[policy.sensitivity];
|
||||
const routing = routingPresentation[policy.routing.scope];
|
||||
const redactions = getResourcePolicyRedactionLabels(policy);
|
||||
const hasNonDefaultSensitivity = policy.sensitivity !== 'internal';
|
||||
const hasNonDefaultRouting = policy.routing.scope !== 'cloud-summary';
|
||||
const primary = hasNonDefaultSensitivity
|
||||
? sensitivity
|
||||
: hasNonDefaultRouting
|
||||
? routing
|
||||
: redactionBadgePresentation;
|
||||
const primary = policy.routing.scope === 'local-only' ? routing : sensitivity;
|
||||
const redactionTitle = redactions.length > 0 ? `Redacts ${redactions.join(', ')}.` : undefined;
|
||||
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user