diff --git a/src/app/differentials/diagnoses/[slug]/error.tsx b/src/app/differentials/diagnoses/[slug]/error.tsx
new file mode 100644
index 0000000000..517e6de4f4
--- /dev/null
+++ b/src/app/differentials/diagnoses/[slug]/error.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { useEffect } from "react";
+import { AlertTriangle, RefreshCw } from "lucide-react";
+import { primaryControl } from "@/components/ui-primitives";
+import { cn } from "@/components/ui-primitives";
+
+export default function ErrorBoundary({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
+ useEffect(() => {
+ console.error("Unhandled runtime error captured in differentials diagnoses segment:", error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+ Failed to load differential details
+
+
+
+ An unexpected error occurred while fetching the differential diagnosis details.
+
+
+ {error.digest && (
+
+ Digest: {error.digest}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/differentials/diagnoses/[slug]/loading.tsx b/src/app/differentials/diagnoses/[slug]/loading.tsx
new file mode 100644
index 0000000000..59334e70b5
--- /dev/null
+++ b/src/app/differentials/diagnoses/[slug]/loading.tsx
@@ -0,0 +1,5 @@
+import { ModeHomeRouteLoading } from "@/components/mode-home-page-skeleton";
+
+export default function Loading() {
+ return ;
+}
diff --git a/src/app/differentials/presentations/[slug]/error.tsx b/src/app/differentials/presentations/[slug]/error.tsx
new file mode 100644
index 0000000000..132d9587bc
--- /dev/null
+++ b/src/app/differentials/presentations/[slug]/error.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { useEffect } from "react";
+import { AlertTriangle, RefreshCw } from "lucide-react";
+import { primaryControl } from "@/components/ui-primitives";
+import { cn } from "@/components/ui-primitives";
+
+export default function ErrorBoundary({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
+ useEffect(() => {
+ console.error("Unhandled runtime error captured in differentials presentations segment:", error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+ Failed to load presentation workflow
+
+
+
+ An unexpected error occurred while fetching the differential presentation workflow details.
+
+
+ {error.digest && (
+
+ Digest: {error.digest}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/differentials/presentations/[slug]/loading.tsx b/src/app/differentials/presentations/[slug]/loading.tsx
new file mode 100644
index 0000000000..59334e70b5
--- /dev/null
+++ b/src/app/differentials/presentations/[slug]/loading.tsx
@@ -0,0 +1,5 @@
+import { ModeHomeRouteLoading } from "@/components/mode-home-page-skeleton";
+
+export default function Loading() {
+ return ;
+}
diff --git a/src/app/medications/[slug]/error.tsx b/src/app/medications/[slug]/error.tsx
new file mode 100644
index 0000000000..57623ac02a
--- /dev/null
+++ b/src/app/medications/[slug]/error.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { useEffect } from "react";
+import { AlertTriangle, RefreshCw } from "lucide-react";
+import { primaryControl } from "@/components/ui-primitives";
+import { cn } from "@/components/ui-primitives";
+
+export default function ErrorBoundary({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
+ useEffect(() => {
+ console.error("Unhandled runtime error captured in medications segment:", error);
+ }, [error]);
+
+ return (
+
+
+
+
+
+ Failed to load medication details
+
+
+
+ An unexpected error occurred while fetching the prescribing details.
+
+
+ {error.digest && (
+
+ Digest: {error.digest}
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/medications/[slug]/loading.tsx b/src/app/medications/[slug]/loading.tsx
new file mode 100644
index 0000000000..59334e70b5
--- /dev/null
+++ b/src/app/medications/[slug]/loading.tsx
@@ -0,0 +1,5 @@
+import { ModeHomeRouteLoading } from "@/components/mode-home-page-skeleton";
+
+export default function Loading() {
+ return ;
+}
diff --git a/src/components/ClinicalDashboard.tsx b/src/components/ClinicalDashboard.tsx
index 84684daeec..2545d2b249 100644
--- a/src/components/ClinicalDashboard.tsx
+++ b/src/components/ClinicalDashboard.tsx
@@ -2154,16 +2154,13 @@ export function ClinicalDashboard({
useEffect(() => {
const previous = prevAuthStatusRef.current;
prevAuthStatusRef.current = authStatus;
- if (
- (authStatus === "signed_out" || authStatus === "expired") &&
- (previous === "authenticated" || previous === "loading")
- ) {
+ if ((authStatus === "signed_out" || authStatus === "expired") && previous === "authenticated") {
resetAnswerThread();
setAnswer(null);
setSources([]);
latestAnswerTurnRef.current = null;
}
- }, [authStatus]);
+ }, [authStatus, resetAnswerThread]);
const supabaseEnvStatus = setupChecks.find((check) => check.id === "env")?.status;
const browserAuthUnavailableDemoFallback = !auth.isConfigured && supabaseEnvStatus !== "ready";
const localNoAuthMode = isLocalNoAuthMode();
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index 5b2a68c3ac..3d2bae636a 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -1,5 +1,6 @@
import type { Route } from "playwright-core";
import { expect, test, type Locator, type Page } from "playwright/test";
+import { answerThreadStorageKey } from "../src/lib/answer-thread-storage";
import { demoAnswer, demoDocuments, getDemoDocument, getDemoDocumentPayload } from "../src/lib/demo-data";
import { deriveGovernanceFromSections } from "../src/lib/medication-records";
import { getMedicationRecord, loadMedicationSnapshot } from "../src/lib/medication-snapshot";
@@ -551,6 +552,23 @@ async function waitForDemoDashboardReady(page: Page) {
await expect(page.getByRole("button", { name: "Open answer options" })).toBeVisible({ timeout: 30000 });
}
+async function waitForPersistedAnswerThread(page: Page, minPriorTurns = 1) {
+ await expect
+ .poll(async () =>
+ page.evaluate((storageKey) => {
+ try {
+ const raw = window.localStorage.getItem(storageKey);
+ if (!raw) return 0;
+ const parsed = JSON.parse(raw) as { priorTurns?: unknown[] };
+ return Array.isArray(parsed.priorTurns) ? parsed.priorTurns.length : 0;
+ } catch {
+ return 0;
+ }
+ }, answerThreadStorageKey),
+ )
+ .toBeGreaterThanOrEqual(minPriorTurns);
+}
+
async function openGuide(page: Page) {
const viewport = page.viewportSize();
const dialog = page.getByRole("dialog", { name: "Clinical KB guide" });
@@ -1243,9 +1261,12 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(page).toHaveURL(/\?mode=answer&q=what\+about\+renal\+impairment\%3F&run=1/);
await expectNoPageHorizontalOverflow(page);
+ await waitForPersistedAnswerThread(page, 1);
await page.reload();
await waitForDemoDashboardReady(page);
- await expect(page.getByTestId("user-question-bubble")).toHaveCount(2, { timeout: uiAssertionTimeoutMs });
+ await expect(async () => {
+ await expect(page.getByTestId("user-question-bubble")).toHaveCount(2);
+ }).toPass({ timeout: 15_000 });
await expect(page.getByTestId("user-question-bubble").first()).toContainText(firstQuestion);
await expect(page.getByTestId("user-question-bubble").nth(1)).toContainText(followUp);
await expect(page.locator('[data-dashboard-stage="answer-thread-turn"][data-collapsed="true"]')).toHaveCount(1);
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index b110efc708..ef9823b7d5 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -97,9 +97,12 @@ async function commandSurfaceOpensAbovePill(page: Page, hintPattern: RegExp) {
{ timeout: 10_000 },
);
await input.click();
- await input.press("ArrowDown");
+ await expect(async () => {
+ await input.press("ArrowDown");
+ await expect(page.getByText(hintPattern)).toBeVisible();
+ await expect(page.getByRole("listbox").first()).toBeVisible();
+ }).toPass({ timeout: 15_000 });
- await expect(page.getByText(hintPattern)).toBeVisible();
const listbox = page.getByRole("listbox").first();
await expect(listbox).toBeVisible();
@@ -407,6 +410,8 @@ test.describe("Clinical KB applications launcher", () => {
await page.setViewportSize({ width: 390, height: 820 });
await gotoLauncher(page, "/services?q=13YARN&focus=1&run=1");
await expect(page.getByRole("button", { name: "Mode Services" })).toBeVisible();
+ await expect(visibleGlobalSearchInput(page).first()).toBeVisible();
+ await page.waitForLoadState("networkidle", { timeout: 15_000 }).catch(() => undefined);
await commandSurfaceOpensAbovePill(page, /Searching services/i);
await expectNoPageHorizontalOverflow(page);
});