From 88ae7c315980a035a6904fb6bb6140b2445875c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 08:27:51 +0000 Subject: [PATCH 01/11] Plain wording for medicine record, AI summary label, Clinical Ask limits (D26-06, D26-09, D26-10, D26-12) Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01QYsGZ5ecrWh4xL76inGoAq --- .../use-medication-catalog.ts | 19 ++++++-- .../document-clinical-summary.tsx | 10 ++++- src/lib/answer-limit-copy.ts | 44 +++++++++++++++++++ src/lib/clinical-ask/client-stream.ts | 30 ++++++++++--- tests/clinical-ask-stream-contract.test.ts | 34 +++++++++++++- tests/document-clinical-summary.dom.test.tsx | 9 ++++ tests/medication-catalog-errors.dom.test.tsx | 20 ++++++++- 7 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 src/lib/answer-limit-copy.ts diff --git a/src/components/clinical-dashboard/use-medication-catalog.ts b/src/components/clinical-dashboard/use-medication-catalog.ts index d68a1a287f..f49c5083b5 100644 --- a/src/components/clinical-dashboard/use-medication-catalog.ts +++ b/src/components/clinical-dashboard/use-medication-catalog.ts @@ -205,6 +205,21 @@ function medicationCatalogErrorMessage(error: unknown, timedOut: boolean): strin return "Could not load medications. Try again in a moment."; } +/** + * Plain words for a medicine record that would not load, with the status kept as a short code + * for reporting a fault. The record page shows this on its Details line, which used to read a + * bare "Request failed (503)" (design audit D26-12). + */ +function medicationRecordErrorMessage(error: unknown): string { + if (!(error instanceof MedicationRequestError)) return "Could not load this medicine."; + const code = `Code ${error.status}.`; + if (error.status === 401) return `Your sign-in has ended. Sign in again to view this medicine. ${code}`; + if (error.status === 429) return `Too many requests in a short time. Wait a moment, then try again. ${code}`; + if (error.status === 404) return `This medicine wasn’t found. ${code}`; + if (error.status >= 500) return `The medicines service is busy or unavailable. Try again in a minute. ${code}`; + return `This medicine couldn’t be loaded. ${code}`; +} + async function responseErrorCode(response: Response): Promise { try { const body = jsonObject(await response.json()); @@ -427,9 +442,7 @@ export function useMedicationDetail(slug?: string): MedicationDetailState { loading: false, error: isOffline ? "You are offline. Connect to view this medication." - : error instanceof Error - ? error.message - : "Could not load medication.", + : medicationRecordErrorMessage(error), notFoundCode: error instanceof MedicationRequestError && error.status === 404 && error.code === "medication_not_found", }); diff --git a/src/components/document-viewer/document-clinical-summary.tsx b/src/components/document-viewer/document-clinical-summary.tsx index 43b0a91bfd..44c40da0c5 100644 --- a/src/components/document-viewer/document-clinical-summary.tsx +++ b/src/components/document-viewer/document-clinical-summary.tsx @@ -435,6 +435,14 @@ export function DocumentClinicalSummary({ ) : null} + {/* A model wrote this summary and the priorities below when the file was indexed, so it is + labelled as AI, as answers are; unlabelled it read as the document's own words (D26-06). */} +

+ + AI summary + + Written by AI from this document when it was added. Check each point against the page it links to. +

{model.summary ? (

setMobilePrioritiesOpen(false)} returnFocusRef={mobilePrioritiesButtonRef} title="Clinical priorities" - description="Expanded, source-linked clinical detail" + description="AI summary of this document. Check each point against the page it links to." headerLeading={

Remove identifiable details before using Smart mode.

) : null} {session.submitted ?

Clinical Ask is gathering governed evidence…

: null} + {stall.stalled ? ( +
+

+ This is taking longer than usual. The service may be busy. +

+
+ + +
+
+ ) : null} {session.response ? ( void; onRetry: () => void; onSearchDocuments: () => void; + /** Opens sign-in and sends the held question once signed in (D26-05). */ + onSignInAndSend?: () => void; }) { + const answerMode = activeModeResultKind === "answer"; + if (error && answerMode && errorKind === "session-expired") { + // The question was never sent, so say so and keep it: one button signs in and sends it, + // instead of a dead end that left the reader to find Sign in and retype (D26-05). + return ( + onSignInAndSend()} + className={cn(primaryControl, "text-xs")} + > +