+
{isWorkflowHeader ? (
<>
>
)}
-
);
}
diff --git a/src/lib/rag-candidate-sources.ts b/src/lib/rag-candidate-sources.ts
index 80e30acd..effed08f 100644
--- a/src/lib/rag-candidate-sources.ts
+++ b/src/lib/rag-candidate-sources.ts
@@ -44,13 +44,6 @@ import type { DocumentIndexUnitMatch, DocumentMemoryCard, SearchResult } from "@
// the floor, which is how the live schema drift (42702) went unnoticed. Log it structurally and,
// where telemetry is in scope, record the failing RPC + code so it shows up in rag_retrieval_logs.
export type SupabaseRpcError = { message?: string; code?: string; details?: string; hint?: string } | null;
-type RpcResult
= Promise<{ data: T | null; error: SupabaseRpcError }>;
-type AbortableRpc = RpcResult & {
- abortSignal?: (signal: AbortSignal) => RpcResult;
-};
-type SupabaseRpcClient = {
- rpc: (name: string, rpcArgs: Record) => AbortableRpc | PromiseLike;
-};
function legacyRankFields(versionedName: string) {
if (versionedName === "match_document_chunks_v2") return ["similarity"];
@@ -88,20 +81,15 @@ export async function callVersionedRetrievalRpc
versionedName: string,
legacyName: string,
args: Record,
- signal?: AbortSignal,
): Promise<{ data: T | null; error: SupabaseRpcError }> {
- const client = supabase as unknown as SupabaseRpcClient;
- const executeRpc = async (name: string, rpcArgs: Record) => {
- const pending = client.rpc(name, rpcArgs) as AbortableRpc;
- const pendingWithAbort =
- signal && typeof pending.abortSignal === "function" ? pending.abortSignal(signal) : pending;
- return await pendingWithAbort;
+ const client = supabase as unknown as {
+ rpc: (name: string, rpcArgs: Record) => Promise<{ data: T | null; error: SupabaseRpcError }>;
};
- const versioned = await executeRpc(versionedName, args);
+ const versioned = await client.rpc(versionedName, args);
if (versioned && !isMissingRetrievalRpcError(versioned.error)) return versioned;
const legacyArgs = { ...args };
delete legacyArgs.include_public;
- const ownerResult = await executeRpc(legacyName, legacyArgs);
+ const ownerResult = await client.rpc(legacyName, legacyArgs);
const ownerFilter = String(args.owner_filter ?? "");
if (
ownerResult.error ||
@@ -111,7 +99,7 @@ export async function callVersionedRetrievalRpc
) {
return ownerResult;
}
- const publicResult = await executeRpc(legacyName, {
+ const publicResult = await client.rpc(legacyName, {
...legacyArgs,
owner_filter: PUBLIC_OWNER_FILTER_SENTINEL,
});
diff --git a/src/lib/rag.ts b/src/lib/rag.ts
index 3e9e5a76..5cd3c449 100644
--- a/src/lib/rag.ts
+++ b/src/lib/rag.ts
@@ -2,7 +2,6 @@ import { createAdminClient } from "@/lib/supabase/admin";
import { retrievalAccessScopeForArgs, retrievalRpcScopeArgs } from "@/lib/owner-scope";
import {
callVersionedRetrievalRpc,
- createChunkLoadCache,
memoryCardChunkScore,
mergeSearchResults,
recordHybridRpcError,
@@ -120,11 +119,6 @@ export {
retrievalPlanCacheQuery,
} from "@/lib/rag-cache";
import { classifySearchCacheOutcome, recordCacheLookup } from "@/lib/observability/cache-metrics";
-import {
- recordAnswerOrigination,
- recordAnswerOriginationFinished,
- recordCoalescedAnswerWaiter,
-} from "@/lib/observability/answer-coalescing-metrics";
import { buildRagSourceBlock, compactContextText, neutralizeIdentityField } from "@/lib/rag-source-block";
export { buildRagSourceBlock, truncateForModel } from "@/lib/rag-source-block";
import {
@@ -428,26 +422,6 @@ function throwIfAborted(signal?: AbortSignal) {
}
}
-function awaitWithCallerSignal(pending: Promise, signal?: AbortSignal): Promise {
- if (!signal) return pending;
- if (signal.aborted) throw signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
-
- return new Promise((resolve, reject) => {
- const onAbort = () => reject(signal.reason ?? new DOMException("The operation was aborted.", "AbortError"));
- signal.addEventListener("abort", onAbort, { once: true });
- pending.then(
- (value) => {
- signal.removeEventListener("abort", onAbort);
- resolve(value);
- },
- (error) => {
- signal.removeEventListener("abort", onAbort);
- reject(error);
- },
- );
- });
-}
-
export type AnswerProgressEvent = {
stage:
| "retrieved"
@@ -1315,7 +1289,6 @@ export async function analyzeQueryWithClassifierFallback(
// owner_filter retrieval will use so grounding can never see documents retrieval cannot.
corpusGrounding?: { supabase: ReturnType; ownerFilter: string | null };
ownerId?: string | null;
- signal?: AbortSignal;
},
) {
if (
@@ -1387,16 +1360,10 @@ export async function analyzeQueryWithClassifierFallback(
}
try {
- const verdict = await awaitWithCallerSignal(pending, opts?.signal);
+ const verdict = await pending;
storeClassifierVerdictMemo(memoKey, verdict);
return applyClassifierVerdict(analysis, verdict);
- } catch (error) {
- if (
- error &&
- (error instanceof DOMException || typeof error === "object") &&
- (error as { name?: string }).name === "AbortError"
- )
- throw error;
+ } catch {
// Transport/parse failures are deliberately NOT memoized: fall back to the deterministic
// analysis for this request only, and let the next request retry the classifier.
return analysis;
@@ -2403,7 +2370,6 @@ export async function searchChunksWithTelemetry(args: SearchChunksArgs) {
// A3: shared across every withMemoryBoostedCandidates call in this request so the same
// owner/query memory cards are fetched at most once per (query, embedding-present, count).
const memoryCardCache: MemoryCardCache = new Map();
- const chunkLoadCache = createChunkLoadCache();
const documentRankingMetadataCache = createDocumentRankingMetadataCache();
const modeQueryClass = queryClassForClinicalMode(args.queryMode ?? "auto");
const documentFilterList = args.documentIds?.length
@@ -2430,7 +2396,6 @@ export async function searchChunksWithTelemetry(args: SearchChunksArgs) {
const queryAnalysis = await analyzeQueryWithClassifierFallback(retrievalQuery, analyzeClinicalQuery(retrievalQuery), {
corpusGrounding: corpusGroundingScope,
ownerId: args.ownerId,
- signal: args.signal,
});
throwIfAborted(args.signal);
if (modeQueryClass) queryAnalysis.queryClass = modeQueryClass;
@@ -3156,7 +3121,6 @@ export async function answerQuestionWithScope(args: AnswerQuestionWithScopeArgs)
let existing = inflightKey ? answerInflight.get(inflightKey) : undefined;
while (existing) {
- recordCoalescedAnswerWaiter();
await args.onProgress?.({
stage: "cached",
message: "Waiting for an identical cited answer request already in progress.",
@@ -3188,15 +3152,8 @@ export async function answerQuestionWithScope(args: AnswerQuestionWithScopeArgs)
}
}
- // Only coalescible requests belong in this process-local signal. Requests
- // that intentionally bypass cache/coalescing must not make a replica look
- // ineffective, and neither keys nor clinical content leave this function.
- if (inflightKey) recordAnswerOrigination();
const pending = answerQuestionWithScopeUncoalesced(args, startedAt).finally(() => {
- if (inflightKey) {
- answerInflight.delete(inflightKey);
- recordAnswerOriginationFinished();
- }
+ if (inflightKey) answerInflight.delete(inflightKey);
});
if (inflightKey) answerInflight.set(inflightKey, pending);
return pending;
diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json
index 40ebc996..ed2fe468 100644
--- a/supabase/drift-manifest.json
+++ b/supabase/drift-manifest.json
@@ -1,9 +1,9 @@
{
- "generated_at": "2026-07-17T22:10:14.366Z",
+ "generated_at": "2026-07-17T14:08:59.595Z",
"generator": "scripts/generate-drift-manifest.ts",
"postgres_image": "supabase/postgres:17.6.1.127",
- "schema_sha256": "302a9ecec8e69564d50035be8480fa5e5686ca6136d96978f2188ecf5fb58be1",
- "replay_seconds": 13,
+ "schema_sha256": "e93fe90cae38654e77b7be0f81e5a0c90b0c7db2fd37d76e32f4f52a089eff45",
+ "replay_seconds": 30,
"snapshot": {
"views": [
{
@@ -5290,12 +5290,6 @@
"table": "document_table_facts",
"def_hash": "9ac4c08564d3f549df99a1e543875cb8"
},
- {
- "def": "CREATE INDEX document_title_words_document_id_idx ON public.document_title_words USING btree (document_id)",
- "name": "document_title_words_document_id_idx",
- "table": "document_title_words",
- "def_hash": "003cf77523b819a06ff50cf8df6fcf4b"
- },
{
"def": "CREATE UNIQUE INDEX document_title_words_pkey ON public.document_title_words USING btree (word, document_id)",
"name": "document_title_words_pkey",
@@ -5356,12 +5350,6 @@
"table": "documents",
"def_hash": "fc27ae2194373897e2f316e1cac47fb0"
},
- {
- "def": "CREATE INDEX documents_registry_projection_lookup_idx ON public.documents USING btree (((metadata ->> 'registry_record_kind'::text)), ((metadata ->> 'registry_record_id'::text))) WHERE ((metadata ->> 'source_kind'::text) = 'registry_record'::text)",
- "name": "documents_registry_projection_lookup_idx",
- "table": "documents",
- "def_hash": "2c19fee4cac85c85d9ec69f179a6cb79"
- },
{
"def": "CREATE INDEX documents_search_idx ON public.documents USING gin (search_tsv)",
"name": "documents_search_idx",
@@ -6340,11 +6328,6 @@
"name": "documents_require_publication_approval",
"table": "documents"
},
- {
- "def": "CREATE TRIGGER documents_sync_title_words AFTER INSERT OR DELETE OR UPDATE OF title, status, owner_id ON public.documents FOR EACH ROW EXECUTE FUNCTION public.sync_document_title_words()",
- "name": "documents_sync_title_words",
- "table": "documents"
- },
{
"def": "CREATE TRIGGER documents_updated_at BEFORE UPDATE ON public.documents FOR EACH ROW EXECUTE FUNCTION public.set_updated_at()",
"name": "documents_updated_at",
@@ -6465,9 +6448,10 @@
},
{
"acl": [
- "postgres=X/postgres"
+ "postgres=X/postgres",
+ "service_role=X/postgres"
],
- "def_hash": "7104ee6e947fcca68fdebdf2fa878339",
+ "def_hash": "a0e9e41b95c9eeff7957dd292f3dd3fa",
"signature": "public.cleanup_registry_corpus_document()"
},
{
@@ -6525,14 +6509,6 @@
"def_hash": "e263f3819b05177abcf2eedd3f468991",
"signature": "public.consume_api_subject_rate_limit(text,text,integer,integer)"
},
- {
- "acl": [
- "postgres=X/postgres",
- "service_role=X/postgres"
- ],
- "def_hash": "833c00fa1743026d9269b9af28e0b86f",
- "signature": "public.consume_summary_rate_limits_atomic(uuid,text,integer,integer,integer,integer,integer,integer)"
- },
{
"acl": [
"postgres=X/postgres",
@@ -6554,7 +6530,7 @@
"postgres=X/postgres",
"service_role=X/postgres"
],
- "def_hash": "cb65883a561cb1f5cd2247213f417a41",
+ "def_hash": "a5ebdd1ec14008cdf0c8601a32b4d24c",
"signature": "public.correct_clinical_query_terms(text,real)"
},
{
@@ -6562,7 +6538,7 @@
"postgres=X/postgres",
"service_role=X/postgres"
],
- "def_hash": "a6f0e7c27da08e47e2c01dce4ab5d58d",
+ "def_hash": "675d521a0746befe09e68e47986c6355",
"signature": "public.default_privileges_status(text,text)"
},
{
@@ -7080,9 +7056,10 @@
},
{
"acl": [
- "postgres=X/postgres"
+ "postgres=X/postgres",
+ "service_role=X/postgres"
],
- "def_hash": "6b51ece12494d136b39977d8532c013c",
+ "def_hash": "582d35a5082ff0e4879db461294404fd",
"signature": "public.sync_document_title_words()"
},
{
@@ -7596,21 +7573,11 @@
"name": "document_title_words_document_id_fkey",
"table": "document_title_words"
},
- {
- "def": "CHECK ((word = lower(word)))",
- "name": "document_title_words_lowercase",
- "table": "document_title_words"
- },
{
"def": "PRIMARY KEY (word, document_id)",
"name": "document_title_words_pkey",
"table": "document_title_words"
},
- {
- "def": "CHECK (((length(word) >= 4) AND (length(word) <= 40)))",
- "name": "document_title_words_word_length",
- "table": "document_title_words"
- },
{
"def": "FOREIGN KEY (import_batch_id) REFERENCES public.import_batches(id) ON DELETE SET NULL",
"name": "documents_import_batch_id_fkey",
diff --git a/supabase/migrations/20260714180000_patch_rag_and_corrector_scalability.sql b/supabase/migrations/20260714180000_patch_rag_and_corrector_scalability.sql
index 1b37c57a..45d3e118 100644
--- a/supabase/migrations/20260714180000_patch_rag_and_corrector_scalability.sql
+++ b/supabase/migrations/20260714180000_patch_rag_and_corrector_scalability.sql
@@ -60,14 +60,12 @@ security definer
set search_path = public, pg_catalog, pg_temp
as $$
begin
- if TG_OP = 'DELETE' or
- (TG_OP = 'UPDATE' and OLD.status = 'indexed' and NEW.status is distinct from 'indexed') then
+ if TG_OP = 'DELETE' or (TG_OP = 'UPDATE' and OLD.status = 'indexed' and NEW.status <> 'indexed') then
delete from public.document_title_words where document_id = OLD.id;
end if;
- if (TG_OP = 'INSERT' and NEW.status = 'indexed') or
- (TG_OP = 'UPDATE' and NEW.status = 'indexed' and
- (OLD.status is distinct from 'indexed' or OLD.title is distinct from NEW.title)) then
+ if (TG_OP = 'INSERT' and NEW.status = 'indexed') or
+ (TG_OP = 'UPDATE' and NEW.status = 'indexed' and (OLD.status <> 'indexed' or OLD.title <> NEW.title)) then
if TG_OP = 'UPDATE' then
delete from public.document_title_words where document_id = NEW.id;
diff --git a/supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql b/supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql
index 1bff910f..c0523bf6 100644
--- a/supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql
+++ b/supabase/migrations/20260714190000_document_table_facts_trgm_idx.sql
@@ -1,9 +1,11 @@
--- Intentionally a no-op.
---
--- An earlier revision of this migration created
--- public.document_table_facts_text_trgm_idx, but
--- 20260717010000_harden_rag_scalability_patch.sql always drops that index.
--- Keeping a transactional CREATE here forced fresh replays to pay for a write-
--- blocking build that is immediately discarded. Environments that already
--- applied the CREATE still clean up via the harden migration's DROP INDEX IF EXISTS.
-select 1;
+-- Migration to add GIN trigram index on document_table_facts text fields for performance optimization
+create index if not exists document_table_facts_text_trgm_idx
+ on public.document_table_facts using gin (
+ lower(
+ coalesce(table_title, '') || ' ' ||
+ coalesce(row_label, '') || ' ' ||
+ coalesce(clinical_parameter, '') || ' ' ||
+ coalesce(threshold_value, '') || ' ' ||
+ coalesce(action, '')
+ ) extensions.gin_trgm_ops
+ );
diff --git a/tests/audit-content-services-regressions.test.ts b/tests/audit-content-services-regressions.test.ts
index 3a248013..d86cfb4c 100644
--- a/tests/audit-content-services-regressions.test.ts
+++ b/tests/audit-content-services-regressions.test.ts
@@ -71,14 +71,10 @@ describe("content and services audit regressions", () => {
expect(canCompareServices([])).toBe(false);
expect(canCompareServices(records.slice(0, 1))).toBe(false);
expect(canCompareServices(records.slice(0, 2))).toBe(true);
- expect(normalizedServiceNavigatorSource).not.toContain(
+ expect(normalizedServiceNavigatorSource).toContain(
'key={selected.length === 0 ? "empty" : selected.length === 1 ? "single" : "multiple"}',
);
expect(serviceNavigatorSource).not.toContain("useEffect(");
- expect(serviceNavigatorSource).toContain("const checklistExpanded = showChecklistDetails && selected.length > 0");
- expect(serviceNavigatorSource).toContain("const comparisonExpanded = showComparison && comparisonAvailable");
- expect(serviceNavigatorSource).toContain("if (remainingCount === 0) setShowChecklistDetails(false)");
- expect(serviceNavigatorSource).toContain("if (remainingCount < 2) setShowComparison(false)");
expect(serviceNavigatorSource).toContain("aria-pressed={selected}");
expect(serviceNavigatorSource).toContain("Add ${service.title} to comparison");
expect(serviceNavigatorSource).toContain("Remove ${service.title} from comparison");
@@ -112,24 +108,28 @@ describe("content and services audit regressions", () => {
expect(transport).not.toBeNull();
expect(transport).toMatchObject({
- verification: { locallyVerified: false, confidence: "Medium" },
+ verification: { locallyVerified: false, confidence: "Unknown" },
source: {
- label: "Office of the Chief Psychiatrist WA — approved MHA 2014 forms",
- status: "Source checked",
+ label: "Transport form workflow entry",
+ status: "Local source confirmation required",
},
});
- expect(transport?.source).toHaveProperty("url");
- expect(transport?.source).toHaveProperty("reviewed");
+ expect(transport?.source).not.toHaveProperty("url");
+ expect(transport?.source).not.toHaveProperty("published");
+ expect(transport?.source).not.toHaveProperty("reviewed");
expect(transport?.source).not.toHaveProperty("pages");
expect(transport?.source).not.toHaveProperty("pageCount");
expect(transport?.source).not.toHaveProperty("reviewDue");
- expect(JSON.stringify(transport?.source)).not.toMatch(/\b\d+\s+pages?\b|\bstatutory\b/i);
- expect(formDetailSource).not.toMatch(/\b\d+\s+pages?\b|\bReview due\b/i);
+ expect(JSON.stringify(transport?.source)).not.toMatch(/\.pdf\b|\b\d+\s+pages?\b|\bact sections?\b|\bstatutory\b/i);
+ expect(formDetailSource).not.toMatch(/\.pdf\b|\b\d+\s+pages?\b|\bAct sections?\b|\bReview due\b/i);
expect(formDetailSource).not.toContain("01 May 2026");
- expect(formDetailSource).not.toMatch(/5\(2\)|Admission order|Treatment order/);
- expect(formDetailSource).toContain("Full pathway unavailable");
+ expect(formDetailSource).not.toMatch(/\b(?:1A|3A|4A|4B)\b|5\(2\)|Admission order|Treatment order/);
+ expect(formDetailSource).not.toMatch(
+ /Pathway navigation is not available yet|Full pathway unavailable|>Source info,
+ );
+ expect(formDetailSource).toContain("No linked full pathway is available for this record.");
expect(normalizedFormDetailSource).toContain(
- 'label: "Source currency", value: displayText(form.source?.reviewed, "Review locally")',
+ '...(hasText(form.source?.reviewed) ? [{ icon: CalendarDays, label: "Source review", value: form.source.reviewed.trim() }] : [])',
);
for (const form of formRecords) {
@@ -140,15 +140,19 @@ describe("content and services audit regressions", () => {
expect(form.source?.reviewed, form.slug).toBeUndefined();
}
- expect(formsSearchSource).toContain("Title or content match");
- expect(formsSearchSource).toContain("Content match in related pathway");
- expect(formsSearchSource).toContain("View all forms");
+ expect(formsSearchSource).not.toMatch(
+ /\b(?:1A|3A|4A|4B)\b|Evidence 278|Pathways 12|Tasks 8|PSOLIS|Source verified|Official source|Aligned to MHA|Open account setup|View full pathway|Filter controls are coming soon/,
+ );
+ expect(formsSearchSource).toContain("statusToneClass(chip.tone)");
+ expect(formsSearchSource).toContain("Title or identifier match");
+ expect(formsSearchSource).toContain("Match in form record details");
+ expect(formsSearchSource).toContain("Browse all forms");
expect(formsHomeSource).not.toMatch(/Source verified|Open account setup/);
expect(formsHomeSource).not.toMatch(
/Number, pathway, clock|Maker, clock, copies|Browse pathways|Before, current, parallel, after|starter set of MHA 2014 forms|follow a pathway/,
);
- expect(formsHomeSource).toContain("local confirmation");
- expect(formsHomeSource).toContain("Source catalogue reviewed");
+ expect(formsHomeSource).toContain("Local confirmation required");
+ expect(formsHomeSource).toContain("form records confirmed");
});
it("does not render negative or text-only source statuses as verified", () => {
@@ -202,12 +206,14 @@ describe("content and services audit regressions", () => {
});
it("claims and renders a form source link only when the record has a URL", () => {
- expect(normalizedFormDetailSource).toContain("sourceHref={form.source?.url ?? null}");
- expect(normalizedFormDetailSource).toContain("href={form.source.url}");
- expect(normalizedFormDetailSource).toContain('target="_blank"');
- expect(normalizedFormDetailSource).toContain('rel="noopener noreferrer"');
- expect(normalizedFormDetailSource).toContain("inline-flex min-h-10");
- expect(formDetailSource).toContain("Source link pending");
- expect(formDetailSource).toContain("Official");
+ expect(normalizedFormDetailSource).toContain(
+ '{form.source?.url ? "Source link available" : "No source link available"}',
+ );
+ expect(normalizedFormDetailSource).toMatch(/\{form\.source\?\.url \? \( {
"https://clinical-kb.test/differentials/presentations/acute-confusion-encephalopathy?q=acute+confusion&ids=delirium",
);
- const presentationsEmptyQueryFallback = redirectPresentations(
- new NextRequest("https://clinical-kb.test/differentials/presentations?query=%20%20&q=delirium"),
- );
- expect(presentationsEmptyQueryFallback.status).toBe(307);
- expect(presentationsEmptyQueryFallback.headers.get("location")).toBe(
- "https://clinical-kb.test/differentials/presentations/acute-confusion-encephalopathy?q=delirium",
- );
-
const medications = redirectMedications(new NextRequest("https://clinical-kb.test/medications?ignored=1"));
expect(medications.status).toBe(307);
expect(medications.headers.get("location")).toBe("https://clinical-kb.test/?mode=prescribing");
@@ -100,22 +92,21 @@ describe("audit navigation and auth regressions", () => {
it("gates private polling and mutations on local readiness plus authenticated status", () => {
const privateCapabilityContract = sourceSegment(
clinicalDashboardSource,
- "const canUsePrivateApis =",
+ "// Local/demo guests can read the public library",
"const canRunSearch =",
);
- expect(privateCapabilityContract).toContain("const canUsePrivateApis =");
expect(privateCapabilityContract).toContain(
- 'localNoAuthMode || localDevCanAttemptPrivateApis || authStatus === "authenticated"',
+ 'const canUsePrivateApis = localProjectReady && authStatus === "authenticated";',
);
+ expect(privateCapabilityContract).not.toMatch(/localNoAuth|clientDemoMode/);
const pollingContract = sourceSegment(
clinicalDashboardSource,
- "if (!nextDemoMode && !canUsePrivateApis) {",
"const shouldRefreshWorkState =",
+ "const [documentsResponse",
);
- expect(pollingContract).toContain("if (!nextDemoMode && !canUsePrivateApis) {");
- expect(pollingContract).toContain("setDocuments([]);");
- expect(pollingContract).toContain("return;");
+ expect(pollingContract).toContain("(canUsePrivateApis || serverDemoMode)");
+ expect(pollingContract).not.toMatch(/localNoAuth|clientDemoMode/);
const labelMutationContract = sourceSegment(
clinicalDashboardSource,
@@ -132,8 +123,8 @@ describe("audit navigation and auth regressions", () => {
expect(uploadMutationContract).toContain("if (!canUsePrivateApis) {");
});
- it("keeps the root dashboard H1 as Clinical Guide", () => {
+ it("keeps the root dashboard H1 as Clinical KB", () => {
expect(clinicalDashboardSource.match(/\s*Clinical Guide\s*<\/h1>/);
+ expect(clinicalDashboardSource).toMatch(/\s*Clinical KB\s*<\/h1>/);
});
});
diff --git a/tests/site-map.test.ts b/tests/site-map.test.ts
index 71ea01db..4c28fe5d 100644
--- a/tests/site-map.test.ts
+++ b/tests/site-map.test.ts
@@ -97,6 +97,40 @@ describe("tracked sitemap", () => {
}
});
+ it("keeps public redirect handlers in product routes and API handlers in the API section", () => {
+ const data = collectSiteMapData();
+ const productSection = siteMap.slice(
+ siteMap.indexOf("## Main product routes"),
+ siteMap.indexOf("## Mode/query routes"),
+ );
+ const apiSection = siteMap.slice(siteMap.indexOf("## API routes"), siteMap.indexOf("## Redirects"));
+ const expectedProductHandlers = [
+ ["/applications", "src/app/applications/route.ts", "/tools"],
+ [
+ "/differentials/presentations",
+ "src/app/differentials/presentations/route.ts",
+ "/differentials/presentations/[workflow-slug]",
+ ],
+ ["/medications", "src/app/medications/route.ts", "/?mode=prescribing"],
+ ] as const;
+
+ expect(data.apiRoutes.every((route) => route.route === "/api" || route.route.startsWith("/api/"))).toBe(true);
+ expect(data.publicRouteHandlers.some((route) => route.route === "/auth/callback")).toBe(true);
+ expect(data.publicRouteHandlers).toContainEqual({
+ route: "/icons/[variant]",
+ file: "src/app/icons/[variant]/route.tsx",
+ });
+ expect(apiSection).not.toContain("`/icons/[variant]`");
+
+ for (const [route, file, target] of expectedProductHandlers) {
+ expect(data.publicRouteHandlers).toContainEqual({ route, file });
+ expect(data.apiRoutes).not.toContainEqual({ route, file });
+ expect(data.redirects).toContainEqual({ route, file, target });
+ expect(productSection).toContain(`\`${route}\``);
+ expect(apiSection).not.toContain(`\`${route}\``);
+ }
+ });
+
it("documents seeded dynamic slugs", () => {
for (const service of serviceRecords) expectDocumentedRoute(service.slug);
for (const form of formRecords) expectDocumentedRoute(form.slug);
diff --git a/tests/supabase-schema.test.ts b/tests/supabase-schema.test.ts
index 33a3f03a..c0f6dcfd 100644
--- a/tests/supabase-schema.test.ts
+++ b/tests/supabase-schema.test.ts
@@ -129,7 +129,7 @@ const deleteDocumentIfIdleMigration = readFileSync(
"utf8",
).replace(/\s+/g, " ");
const defaultAclAssertionMigration = readFileSync(
- new URL("../supabase/migrations/20260717173000_reassert_supabase_admin_default_privileges.sql", import.meta.url),
+ new URL("../supabase/migrations/20260717161000_assert_supabase_admin_default_privileges.sql", import.meta.url),
"utf8",
).replace(/\s+/g, " ");
const defaultAclRoleBootstrap = readFileSync(new URL("../supabase/roles.sql", import.meta.url), "utf8").replace(
@@ -1219,7 +1219,7 @@ describe("Supabase Preview replay guards", () => {
const migrationFiles = readdirSync(migrationDirectoryUrl)
.filter((fileName) => /^\d+_.+\.sql$/.test(fileName))
.sort();
- expect(migrationFiles.at(-1)).toBe("20260717173000_reassert_supabase_admin_default_privileges.sql");
+ expect(migrationFiles.at(-1)).toBe("20260717161000_assert_supabase_admin_default_privileges.sql");
});
it("bootstraps safe default ACLs before fresh local and preview migration replay", () => {
@@ -1369,10 +1369,9 @@ describe("Supabase Preview replay guards", () => {
"create or replace function public.cleanup_registry_corpus_document()",
"revoke execute on function public.cleanup_registry_corpus_document()",
);
- const cleanupLower = cleanup.toLowerCase();
- expect(cleanupLower).toContain("metadata->>'registry_record_id' = old.id::text");
- expect(cleanupLower).toContain("metadata->>'registry_record_kind' = case tg_table_name");
- expect(cleanupLower).toMatch(/when 'clinical_registry_records' then (pg_catalog\.)?to_jsonb\(old\)->>'kind'/);
+ expect(cleanup).toContain("metadata->>'registry_record_id' = old.id::text");
+ expect(cleanup).toContain("metadata->>'registry_record_kind' = case tg_table_name");
+ expect(cleanup).toContain("when 'clinical_registry_records' then to_jsonb(old)->>'kind'");
expect(cleanup).toContain("when 'medication_records' then 'medication'");
expect(cleanup).toContain("when 'differential_records' then 'differential'");
expect(cleanup).not.toContain("registry_record_id')::uuid");
@@ -1396,10 +1395,8 @@ describe("Supabase Preview replay guards", () => {
expect(corrector).toContain("lower(canonical) % tok");
expect(corrector).toContain("word % tok");
expect(corrector).toContain("limit 32");
- expect(corrector).toContain("best is not null and best_sim >= min_sim");
- if (corrector.includes("min_sim is null")) {
- expect(corrector).toContain("min_sim is null or min_sim < 0.3 or min_sim > 1");
- }
+ expect(corrector).toContain("set pg_trgm.similarity_threshold = 0.3");
+ expect(corrector).toContain("min_sim is null or min_sim < 0.3 or min_sim > 1");
expect(corrector).not.toContain("array_agg(distinct term)");
expect(corrector).not.toContain("unnest(vocab)");
expect(sql).toContain("rag_aliases_canonical_trgm_idx");
@@ -1417,11 +1414,7 @@ describe("Supabase Preview replay guards", () => {
"$function$;",
);
- // Fresh installs skip the write-blocking CREATE that harden immediately drops.
- expect(documentTableFactsTrgmMigration).not.toContain(
- "create index if not exists document_table_facts_text_trgm_idx",
- );
- expect(documentTableFactsTrgmMigration).toMatch(/intentionally a no-op/i);
+ expect(documentTableFactsTrgmMigration).toContain("create index if not exists document_table_facts_text_trgm_idx");
expect(hardenRagScalabilityPatchMigration).toContain(
"drop index if exists public.document_table_facts_text_trgm_idx",
);
diff --git a/tests/ui-accessibility.spec.ts b/tests/ui-accessibility.spec.ts
index e18ffd1c..6be58fad 100644
--- a/tests/ui-accessibility.spec.ts
+++ b/tests/ui-accessibility.spec.ts
@@ -48,7 +48,7 @@ async function expectNoPageHorizontalOverflow(page: Page) {
}
async function expectDashboardUsable(page: Page) {
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toHaveCount(1);
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toHaveCount(1);
await expect(page.getByRole("heading", { name: "Answer" })).toBeVisible();
await expect(page.locator('[aria-label^="Search indexed guidelines by question or keyword"]:visible')).toBeVisible();
await expect(page.getByRole("button", { name: "Open answer options" })).toBeVisible();
diff --git a/tests/ui-smoke.spec.ts b/tests/ui-smoke.spec.ts
index f8468792..21cee432 100644
--- a/tests/ui-smoke.spec.ts
+++ b/tests/ui-smoke.spec.ts
@@ -873,7 +873,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await gotoApp(page, "/");
await waitForDemoDashboardReady(page);
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toHaveCount(1);
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toHaveCount(1);
await expect(page.getByRole("heading", { name: "Answer" })).toBeVisible();
await expect(visibleQuestionInput(page)).toBeVisible();
await expect(page.getByRole("button", { name: "Generate source-backed answer" })).toHaveText(/^\s*Ask\s*$/);
@@ -1268,7 +1268,7 @@ test.describe("Clinical KB UI smoke coverage", () => {
await expect(page.getByRole("button", { name: "Generate source-backed answer" })).toBeEnabled();
await expect(page.getByTestId("answer-grounding-chip")).toHaveCount(0);
expect(answerRequests).toEqual([]);
- await expect(page.getByRole("heading", { level: 1, name: "Clinical Guide" })).toBeVisible();
+ await expect(page.getByRole("heading", { level: 1, name: "Clinical KB" })).toBeVisible();
await expectDomIntegrity(page, { mobileNav: true, mobileFabReady: false });
await expectNoPageHorizontalOverflow(page);
});
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index 98dccb93..c20855b1 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -885,7 +885,7 @@ test.describe("Clinical KB tools launcher", () => {
await expectNoPageHorizontalOverflow(page);
});
- test("forms mode shows source-backed form records in search results", async ({ page }) => {
+ test("forms mode shows registry-backed form records without unsupported pathway claims", async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 900 });
await mockAnswerDashboardApi(page);
await gotoLauncher(page, "/forms?q=transport%20forms&focus=1&run=1");
@@ -905,6 +905,9 @@ test.describe("Clinical KB tools launcher", () => {
await expect(
page.getByTestId("form-search-result-transport-crisis-form").getByLabel("Open Transport order"),
).toHaveAttribute("href", "/forms/transport-crisis-form");
+ await expect(page.getByRole("button", { name: "Refine" })).toHaveCount(0);
+ await expect(page.getByText(/Evidence 278|Pathways 12|Tasks 8|Source verified|Aligned to MHA 2014/)).toHaveCount(0);
+ await expect(page.getByText(/PSOLIS Transport|View full pathway/)).toHaveCount(0);
await expect(page.getByTestId("service-search-results")).toHaveCount(0);
await expectNoPageHorizontalOverflow(page);
});
@@ -1005,6 +1008,7 @@ test.describe("Clinical KB tools launcher", () => {
await expect(page.getByTestId("form-search-mobile-results")).toBeVisible();
await expect(page.getByTestId("form-search-mobile-result-transport-crisis-form")).toContainText("Transport order");
+ await expect(page.getByText(/PSOLIS Transport|View full pathway|Source verified/)).toHaveCount(0);
await expect(visibleGlobalSearchInput(page)).toHaveValue("transport");
await expectNoPageHorizontalOverflow(page);
});
@@ -1017,6 +1021,19 @@ test.describe("Clinical KB tools launcher", () => {
const dock = page.locator("form.answer-footer-search-dock");
await expect(dock).toBeVisible();
await expect(dock).not.toHaveAttribute("data-scroll-hidden", "true");
+ const transition = await dock.evaluate((node) => {
+ const style = window.getComputedStyle(node);
+ const durationMs = Math.max(
+ ...style.transitionDuration.split(",").map((value) => {
+ const normalized = value.trim();
+ const duration = Number.parseFloat(normalized);
+ return normalized.endsWith("ms") ? duration : duration * 1000;
+ }),
+ );
+ return { durationMs, property: style.transitionProperty };
+ });
+ expect(transition.property).toMatch(/transform|all/);
+ expect(transition.durationMs).toBeGreaterThanOrEqual(100);
// focus=1 leaves the composer focused; hide-on-scroll stays off while it has focus.
const input = visibleGlobalSearchInput(page).first();
From fac9daaca38fc481cf4cc968e44219afd671ce56 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sat, 18 Jul 2026 15:08:13 +0800
Subject: [PATCH 3/4] fix: stop Escape focus restore from closing the app-mode
menu (#822)
* fix: stop Escape focus restore from closing the app-mode menu
Scope dismiss deferred a focus restore onto the answer-options trigger.
When a mode-menu open landed in the same frame window, that restore stole
focus, blur-dismissed the menu, and flaked the Documents mode switch in
Production UI CI. Skip restore when the mode menu is open or focus already
moved, and wait for the scope popover to hide before opening the menu.
Co-authored-by: BigSimmo
* fix(test): fail closed when scope popover does not dismiss
Only wait for the scope popover when it is visible, and let that wait
throw if dismissal times out so the mode-menu open cannot recreate the
Escape focus-restore race.
Co-authored-by: BigSimmo
---------
Co-authored-by: Cursor Agent
Co-authored-by: BigSimmo
---
.../clinical-dashboard/master-search-header.tsx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/components/clinical-dashboard/master-search-header.tsx b/src/components/clinical-dashboard/master-search-header.tsx
index 40e793b2..77670a7d 100644
--- a/src/components/clinical-dashboard/master-search-header.tsx
+++ b/src/components/clinical-dashboard/master-search-header.tsx
@@ -33,7 +33,7 @@ import {
import { DocumentTagCloud } from "@/components/DocumentTagCloud";
import { PrivacyInputNotice } from "@/components/privacy-input-notice";
-import { useDismissableLayer } from "@/components/use-dismissable-layer";
+import { restoreFocusUnlessMoved, useDismissableLayer } from "@/components/use-dismissable-layer";
import { useHideOnScroll } from "@/components/clinical-dashboard/use-hide-on-scroll";
import { AnswerFollowUpSuggestions } from "@/components/clinical-dashboard/answer-follow-up-suggestions";
import {
@@ -747,13 +747,15 @@ export function MasterSearchHeader({
if (scopeOpen || !restoreActionMenuFocusRef.current) return;
restoreActionMenuFocusRef.current = false;
window.requestAnimationFrame(() => {
- actionMenuTriggerRef.current?.focus({ preventScroll: true });
+ restoreFocusUnlessMoved(actionMenuTriggerRef.current);
});
}, [scopeOpen]);
const closeScopeSheet = useCallback(() => {
setScopeSheetOpen(false);
- window.requestAnimationFrame(() => actionMenuTriggerRef.current?.focus());
+ window.requestAnimationFrame(() => {
+ restoreFocusUnlessMoved(actionMenuTriggerRef.current);
+ });
}, []);
useEffect(() => {
From 546e26ef7396ef27ec4c3d70185c183a1c2154f4 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sat, 18 Jul 2026 08:11:27 +0000
Subject: [PATCH 4/4] fix: remove duplicate lazy PDF viewer declarations
A merge left PdfCanvasViewer/NativePdfEmbed/PdfPreviewLoading declared
twice in DocumentViewer, which broke typecheck and the production build.
Co-authored-by: BigSimmo
---
src/components/DocumentViewer.tsx | 28 ----------------------------
1 file changed, 28 deletions(-)
diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx
index d37baa3f..05f21b30 100644
--- a/src/components/DocumentViewer.tsx
+++ b/src/components/DocumentViewer.tsx
@@ -133,34 +133,6 @@ function PdfPreviewLoading() {
);
}
-// pdf-canvas-viewer is only needed after a source document has loaded and the
-// user is viewing a PDF. Keeping it out of the document route's initial client
-// chunk avoids parsing its reader controls for image, text, and download-only
-// documents. pdf.js itself remains loaded on demand by that component.
-const PdfCanvasViewer = dynamic(
- () => import("@/components/document-viewer/pdf-canvas-viewer").then((module) => module.PdfCanvasViewer),
- {
- ssr: false,
- loading: () => ,
- },
-);
-const NativePdfEmbed = dynamic(
- () => import("@/components/document-viewer/pdf-canvas-viewer").then((module) => module.NativePdfEmbed),
- { ssr: false, loading: () => },
-);
-
-function PdfPreviewLoading() {
- return (
-
- Loading PDF reader…
-
- );
-}
-
type PageRow = {
id: string;
page_number: number;