From 7957d6cf1432e58812d12d6a5174683721c6143b Mon Sep 17 00:00:00 2001
From: Christopher <131207452+PetrefiedThunder@users.noreply.github.com>
Date: Wed, 28 Oct 2026 13:28:00 -0700
Subject: [PATCH 1/3] fix(SEL-616): degrade Compliance when readiness/summary
is unavailable
Root cause of the prod 503 is outside this PR (regengine lacks SELECT
on tenants.status for the bridge-key auth path). Soften the Compliance
Dashboard so a score or workbench readiness failure stays sectional
instead of blanking the page, and document the DevOps grant in the PR.
---
.../pages/dashboard-load-errors.test.tsx | 16 +-
.../src/app/dashboard/compliance/page.tsx | 164 +++++++++++++-----
frontend/src/lib/api-hooks.ts | 7 +
3 files changed, 145 insertions(+), 42 deletions(-)
diff --git a/frontend/src/__tests__/pages/dashboard-load-errors.test.tsx b/frontend/src/__tests__/pages/dashboard-load-errors.test.tsx
index e7e3d297a..a477c383b 100644
--- a/frontend/src/__tests__/pages/dashboard-load-errors.test.tsx
+++ b/frontend/src/__tests__/pages/dashboard-load-errors.test.tsx
@@ -223,11 +223,25 @@ describe('shared dashboard load errors', () => {
const user = userEvent.setup();
renderWithQuery();
- expect(await screen.findByRole('alert')).toHaveTextContent('We could not load this page.');
+ expect(await screen.findByRole('alert')).toHaveTextContent('We could not load the compliance score.');
+ // SEL-616: score failure must not blank Import readiness / setup paths
+ expect(await screen.findByText('Import readiness')).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: 'Retry' }));
await waitFor(() => expect(fetchComplianceScoreMock).toHaveBeenCalledTimes(2));
});
+ it('keeps Import readiness visible when score and readiness both fail (SEL-616)', async () => {
+ fetchComplianceScoreMock.mockRejectedValue(apiError(401, 'Unauthorized', crypto.randomUUID()));
+ fetchWorkbenchSummaryMock.mockRejectedValue(apiError(503, 'Service Unavailable', crypto.randomUUID()));
+ renderWithQuery();
+
+ expect(await screen.findByRole('alert')).toHaveTextContent('We could not load the compliance score.');
+ expect(await screen.findByText('Import readiness')).toBeInTheDocument();
+ expect(screen.getByText(/Import readiness is unavailable right now/i)).toBeInTheDocument();
+ expect(screen.queryByText('We could not load this page.')).not.toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Retry import readiness' })).toBeInTheDocument();
+ });
+
it('lets recall report retry a failed request in place', async () => {
fetchRecallReportMock.mockRejectedValue(new Error('server error'));
const user = userEvent.setup();
diff --git a/frontend/src/app/dashboard/compliance/page.tsx b/frontend/src/app/dashboard/compliance/page.tsx
index fe90da7a7..550d81430 100644
--- a/frontend/src/app/dashboard/compliance/page.tsx
+++ b/frontend/src/app/dashboard/compliance/page.tsx
@@ -321,6 +321,7 @@ export default function ComplianceDashboardPage() {
const {
data: workbenchSummary = null,
+ isError: readinessFailed,
refetch: fetchWorkbenchSummary,
} = useQuery({
queryKey: ['inflow-workbench-readiness', tenantId],
@@ -330,7 +331,8 @@ export default function ComplianceDashboardPage() {
retry: false,
});
- const error = scoreError;
+ // Score and workbench failures stay sectional so one outage cannot blank the page
+ // (SEL-616: readiness/summary 503 must not look like a full Compliance crash).
const lastFetched = dataUpdatedAt ? new Date(dataUpdatedAt) : null;
// Ordered breakdown keys by weight (highest first)
@@ -381,12 +383,12 @@ export default function ComplianceDashboardPage() {
- {lastFetched && !error && (
+ {lastFetched && !scoreError && (
Updated {lastFetched.toLocaleTimeString()}
)}
- {score && !error && (
+ {score && !scoreError && (