Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ export default defineConfig({
grepInvert: mockupTag,
use: {
...devices["Desktop Chrome"],
contextOptions: {
reducedMotion: "no-preference",
},
...(chromiumExecutablePath ? { launchOptions: { executablePath: chromiumExecutablePath } } : {}),
},
},
Expand Down
55 changes: 11 additions & 44 deletions supabase/drift-manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"generated_at": "2026-07-18T08:58:41.495Z",
"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": 18,
"schema_sha256": "e93fe90cae38654e77b7be0f81e5a0c90b0c7db2fd37d76e32f4f52a089eff45",
"replay_seconds": 30,
"snapshot": {
"views": [
{
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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()"
},
{
Expand Down Expand Up @@ -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",
Expand All @@ -6554,15 +6530,15 @@
"postgres=X/postgres",
"service_role=X/postgres"
],
"def_hash": "cb65883a561cb1f5cd2247213f417a41",
"def_hash": "a5ebdd1ec14008cdf0c8601a32b4d24c",
"signature": "public.correct_clinical_query_terms(text,real)"
},
{
"acl": [
"postgres=X/postgres",
"service_role=X/postgres"
],
"def_hash": "a6f0e7c27da08e47e2c01dce4ab5d58d",
"def_hash": "675d521a0746befe09e68e47986c6355",
"signature": "public.default_privileges_status(text,text)"
},
{
Expand Down Expand Up @@ -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()"
},
{
Expand Down Expand Up @@ -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",
Expand Down
67 changes: 30 additions & 37 deletions tests/audit-content-services-regressions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,61 +107,52 @@
const transport = getFormRecord("transport-crisis-form");

expect(transport).not.toBeNull();
expect(transport).toMatchObject({

Check failure on line 110 in tests/audit-content-services-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-content-services-regressions.test.ts > content and services audit regressions > keeps seeded form provenance explicitly unverified and free of invented source facts

AssertionError: expected { Object (slug, title, ...) } to match object { verification: { …(2) }, …(1) } (66 matching properties omitted from actual) - Expected + Received { "source": { - "label": "Transport form workflow entry", - "status": "Local source confirmation required", + "label": "Office of the Chief Psychiatrist WA — approved MHA 2014 forms", + "status": "Source checked", }, "verification": { - "confidence": "Unknown", + "confidence": "Medium", "locallyVerified": false, }, } ❯ tests/audit-content-services-regressions.test.ts:110:23

Check failure on line 110 in tests/audit-content-services-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-content-services-regressions.test.ts > content and services audit regressions > keeps seeded form provenance explicitly unverified and free of invented source facts

AssertionError: expected { Object (slug, title, ...) } to match object { verification: { …(2) }, …(1) } (66 matching properties omitted from actual) - Expected + Received { "source": { - "label": "Transport form workflow entry", - "status": "Local source confirmation required", + "label": "Office of the Chief Psychiatrist WA — approved MHA 2014 forms", + "status": "Source checked", }, "verification": { - "confidence": "Unknown", + "confidence": "Medium", "locallyVerified": false, }, } ❯ tests/audit-content-services-regressions.test.ts:110:23
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).not.toHaveProperty("url");
expect(transport?.source).not.toHaveProperty("published");
expect(transport?.source).toHaveProperty("reviewed");
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)).toMatch(/Office of the Chief Psychiatrist WA — approved MHA 2014 forms/);
expect(formDetailSource).not.toMatch(/Review due/i);
expect(formDetailSource).not.toMatch(/Admission order|Treatment order|5\(2\)/);
expect(formDetailSource).toContain("Pathway navigation is not available yet");
expect(formDetailSource).toContain("Full pathway unavailable");
expect(formDetailSource).toContain(">Source info</");
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(/\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) {
if (form.source?.url) continue;
expect(form.verification?.locallyVerified, form.slug).toBe(false);
expect(form.verification?.confidence, form.slug).toBe("Unknown");
expect(form.source?.status, form.slug).toMatch(/confirmation required|source checked/i);
expect(form.source?.status, form.slug).toMatch(/confirmation required/i);
expect(form.source?.reviewed, form.slug).toBeUndefined();
}

expect(formsSearchSource).toContain("const sourceSnippetCount = 278;");
expect(formsSearchSource).toContain("const taskCount = 8;");
expect(formsSearchSource).toContain("const pathwayCount = 12;");
expect(formsSearchSource).toContain('["Evidence", sourceSnippetCount]');
expect(formsSearchSource).toContain('["Pathways", pathwayCount]');
expect(formsSearchSource).toContain('["Tasks", taskCount]');
expect(formsSearchSource).toContain("PSOLIS");
expect(formsSearchSource).toContain("Source verified");
expect(formsSearchSource).toContain("Official source");
expect(formsSearchSource).toContain("Aligned to MHA 2014");
expect(formsSearchSource).toContain("Open account setup");
expect(formsSearchSource).toContain("View full pathway");
expect(formsSearchSource).toContain("Filter controls are coming soon");
expect(formsSearchSource).toContain("tagToneClass(chipLabel)");
expect(formsSearchSource).toContain("Title or content match");
expect(formsSearchSource).toContain("Content match in related pathway");
expect(formsSearchSource).toContain("Content match in the forms catalogue");
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("need 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", () => {
Expand Down Expand Up @@ -215,12 +206,14 @@
});

it("claims and renders a form source link only when the record has a URL", () => {
expect(normalizedFormDetailSource).toContain("form.source?.url ? (");
expect(normalizedFormDetailSource).toContain(

Check failure on line 209 in tests/audit-content-services-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-content-services-regressions.test.ts > content and services audit regressions > claims and renders a form source link only when the record has a URL

AssertionError: expected '"use client"; import { useRouter } fr…' to contain '{form.source?.url ? "Source link avai…' - Expected + Received - {form.source?.url ? "Source link available" : "No source link available"} + "use client"; import { useRouter } from "next/navigation"; import { ArrowLeft, Bookmark, BookmarkCheck, CalendarDays, CircleCheck, ChevronRight, Clipboard, ClipboardList, Clock3, Download, ExternalLink, FileText, Info, MapPin, Navigation, Phone, Route, Scale, ShieldCheck, Tag, UserRound, X, CircleX, type LucideIcon, } from "lucide-react"; import { useMemo, useState, type ReactNode } from "react"; import { cn, codeText, floatingControl, metadataPill, pageContainer, primaryControl, textMuted, toneDanger, toneInfo, toneNeutral, toneSuccess, toneWarning, } from "@/components/ui-primitives"; import { FormCodeBadge, splitFormCode } from "@/components/forms/form-code-badge"; import { appModeHomeHref } from "@/lib/app-modes"; import { formCatalogDetails, type FormRecord } from "@/lib/form-ranker"; import type { ServiceChipTone, ServiceContact, ServiceCriterion, ServiceSummaryCard } from "@/lib/service-ranker"; import { readSavedRegistrySlugs, savedFormsStorageKey, writeSavedRegistrySlugs } from "@/lib/saved-registry-storage"; const missingText = "Not listed"; function hasText(value: string | null | undefined): value is string { return Boolean(value && value.trim().length > 0); } function displayText(value: string | null | undefined, fallback = missingText) { return hasText(value) ? value.trim() : fallback; } async function copyText(value: string) { if (navigator.clipboard?.writeText) { try { await navigator.clipboard.writeText(value); return; } catch { // Fall through to the legacy selection path for restricted browser contexts. } } const textArea = document.createElement("textarea"); textArea.value = value; textArea.setAttribute("readonly", ""); textArea.style.position = "fixed"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.select(); try { const copied = document.execCommand?.("copy"); if (copied === false) throw new Error("copy command rejected"); } finally { document.body.removeChild(textArea); } } function chipToneClass(tone: ServiceChipTone | null | undefined) { if (tone === "danger") return toneDanger; if (tone === "info") return toneInfo; if (tone === "warning") return toneWarning; if (tone === "success") return toneSuccess; return toneNeutral; } export function sourceToneClass(form: FormRecord) { const status = form.source?.status?.toLowerCase() ?? ""; if (/required|review|unverified|not verified|unchecked|pending|unknown|confirm/.test(status)) return toneWarning; if (form.verification?.locallyVerified === true) return toneSuccess; return toneNeutral; } function formCode(form: FormRecord) { const details = formCatalogDetails(form); if (details?.form) return details.form; if (form.slug.includes("transport")) return "4A"; if (form.slug.includes("capacity")) return "CAP"; if (form.slug.includes("clozapine")) return "CLZ"; if (form.slug.includes("handover")) return "SAFE"; return form.title .split(/\s+/) .map((part) => part[0]) .join("") .slice(0, 4) .toUpperCase(); } function formShortTitle(form: FormRecord) { const details = formCatalogDetails(form); return details?.form ? `Form ${details.form}` : displayText(form.catalogueLabel, "Form"); } function summaryIcon(card: ServiceSummaryCard) { const label = `${card.id} ${card.label} ${card.title}`.toLowerCase(); const Icon = label.includes("clock") ? Clock3 : label.includes("destination") || label.includes("place") || label.includes("route") ? MapPin : label.includes("authority") || label.includes("maker") ? UserRound : label.includes("criteria") || label.includes("threshold") ? Scale : ClipboardList; return <Icon className="h-5 w-5" aria-hidden />; } function summaryCardsFor(form: FormRecord): ServiceSummaryCard[] { if (form.summaryCards?.length) return form.summaryCards.slice(0, 4); return [ { id: "route", label: "Route", title: "Use pathway", detail: form.route }, { id: "eligibility", label: "Eligibility", title: "Patient fi

Check failure on line 209 in tests/audit-content-services-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-content-services-regressions.test.ts > content and services audit regressions > claims and renders a form source link only when the record has a URL

AssertionError: expected '"use client"; import { useRouter } fr…' to contain '{form.source?.url ? "Source link avai…' - Expected + Received - {form.source?.url ? "Source link available" : "No source link available"} + "use client"; import { useRouter } from "next/navigation"; import { ArrowLeft, Bookmark, BookmarkCheck, CalendarDays, CircleCheck, ChevronRight, Clipboard, ClipboardList, Clock3, Download, ExternalLink, FileText, Info, MapPin, Navigation, Phone, Route, Scale, ShieldCheck, Tag, UserRound, X, CircleX, type LucideIcon, } from "lucide-react"; import { useMemo, useState, type ReactNode } from "react"; import { cn, codeText, floatingControl, metadataPill, pageContainer, primaryControl, textMuted, toneDanger, toneInfo, toneNeutral, toneSuccess, toneWarning, } from "@/components/ui-primitives"; import { FormCodeBadge, splitFormCode } from "@/components/forms/form-code-badge"; import { appModeHomeHref } from "@/lib/app-modes"; import { formCatalogDetails, type FormRecord } from "@/lib/form-ranker"; import type { ServiceChipTone, ServiceContact, ServiceCriterion, ServiceSummaryCard } from "@/lib/service-ranker"; import { readSavedRegistrySlugs, savedFormsStorageKey, writeSavedRegistrySlugs } from "@/lib/saved-registry-storage"; const missingText = "Not listed"; function hasText(value: string | null | undefined): value is string { return Boolean(value && value.trim().length > 0); } function displayText(value: string | null | undefined, fallback = missingText) { return hasText(value) ? value.trim() : fallback; } async function copyText(value: string) { if (navigator.clipboard?.writeText) { try { await navigator.clipboard.writeText(value); return; } catch { // Fall through to the legacy selection path for restricted browser contexts. } } const textArea = document.createElement("textarea"); textArea.value = value; textArea.setAttribute("readonly", ""); textArea.style.position = "fixed"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.select(); try { const copied = document.execCommand?.("copy"); if (copied === false) throw new Error("copy command rejected"); } finally { document.body.removeChild(textArea); } } function chipToneClass(tone: ServiceChipTone | null | undefined) { if (tone === "danger") return toneDanger; if (tone === "info") return toneInfo; if (tone === "warning") return toneWarning; if (tone === "success") return toneSuccess; return toneNeutral; } export function sourceToneClass(form: FormRecord) { const status = form.source?.status?.toLowerCase() ?? ""; if (/required|review|unverified|not verified|unchecked|pending|unknown|confirm/.test(status)) return toneWarning; if (form.verification?.locallyVerified === true) return toneSuccess; return toneNeutral; } function formCode(form: FormRecord) { const details = formCatalogDetails(form); if (details?.form) return details.form; if (form.slug.includes("transport")) return "4A"; if (form.slug.includes("capacity")) return "CAP"; if (form.slug.includes("clozapine")) return "CLZ"; if (form.slug.includes("handover")) return "SAFE"; return form.title .split(/\s+/) .map((part) => part[0]) .join("") .slice(0, 4) .toUpperCase(); } function formShortTitle(form: FormRecord) { const details = formCatalogDetails(form); return details?.form ? `Form ${details.form}` : displayText(form.catalogueLabel, "Form"); } function summaryIcon(card: ServiceSummaryCard) { const label = `${card.id} ${card.label} ${card.title}`.toLowerCase(); const Icon = label.includes("clock") ? Clock3 : label.includes("destination") || label.includes("place") || label.includes("route") ? MapPin : label.includes("authority") || label.includes("maker") ? UserRound : label.includes("criteria") || label.includes("threshold") ? Scale : ClipboardList; return <Icon className="h-5 w-5" aria-hidden />; } function summaryCardsFor(form: FormRecord): ServiceSummaryCard[] { if (form.summaryCards?.length) return form.summaryCards.slice(0, 4); return [ { id: "route", label: "Route", title: "Use pathway", detail: form.route }, { id: "eligibility", label: "Eligibility", title: "Patient fi
'{form.source?.url ? "Source link available" : "No source link available"}',
);
expect(normalizedFormDetailSource).toMatch(/\{form\.source\?\.url \? \( <a href=\{form\.source\.url\}/);
expect(normalizedFormDetailSource).toMatch(
/<a href=\{form\.source\.url\} target="_blank" rel="noopener noreferrer" className="inline-flex min-h-10/,
/<a href=\{form\.source\.url\} target="_blank" rel="noopener noreferrer" className="inline-flex min-h-tap/,
);
expect(formDetailSource.match(/Source link pending/g)).toHaveLength(1);
expect(formDetailSource).toContain("Source link pending");
expect(formDetailSource.match(/Source link available/g)).toHaveLength(1);
expect(formDetailSource).not.toContain("Source link pending");
Comment on lines +209 to +217

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Align this source-link contract with the component.

The reported unit test fails because form-detail-page.tsx does not contain the exact conditional required here. Restore the intended component contract or update this assertion to the component’s stable rendered structure; the current PR leaves CI failing.

🧰 Tools
🪛 GitHub Check: Unit coverage

[failure] 209-209: [node] tests/audit-content-services-regressions.test.ts > content and services audit regressions > claims and renders a form source link only when the record has a URL
AssertionError: expected '"use client"; import { useRouter } fr…' to contain '{form.source?.url ? "Source link avai…'

  • Expected
  • Received
  • {form.source?.url ? "Source link available" : "No source link available"}
  • "use client"; import { useRouter } from "next/navigation"; import { ArrowLeft, Bookmark, BookmarkCheck, CalendarDays, CircleCheck, ChevronRight, Clipboard, ClipboardList, Clock3, Download, ExternalLink, FileText, Info, MapPin, Navigation, Phone, Route, Scale, ShieldCheck, Tag, UserRound, X, CircleX, type LucideIcon, } from "lucide-react"; import { useMemo, useState, type ReactNode } from "react"; import { cn, codeText, floatingControl, metadataPill, pageContainer, primaryControl, textMuted, toneDanger, toneInfo, toneNeutral, toneSuccess, toneWarning, } from "@/components/ui-primitives"; import { FormCodeBadge, splitFormCode } from "@/components/forms/form-code-badge"; import { appModeHomeHref } from "@/lib/app-modes"; import { formCatalogDetails, type FormRecord } from "@/lib/form-ranker"; import type { ServiceChipTone, ServiceContact, ServiceCriterion, ServiceSummaryCard } from "@/lib/service-ranker"; import { readSavedRegistrySlugs, savedFormsStorageKey, writeSavedRegistrySlugs } from "@/lib/saved-registry-storage"; const missingText = "Not listed"; function hasText(value: string | null | undefined): value is string { return Boolean(value && value.trim().length > 0); } function displayText(value: string | null | undefined, fallback = missingText) { return hasText(value) ? value.trim() : fallback; } async function copyText(value: string) { if (navigator.clipboard?.writeText) { try { await navigator.clipboard.writeText(value); return; } catch { // Fall through to the legacy selection path for restricted browser contexts. } } const textArea = document.createElement("textarea"); textArea.value = value; textArea.setAttribute("readonly", ""); textArea.style.position = "fixed"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.select(); try { const copied = document.execCommand?.("copy"); if (copied === false) throw new Error("copy command rejected"); } finally { document.body.removeChild(textArea); } } function chipToneClass(tone: ServiceChipTone | null | undefined) { if (tone === "danger") return toneDanger; if (tone === "info") return toneInfo; if (tone === "warning") return toneWarning; if (tone === "success") return toneSuccess; return toneNeutral; } export function sourceToneClass(form: FormRecord) { const status = form.source?.status?.toLowerCase() ?? ""; if (/required|review|unverified|not verified|unchecked|pending|unknown|confirm/.test(status)) return toneWarning; if (form.verification?.locallyVerified === true) return toneSuccess; return toneNeutral; } function formCode(form: FormRecord) { const details = formCatalogDetails(form); if (details?.form) return details.form; if (form.slug.includes("transport")) return "4A"; if (form.slug.includes("capacity")) return "CAP"; if (form.slug.includes("clozapine")) return "CLZ"; if (form.slug.includes("handover")) return "SAFE"; return form.title .split(/\s+/) .map((part) => part[0]) .join("") .slice(0, 4) .toUpperCase(); } function formShortTitle(form: FormRecord) { const details = formCatalogDetails(form); return details?.form ? Form ${details.form} : displayText(form.catalogueLabel, "Form"); } function summaryIcon(card: ServiceSummaryCard) { const label = ${card.id} ${card.label} ${card.title}.toLowerCase(); const Icon = label.includes("clock") ? Clock3 : label.includes("destination") || label.includes("place") || label.includes("route") ? MapPin : label.includes("authority") || label.includes("maker") ? UserRound : label.includes("criteria") || label.includes("threshold") ? Scale : ClipboardList; return ; } function summaryCardsFor(form: FormRecord): ServiceSummaryCard[] { if (form.summaryCards?.length) return form.summaryCards.slice(0, 4); return [ { id: "route", label: "Route", title: "Use pathway", detail: form.route }, { id: "eligibility", label: "Eligibility", title: "Patient fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/audit-content-services-regressions.test.ts` around lines 209 - 217,
Align the source-link assertion in the test with the implementation in the
form-detail component: either restore the conditional structure expected by the
normalized source check, or update the assertions to match the component’s
stable rendered markup. Ensure the test passes while preserving the single
available-link expectation and rejecting “Source link pending.”

Source: Linters/SAST tools

});
});
16 changes: 7 additions & 9 deletions tests/audit-navigation-auth-regressions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
const start = contents.indexOf(startMarker);
const end = contents.indexOf(endMarker, start + startMarker.length);
if (start < 0 || end < 0) {
throw new Error(`Could not locate source segment from ${startMarker} to ${endMarker}.`);

Check failure on line 20 in tests/audit-navigation-auth-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-navigation-auth-regressions.test.ts > audit navigation and auth regressions > gates private polling and mutations on local readiness plus authenticated status

Error: Could not locate source segment from // Local/demo guests can read the public library to const canRunSearch =. ❯ sourceSegment tests/audit-navigation-auth-regressions.test.ts:20:11 ❯ tests/audit-navigation-auth-regressions.test.ts:93:39

Check failure on line 20 in tests/audit-navigation-auth-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-navigation-auth-regressions.test.ts > audit navigation and auth regressions > gates private polling and mutations on local readiness plus authenticated status

Error: Could not locate source segment from // Local/demo guests can read the public library to const canRunSearch =. ❯ sourceSegment tests/audit-navigation-auth-regressions.test.ts:20:11 ❯ tests/audit-navigation-auth-regressions.test.ts:93:39
}
return contents.slice(start, end);
}
Expand Down Expand Up @@ -92,23 +92,21 @@
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(
'localProjectReady && (localNoAuthMode || localDevCanAttemptPrivateApis || authStatus === "authenticated")',
'const canUsePrivateApis = localProjectReady && authStatus === "authenticated";',
);
expect(privateCapabilityContract).not.toMatch(/clientDemoMode/);
expect(privateCapabilityContract).not.toMatch(/localNoAuth|clientDemoMode/);

const pollingContract = sourceSegment(
clinicalDashboardSource,
"const shouldRefreshWorkState =",
"const [documentsResponse",
);
expect(pollingContract).toContain(
"includeAdministrationData && (!administrationDataLoadedRef.current || now >= nextWorkStatePollRef.current)",
);
expect(pollingContract).toContain("(canUsePrivateApis || serverDemoMode)");
expect(pollingContract).not.toMatch(/localNoAuth|clientDemoMode/);

const labelMutationContract = sourceSegment(
clinicalDashboardSource,
Expand All @@ -125,8 +123,8 @@
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(/<h1\b/g)).toHaveLength(1);
expect(clinicalDashboardSource).toMatch(/<h1 className="sr-only">\s*Clinical Guide\s*<\/h1>/);
expect(clinicalDashboardSource).toMatch(/<h1 className="sr-only">\s*Clinical KB\s*<\/h1>/);

Check failure on line 128 in tests/audit-navigation-auth-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-navigation-auth-regressions.test.ts > audit navigation and auth regressions > keeps the root dashboard H1 as Clinical KB

AssertionError: expected '"use client";\n\nimport { useRouter, …' to match /<h1 className="sr-only">\s*Clinica…/h1> - Expected: /<h1 className="sr-only">\s*Clinical KB\s*<\/h1>/ + Received: "\"use client\"; import { useRouter, useSearchParams } from \"next/navigation\"; import dynamic from \"next/dynamic\"; import { CircleAlert, BookOpen, ChevronDown, Clock3, ExternalLink, FileImage, FileText, FolderOpen, Heart, ListChecks, Loader2, Quote, RefreshCw, Search, ShieldAlert, Square, UploadCloud, WifiOff, Wrench, } from \"lucide-react\"; import { type CSSProperties, useCallback, useEffect, useMemo, useReducer, useRef, useState } from \"react\"; import { type DocumentDeleteResult } from \"@/components/DocumentManagementActions\"; import { extractSafetyFindings } from \"@/lib/clinical-safety\"; import { isLocalNoAuthMode, publicUploadsEnabled } from \"@/lib/client-env\"; import { readLocalProjectIdentity, unsafeLocalProjectMessage } from \"@/lib/local-project-identity\"; import { isDeployedClinicalKb } from \"@/lib/deployed-app\"; import { appBackdrop, answerSurface, cn, EmptyState, floatingControl, InlineNotice, primaryControl, textMuted, } from \"@/components/ui-primitives\"; import { useAuthSession } from \"@/lib/supabase/client\"; import { AccountSetupDialog } from \"@/components/clinical-dashboard/account-setup-dialog\"; import { CrossModeLinksSection } from \"@/components/clinical-dashboard/cross-mode-links\"; import { useEventCallback } from \"@/components/clinical-dashboard/use-event-callback\"; import { AuthPanel } from \"@/components/clinical-dashboard/auth-panel\"; import { buildMobileSectionFabState, MobileSectionFab, ToolsHub } from \"@/components/clinical-dashboard/dashboard-nav\"; import { SettingsDialog } from \"@/components/clinical-dashboard/settings-dialog\"; import { useSidebarCollapsed } from \"@/components/clinical-dashboard/use-sidebar-collapsed\"; import { useTheme } from \"@/components/clinical-dashboard/use-theme\"; import { deriveSidebarIdentity, ClinicalDesktopSidebar, ClinicalMobileSidebar, } from \"@/components/clinical-dashboard/ClinicalSidebar\"; import { SetupChecklist, UploadPanel, IndexingMonitor, IngestionQualityConsole, LibraryHealthStrip, fallbackSetupChecks, hasReadyRequiredPublicSearchConfig, hasReadyPublicSearchSetup, type SetupCheck, type IngestionQualityReviewItem, } from \"@/components/clinical-dashboard/DocumentManagerPanel\"; import { GuideDialog, GuideTrigger, UtilityDrawer } from \"@/components/clinical-dashboard/dashboard-shell\"; import { sanitizeAnswerDisplayText, sanitizeDisplayText } from \"@/components/clinical-dashboard/display-text\"; import { isPreformattedGroundedAnswer, NaturalLanguageAnswer, ScopeAndGovernanceNotice, UserQuestionBubble, } from \"@/components/clinical-dashboard/answer-content\"; import { AnswerEmptyState, AnswerProgressStepper, AnswerSkeleton } from \"@/components/clinical-dashboard/answer-status\"; import { type AnswerProgressUpdate, type TimedAnswerProgressUpdate, } from \"@/components/clinical-dashboard/answer-progress\"; import { evidenceMapRowsFromRenderModel } from \"@/components/clinical-dashboard/evidence-map-model\"; import { MasterSearchHeader } from \"@/components/clinical-dashboard/master-search-header\"; import { UniversalSearchAlsoMatches } from \"@/components/clinical-dashboard/universal-search-also-matches\"; import { useScrollHideReporter } from \"@/components/clinical-dashboard/use-hide-on-scroll\"; import { SearchCommandProvider } from \"@/components/clinical-dashboard/search-command-context\"; import { answerReferencesDocument, answerTimedOutError, applyRenamedDocumentToAnswer, compactScopeFilters, hasActiveIndexingWork, hasNonProductionSupabaseApiKeyFallback, isAbortError, mergeDocumentRefresh, normalizeNavigationHash, setupNeedsSlowRecheck, setupRecheckPollMs, shorterPollDelay, } from \"@/components/clinical-dashboard/clinical-dashboard-helpers\"; import { answerRecovery, errorCopy } from \"@/lib/ui-copy\"; imp

Check failure on line 128 in tests/audit-navigation-auth-regressions.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/audit-navigation-auth-regressions.test.ts > audit navigation and auth regressions > keeps the root dashboard H1 as Clinical KB

AssertionError: expected '"use client";\n\nimport { useRouter, …' to match /<h1 className="sr-only">\s*Clinica…/h1> - Expected: /<h1 className="sr-only">\s*Clinical KB\s*<\/h1>/ + Received: "\"use client\"; import { useRouter, useSearchParams } from \"next/navigation\"; import dynamic from \"next/dynamic\"; import { CircleAlert, BookOpen, ChevronDown, Clock3, ExternalLink, FileImage, FileText, FolderOpen, Heart, ListChecks, Loader2, Quote, RefreshCw, Search, ShieldAlert, Square, UploadCloud, WifiOff, Wrench, } from \"lucide-react\"; import { type CSSProperties, useCallback, useEffect, useMemo, useReducer, useRef, useState } from \"react\"; import { type DocumentDeleteResult } from \"@/components/DocumentManagementActions\"; import { extractSafetyFindings } from \"@/lib/clinical-safety\"; import { isLocalNoAuthMode, publicUploadsEnabled } from \"@/lib/client-env\"; import { readLocalProjectIdentity, unsafeLocalProjectMessage } from \"@/lib/local-project-identity\"; import { isDeployedClinicalKb } from \"@/lib/deployed-app\"; import { appBackdrop, answerSurface, cn, EmptyState, floatingControl, InlineNotice, primaryControl, textMuted, } from \"@/components/ui-primitives\"; import { useAuthSession } from \"@/lib/supabase/client\"; import { AccountSetupDialog } from \"@/components/clinical-dashboard/account-setup-dialog\"; import { CrossModeLinksSection } from \"@/components/clinical-dashboard/cross-mode-links\"; import { useEventCallback } from \"@/components/clinical-dashboard/use-event-callback\"; import { AuthPanel } from \"@/components/clinical-dashboard/auth-panel\"; import { buildMobileSectionFabState, MobileSectionFab, ToolsHub } from \"@/components/clinical-dashboard/dashboard-nav\"; import { SettingsDialog } from \"@/components/clinical-dashboard/settings-dialog\"; import { useSidebarCollapsed } from \"@/components/clinical-dashboard/use-sidebar-collapsed\"; import { useTheme } from \"@/components/clinical-dashboard/use-theme\"; import { deriveSidebarIdentity, ClinicalDesktopSidebar, ClinicalMobileSidebar, } from \"@/components/clinical-dashboard/ClinicalSidebar\"; import { SetupChecklist, UploadPanel, IndexingMonitor, IngestionQualityConsole, LibraryHealthStrip, fallbackSetupChecks, hasReadyRequiredPublicSearchConfig, hasReadyPublicSearchSetup, type SetupCheck, type IngestionQualityReviewItem, } from \"@/components/clinical-dashboard/DocumentManagerPanel\"; import { GuideDialog, GuideTrigger, UtilityDrawer } from \"@/components/clinical-dashboard/dashboard-shell\"; import { sanitizeAnswerDisplayText, sanitizeDisplayText } from \"@/components/clinical-dashboard/display-text\"; import { isPreformattedGroundedAnswer, NaturalLanguageAnswer, ScopeAndGovernanceNotice, UserQuestionBubble, } from \"@/components/clinical-dashboard/answer-content\"; import { AnswerEmptyState, AnswerProgressStepper, AnswerSkeleton } from \"@/components/clinical-dashboard/answer-status\"; import { type AnswerProgressUpdate, type TimedAnswerProgressUpdate, } from \"@/components/clinical-dashboard/answer-progress\"; import { evidenceMapRowsFromRenderModel } from \"@/components/clinical-dashboard/evidence-map-model\"; import { MasterSearchHeader } from \"@/components/clinical-dashboard/master-search-header\"; import { UniversalSearchAlsoMatches } from \"@/components/clinical-dashboard/universal-search-also-matches\"; import { useScrollHideReporter } from \"@/components/clinical-dashboard/use-hide-on-scroll\"; import { SearchCommandProvider } from \"@/components/clinical-dashboard/search-command-context\"; import { answerReferencesDocument, answerTimedOutError, applyRenamedDocumentToAnswer, compactScopeFilters, hasActiveIndexingWork, hasNonProductionSupabaseApiKeyFallback, isAbortError, mergeDocumentRefresh, normalizeNavigationHash, setupNeedsSlowRecheck, setupRecheckPollMs, shorterPollDelay, } from \"@/components/clinical-dashboard/clinical-dashboard-helpers\"; import { answerRecovery, errorCopy } from \"@/lib/ui-copy\"; imp
Comment on lines +126 to +128

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reconcile the H1 assertion with ClinicalDashboard.

This test currently fails because the dashboard source does not match the exact className="sr-only" H1 markup. Update the component to the intended accessibility contract or revise the assertion to the actual stable structure before merging.

🧰 Tools
🪛 GitHub Check: Unit coverage

[failure] 128-128: [node] tests/audit-navigation-auth-regressions.test.ts > audit navigation and auth regressions > keeps the root dashboard H1 as Clinical KB
AssertionError: expected '"use client";\n\nimport { useRouter, …' to match /

\s*Clinica…/h1>

  • Expected:
    /

    \sClinical KB\s</h1>/

  • Received:
    ""use client";

import { useRouter, useSearchParams } from "next/navigation";
import dynamic from "next/dynamic";
import {
CircleAlert,
BookOpen,
ChevronDown,
Clock3,
ExternalLink,
FileImage,
FileText,
FolderOpen,
Heart,
ListChecks,
Loader2,
Quote,
RefreshCw,
Search,
ShieldAlert,
Square,
UploadCloud,
WifiOff,
Wrench,
} from "lucide-react";
import { type CSSProperties, useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
import { type DocumentDeleteResult } from "@/components/DocumentManagementActions";
import { extractSafetyFindings } from "@/lib/clinical-safety";
import { isLocalNoAuthMode, publicUploadsEnabled } from "@/lib/client-env";
import { readLocalProjectIdentity, unsafeLocalProjectMessage } from "@/lib/local-project-identity";
import { isDeployedClinicalKb } from "@/lib/deployed-app";
import {
appBackdrop,
answerSurface,
cn,
EmptyState,
floatingControl,
InlineNotice,
primaryControl,
textMuted,
} from "@/components/ui-primitives";
import { useAuthSession } from "@/lib/supabase/client";
import { AccountSetupDialog } from "@/components/clinical-dashboard/account-setup-dialog";
import { CrossModeLinksSection } from "@/components/clinical-dashboard/cross-mode-links";
import { useEventCallback } from "@/components/clinical-dashboard/use-event-callback";
import { AuthPanel } from "@/components/clinical-dashboard/auth-panel";
import { buildMobileSectionFabState, MobileSectionFab, ToolsHub } from "@/components/clinical-dashboard/dashboard-nav";
import { SettingsDialog } from "@/components/clinical-dashboard/settings-dialog";
import { useSidebarCollapsed } from "@/components/clinical-dashboard/use-sidebar-collapsed";
import { useTheme } from "@/components/clinical-dashboard/use-theme";
import {
deriveSidebarIdentity,
ClinicalDesktopSidebar,
ClinicalMobileSidebar,
} from "@/components/clinical-dashboard/ClinicalSidebar";
import {
SetupChecklist,
UploadPanel,
IndexingMonitor,
IngestionQualityConsole,
LibraryHealthStrip,
fallbackSetupChecks,
hasReadyRequiredPublicSearchConfig,
hasReadyPublicSearchSetup,
type SetupCheck,
type IngestionQualityReviewItem,
} from "@/components/clinical-dashboard/DocumentManagerPanel";
import { GuideDialog, GuideTrigger, UtilityDrawer } from "@/components/clinical-dashboard/dashboard-shell";
import { sanitizeAnswerDisplayText, sanitizeDisplayText } from "@/components/clinical-dashboard/display-text";
import {
isPreformattedGroundedAnswer,
NaturalLanguageAnswer,
ScopeAndGovernanceNotice,
UserQuestionBubble,
} from "@/components/clinical-dashboard/answer-content";
import { AnswerEmptyState, AnswerProgressStepper, AnswerSkeleton } from "@/components/clinical-dashboard/answer-status";
import {
type AnswerProgressUpdate,
type TimedAnswerProgressUpdate,
} from "@/components/clinical-dashboard/answer-progress";
import { evidenceMapRowsFromRenderModel } from "@/components/clinical-dashboard/evidence-map-model";
import { MasterSearchHeader } from "@/components/clinical-dashboard/master-search-header";
import { UniversalSearchAlsoMatches } from "@/components/clinical-dashboard/universal-search-also-matches";
import { useScrollHideReporter } from "@/components/clinical-dashboard/use-hide-on-scroll";
import { SearchCommandProvider } from "@/components/clinical-dashboard/search-command-context";
import {
answerReferencesDocument,
answerTimedOutError,
applyRenamedDocumentToAnswer,
compactScopeFilters,
hasActiveIndexingWork,
hasNonProductionSupabaseApiKeyFallback,
isAbortError,
mergeDocumentRefresh,
normalizeNavigationHash,
setupNeedsSlowRecheck,
setupRecheckPollMs,
shorterPollDelay,
} from "@/components/clinical-dashboard/clinical-dashboard-helpers";
import { answerRecovery, errorCopy } from "@/lib/ui-copy";
imp

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/audit-navigation-auth-regressions.test.ts` around lines 126 - 128,
Reconcile the H1 expectation in the test with the markup produced by
ClinicalDashboard: either update ClinicalDashboard’s root heading to provide the
intended accessible “Clinical KB” H1 contract or revise the assertion to match
its stable rendered structure. Ensure exactly one root H1 remains and the
accessible title is preserved.

Source: Linters/SAST tools

});
});
34 changes: 34 additions & 0 deletions tests/site-map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,40 @@
}
});

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}\``);

Check failure on line 129 in tests/site-map.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/site-map.test.ts > tracked sitemap > keeps public redirect handlers in product routes and API handlers in the API section

AssertionError: expected '## Main product routes\n\n- `/` - Mai…' to contain '`/applications`' - Expected + Received - `/applications` + ## Main product routes + + - `/` - Main Clinical KB shell. Source: `src/app/page.tsx`. + - `/differentials` - Differentials home and search surface. Source: `src/app/differentials/page.tsx`. + - `/differentials/diagnoses` - Diagnosis stream. Source: `src/app/differentials/diagnoses/page.tsx`. + - `/differentials/presentations/[slug]` - Route discovered from app directory Source: `src/app/differentials/presentations/[slug]/page.tsx`. + - `/documents/search` - Documents search command centre. Source: `src/app/documents/search/page.tsx`. + - `/documents/source` - Compatibility redirect to the canonical live document viewer when a valid id is supplied. Source: `src/app/documents/source/page.tsx`. + - `/documents/source/evidence` - Compatibility redirect sharing the canonical live document viewer handoff. Source: `src/app/documents/source/evidence/page.tsx`. + - `/dsm` - DSM-5 Diagnosis home. Source: `src/app/dsm/page.tsx`. + - `/dsm/compare` - DSM diagnosis comparison. Source: `src/app/dsm/compare/page.tsx`. + - `/dsm/search` - DSM diagnosis search and catalogue browser. Source: `src/app/dsm/search/page.tsx`. + - `/factsheets` - Route discovered from app directory Source: `src/app/factsheets/page.tsx`. + - `/factsheets/[slug]` - Route discovered from app directory Source: `src/app/factsheets/[slug]/page.tsx`. + - `/factsheets/search` - Route discovered from app directory Source: `src/app/factsheets/search/page.tsx`. + - `/favourites` - Saved clinical items and sets. Source: `src/app/favourites/page.tsx`. + - `/forms` - Forms home and search surface. Source: `src/app/forms/page.tsx`. + - `/formulation` - Clinical formulation home and local mechanism search surface. Source: `src/app/formulation/page.tsx`. + - `/formulation/builder` - Structured clinical formulation builder. Source: `src/app/formulation/builder/page.tsx`. + - `/formulation/compare` - Side-by-side mechanism comparison. Source: `src/app/formulation/compare/page.tsx`. + - `/formulation/map` - Formulation mechanism domain map. Source: `src/app/formulation/map/page.tsx`. + - `/privacy` - Privacy and data-processing governance draft. Source: `src/app/privacy/page.tsx`. + - `/reference/colour-coding` - Route discovered from app directory Source: `src/app/reference/colour-coding/page.tsx`. + - `/services` - Services home and search surface. Source: `src/app/services/page.tsx`. + - `/specifiers` - Psychiatric specifier home and local search surface. Source: `src/app/specifiers/page.tsx`. + - `/specifiers/builder` - Structured diagnostic wording builder. Source: `src/app/specifiers/builder/page.tsx`. + - `/specifiers/compare` - Side-by-side psychiatric specifier comparison. Source: `src/app/specifiers/compare/page.tsx`. + - `/specifiers/map` - Psychiatric specifier family map. Source: `src/app/specifiers/map/page.tsx`. + - `/therapy-compass` - Therapy Compass home (source-grounded therapy decision support). Source: `src/app/therapy-compass/page.tsx`. + - `/therapy-compass/[slug]/brief` - Therapy brief-intervention view. Source: `src/app/therapy-compass/[slug]/brief/page.tsx`. + - `/therapy-compass/[slug]/sheet` - Therapy patient-sheet builder. Source: `src/app/therapy-compass/[slug]/sheet/page.tsx`. + - `/therapy-compass/compare` - Side-by-side therapy comparison. Source: `src/app/therapy-compass/compare/page.tsx`. + - `/therapy-compass/pathways` - Problem-based clinical therapy pathways. Source: `src/app/therapy-compass/pathways/page.tsx`. + - `/therapy-compass/recommend` - Recommend a therapy from a clinical question and constraints. Source: `src/app/therapy-compass/recommend/page.tsx`. + - `/therapy-compass/review` - Therapy records awaiting qualified-clinician source review. Source: `src/app/therapy-compass/review/page.tsx`. + - `/therapy-compass/search` - Therapy library search surface. Source: `src/app/therapy-compass/search/page.tsx`. + - `/tools` - Route discovered from app directory Source: `src/app/tools/page.tsx`. + + �

Check failure on line 129 in tests/site-map.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/site-map.test.ts > tracked sitemap > keeps public redirect handlers in product routes and API handlers in the API section

AssertionError: expected '## Main product routes\n\n- `/` - Mai…' to contain '`/applications`' - Expected + Received - `/applications` + ## Main product routes + + - `/` - Main Clinical KB shell. Source: `src/app/page.tsx`. + - `/differentials` - Differentials home and search surface. Source: `src/app/differentials/page.tsx`. + - `/differentials/diagnoses` - Diagnosis stream. Source: `src/app/differentials/diagnoses/page.tsx`. + - `/differentials/presentations/[slug]` - Route discovered from app directory Source: `src/app/differentials/presentations/[slug]/page.tsx`. + - `/documents/search` - Documents search command centre. Source: `src/app/documents/search/page.tsx`. + - `/documents/source` - Compatibility redirect to the canonical live document viewer when a valid id is supplied. Source: `src/app/documents/source/page.tsx`. + - `/documents/source/evidence` - Compatibility redirect sharing the canonical live document viewer handoff. Source: `src/app/documents/source/evidence/page.tsx`. + - `/dsm` - DSM-5 Diagnosis home. Source: `src/app/dsm/page.tsx`. + - `/dsm/compare` - DSM diagnosis comparison. Source: `src/app/dsm/compare/page.tsx`. + - `/dsm/search` - DSM diagnosis search and catalogue browser. Source: `src/app/dsm/search/page.tsx`. + - `/factsheets` - Route discovered from app directory Source: `src/app/factsheets/page.tsx`. + - `/factsheets/[slug]` - Route discovered from app directory Source: `src/app/factsheets/[slug]/page.tsx`. + - `/factsheets/search` - Route discovered from app directory Source: `src/app/factsheets/search/page.tsx`. + - `/favourites` - Saved clinical items and sets. Source: `src/app/favourites/page.tsx`. + - `/forms` - Forms home and search surface. Source: `src/app/forms/page.tsx`. + - `/formulation` - Clinical formulation home and local mechanism search surface. Source: `src/app/formulation/page.tsx`. + - `/formulation/builder` - Structured clinical formulation builder. Source: `src/app/formulation/builder/page.tsx`. + - `/formulation/compare` - Side-by-side mechanism comparison. Source: `src/app/formulation/compare/page.tsx`. + - `/formulation/map` - Formulation mechanism domain map. Source: `src/app/formulation/map/page.tsx`. + - `/privacy` - Privacy and data-processing governance draft. Source: `src/app/privacy/page.tsx`. + - `/reference/colour-coding` - Route discovered from app directory Source: `src/app/reference/colour-coding/page.tsx`. + - `/services` - Services home and search surface. Source: `src/app/services/page.tsx`. + - `/specifiers` - Psychiatric specifier home and local search surface. Source: `src/app/specifiers/page.tsx`. + - `/specifiers/builder` - Structured diagnostic wording builder. Source: `src/app/specifiers/builder/page.tsx`. + - `/specifiers/compare` - Side-by-side psychiatric specifier comparison. Source: `src/app/specifiers/compare/page.tsx`. + - `/specifiers/map` - Psychiatric specifier family map. Source: `src/app/specifiers/map/page.tsx`. + - `/therapy-compass` - Therapy Compass home (source-grounded therapy decision support). Source: `src/app/therapy-compass/page.tsx`. + - `/therapy-compass/[slug]/brief` - Therapy brief-intervention view. Source: `src/app/therapy-compass/[slug]/brief/page.tsx`. + - `/therapy-compass/[slug]/sheet` - Therapy patient-sheet builder. Source: `src/app/therapy-compass/[slug]/sheet/page.tsx`. + - `/therapy-compass/compare` - Side-by-side therapy comparison. Source: `src/app/therapy-compass/compare/page.tsx`. + - `/therapy-compass/pathways` - Problem-based clinical therapy pathways. Source: `src/app/therapy-compass/pathways/page.tsx`. + - `/therapy-compass/recommend` - Recommend a therapy from a clinical question and constraints. Source: `src/app/therapy-compass/recommend/page.tsx`. + - `/therapy-compass/review` - Therapy records awaiting qualified-clinician source review. Source: `src/app/therapy-compass/review/page.tsx`. + - `/therapy-compass/search` - Therapy library search surface. Source: `src/app/therapy-compass/search/page.tsx`. + - `/tools` - Route discovered from app directory Source: `src/app/tools/page.tsx`. + + �
expect(apiSection).not.toContain(`\`${route}\``);
Comment on lines +125 to +130

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Regenerate the checked-in sitemap artifact.

The test now requires these public handlers in ## Main product routes, but the reported sitemap contents omit /applications, so Line 129 fails. Regenerate and commit the sitemap from the repository’s generator (or intentionally revise the generation contract); do not leave this assertion red.

🧰 Tools
🪛 GitHub Check: Unit coverage

[failure] 129-129: [node] tests/site-map.test.ts > tracked sitemap > keeps public redirect handlers in product routes and API handlers in the API section
AssertionError: expected '## Main product routes\n\n- / - Mai…' to contain '/applications'

  • Expected
  • Received
  • /applications
  • Main product routes

    • / - Main Clinical KB shell. Source: src/app/page.tsx.
    • /differentials - Differentials home and search surface. Source: src/app/differentials/page.tsx.
    • /differentials/diagnoses - Diagnosis stream. Source: src/app/differentials/diagnoses/page.tsx.
    • /differentials/presentations/[slug] - Route discovered from app directory Source: src/app/differentials/presentations/[slug]/page.tsx.
    • /documents/search - Documents search command centre. Source: src/app/documents/search/page.tsx.
    • /documents/source - Compatibility redirect to the canonical live document viewer when a valid id is supplied. Source: src/app/documents/source/page.tsx.
    • /documents/source/evidence - Compatibility redirect sharing the canonical live document viewer handoff. Source: src/app/documents/source/evidence/page.tsx.
    • /dsm - DSM-5 Diagnosis home. Source: src/app/dsm/page.tsx.
    • /dsm/compare - DSM diagnosis comparison. Source: src/app/dsm/compare/page.tsx.
    • /dsm/search - DSM diagnosis search and catalogue browser. Source: src/app/dsm/search/page.tsx.
    • /factsheets - Route discovered from app directory Source: src/app/factsheets/page.tsx.
    • /factsheets/[slug] - Route discovered from app directory Source: src/app/factsheets/[slug]/page.tsx.
    • /factsheets/search - Route discovered from app directory Source: src/app/factsheets/search/page.tsx.
    • /favourites - Saved clinical items and sets. Source: src/app/favourites/page.tsx.
    • /forms - Forms home and search surface. Source: src/app/forms/page.tsx.
    • /formulation - Clinical formulation home and local mechanism search surface. Source: src/app/formulation/page.tsx.
    • /formulation/builder - Structured clinical formulation builder. Source: src/app/formulation/builder/page.tsx.
    • /formulation/compare - Side-by-side mechanism comparison. Source: src/app/formulation/compare/page.tsx.
    • /formulation/map - Formulation mechanism domain map. Source: src/app/formulation/map/page.tsx.
    • /privacy - Privacy and data-processing governance draft. Source: src/app/privacy/page.tsx.
    • /reference/colour-coding - Route discovered from app directory Source: src/app/reference/colour-coding/page.tsx.
    • /services - Services home and search surface. Source: src/app/services/page.tsx.
    • /specifiers - Psychiatric specifier home and local search surface. Source: src/app/specifiers/page.tsx.
    • /specifiers/builder - Structured diagnostic wording builder. Source: src/app/specifiers/builder/page.tsx.
    • /specifiers/compare - Side-by-side psychiatric specifier comparison. Source: src/app/specifiers/compare/page.tsx.
    • /specifiers/map - Psychiatric specifier family map. Source: src/app/specifiers/map/page.tsx.
    • /therapy-compass - Therapy Compass home (source-grounded therapy decision support). Source: src/app/therapy-compass/page.tsx.
    • /therapy-compass/[slug]/brief - Therapy brief-intervention view. Source: src/app/therapy-compass/[slug]/brief/page.tsx.
    • /therapy-compass/[slug]/sheet - Therapy patient-sheet builder. Source: src/app/therapy-compass/[slug]/sheet/page.tsx.
    • /therapy-compass/compare - Side-by-side therapy comparison. Source: src/app/therapy-compass/compare/page.tsx.
    • /therapy-compass/pathways - Problem-based clinical therapy pathways. Source: src/app/therapy-compass/pathways/page.tsx.
    • /therapy-compass/recommend - Recommend a therapy from a clinical question and constraints. Source: src/app/therapy-compass/recommend/page.tsx.
    • /therapy-compass/review - Therapy records awaiting qualified-clinician source review. Source: src/app/therapy-compass/review/page.tsx.
    • /therapy-compass/search - Therapy library search surface. Source: src/app/therapy-compass/search/page.tsx.
    • /tools - Route discovered from app directory Source: src/app/tools/page.tsx.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/site-map.test.ts` around lines 125 - 130, Regenerate the checked-in
sitemap artifact using the repository’s existing sitemap generator so the public
route handlers, including /applications, appear in the Main product routes
section and satisfy the assertions in expectedProductHandlers. Commit the
generated output without changing the test expectations.

Source: Linters/SAST tools

}
});

it("documents seeded dynamic slugs", () => {
for (const service of serviceRecords) expectDocumentedRoute(service.slug);
for (const form of formRecords) expectDocumentedRoute(form.slug);
Expand Down
14 changes: 4 additions & 10 deletions tests/supabase-schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1219,7 +1219,7 @@
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");

Check failure on line 1222 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > fails closed on effective supabase_admin default ACLs

AssertionError: expected '20260717173000_reassert_supabase_admi…' to be '20260717161000_assert_supabase_admin_…' // Object.is equality Expected: "20260717161000_assert_supabase_admin_default_privileges.sql" Received: "20260717173000_reassert_supabase_admin_default_privileges.sql" ❯ tests/supabase-schema.test.ts:1222:35

Check failure on line 1222 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > fails closed on effective supabase_admin default ACLs

AssertionError: expected '20260717173000_reassert_supabase_admi…' to be '20260717161000_assert_supabase_admin_…' // Object.is equality Expected: "20260717161000_assert_supabase_admin_default_privileges.sql" Received: "20260717173000_reassert_supabase_admin_default_privileges.sql" ❯ tests/supabase-schema.test.ts:1222:35
});

it("bootstraps safe default ACLs before fresh local and preview migration replay", () => {
Expand Down Expand Up @@ -1371,7 +1371,7 @@
);
expect(cleanup).toContain("metadata->>'registry_record_id' = old.id::text");
expect(cleanup).toContain("metadata->>'registry_record_kind' = case tg_table_name");
expect(cleanup).toMatch(/when 'clinical_registry_records' then (pg_catalog\.)?to_jsonb\((old|OLD)\)->>'kind'/);
expect(cleanup).toContain("when 'clinical_registry_records' then to_jsonb(old)->>'kind'");

Check failure on line 1374 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > hardens registry cleanup without UUID casts or cross-registry collisions

AssertionError: expected 'create or replace function public.cle…' to contain 'when \'clinical_registry_records\' th…' Expected: "when 'clinical_registry_records' then to_jsonb(old)->>'kind'" Received: "create or replace function public.cleanup_registry_corpus_document() returns trigger language plpgsql security definer set search_path = '' as $$ begin delete from public.documents where metadata->>'source_kind' = 'registry_record' and metadata->>'registry_record_kind' = case tg_table_name when 'clinical_registry_records' then pg_catalog.to_jsonb(old)->>'kind' when 'medication_records' then 'medication' when 'differential_records' then 'differential' else null end and metadata->>'registry_record_id' = old.id::text; return old; end; $$; " ❯ tests/supabase-schema.test.ts:1374:23

Check failure on line 1374 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > hardens registry cleanup without UUID casts or cross-registry collisions

AssertionError: expected 'create or replace function public.cle…' to contain 'when \'clinical_registry_records\' th…' Expected: "when 'clinical_registry_records' then to_jsonb(old)->>'kind'" Received: "create or replace function public.cleanup_registry_corpus_document() returns trigger language plpgsql security definer set search_path = '' as $$ begin delete from public.documents where metadata->>'source_kind' = 'registry_record' and metadata->>'registry_record_kind' = case tg_table_name when 'clinical_registry_records' then pg_catalog.to_jsonb(old)->>'kind' when 'medication_records' then 'medication' when 'differential_records' then 'differential' else null end and metadata->>'registry_record_id' = old.id::text; return old; end; $$; " ❯ tests/supabase-schema.test.ts:1374:23
expect(cleanup).toContain("when 'medication_records' then 'medication'");
expect(cleanup).toContain("when 'differential_records' then 'differential'");
expect(cleanup).not.toContain("registry_record_id')::uuid");
Expand All @@ -1395,14 +1395,8 @@
expect(corrector).toContain("lower(canonical) % tok");
expect(corrector).toContain("word % tok");
expect(corrector).toContain("limit 32");
expect(corrector).toContain("min_sim real default 0.45");
if (corrector.includes("set pg_trgm.similarity_threshold = 0.3")) {
expect(corrector).toContain("set pg_trgm.similarity_threshold = 0.3");
}
expect(corrector).toContain("best_sim >= min_sim");
if (corrector.includes("min_sim is null or min_sim < 0.3 or min_sim > 1")) {
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");

Check failure on line 1398 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > uses bounded indexed probes for clinical query correction

AssertionError: expected 'create or replace function public.cor…' to contain 'set pg_trgm.similarity_threshold = 0.3' Expected: "set pg_trgm.similarity_threshold = 0.3" Received: "create or replace function public.correct_clinical_query_terms( input_query text, min_sim real default 0.45 ) returns text language plpgsql stable security definer set search_path = pg_catalog, extensions as $$ declare tokens text[]; tok text; best text; best_sim real; corrected text[] := array[]::text[]; changed boolean := false; begin if input_query is null or length(trim(input_query)) = 0 then return input_query; end if; tokens := regexp_split_to_array(lower(input_query), '\s+'); foreach tok in array tokens loop if length(tok) < 4 then corrected := corrected || tok; continue; end if; best := null; best_sim := 0; select candidate.term, candidate.match_sim into best, best_sim from ( ( select lower(canonical) as term, similarity(lower(alias), tok) as match_sim from public.rag_aliases where enabled and owner_id is null and length(alias) between 4 and 40 and length(canonical) between 4 and 40 and lower(alias) % tok order by similarity(lower(alias), tok) desc, lower(alias) limit 32 ) union all ( select lower(canonical) as term, similarity(lower(canonical), tok) as match_sim from public.rag_aliases where enabled and owner_id is null and length(canonical) between 4 and 40 and lower(canonical) % tok order by similarity(lower(canonical), tok) desc, lower(canonical) limit 32 ) union all ( select word as term, similarity(word, tok) as match_sim from public.document_title_words where length(word) between 4 and 40 and word % tok order by similarity(word, tok) desc, word limit 32 ) ) candidate order by candidate.match_sim desc, candidate.term limit 1; if best is not null and best_sim >= min_sim and best <> tok and length(best) >= length(tok) then corrected := corrected || best; changed := true; else corrected := corrected || tok; end if; end loop; if not changed then return input_query; end if; return array_to_string(corrected, ' '); end; $$; " ❯ tests/supabase-schema.test.ts:1398:25

Check failure on line 1398 in tests/supabase-schema.test.ts

View workflow job for this annotation

GitHub Actions / Unit coverage

[node] tests/supabase-schema.test.ts > Supabase Preview replay guards > uses bounded indexed probes for clinical query correction

AssertionError: expected 'create or replace function public.cor…' to contain 'set pg_trgm.similarity_threshold = 0.3' Expected: "set pg_trgm.similarity_threshold = 0.3" Received: "create or replace function public.correct_clinical_query_terms( input_query text, min_sim real default 0.45 ) returns text language plpgsql stable security definer set search_path = pg_catalog, extensions as $$ declare tokens text[]; tok text; best text; best_sim real; corrected text[] := array[]::text[]; changed boolean := false; begin if input_query is null or length(trim(input_query)) = 0 then return input_query; end if; tokens := regexp_split_to_array(lower(input_query), '\s+'); foreach tok in array tokens loop if length(tok) < 4 then corrected := corrected || tok; continue; end if; best := null; best_sim := 0; select candidate.term, candidate.match_sim into best, best_sim from ( ( select lower(canonical) as term, similarity(lower(alias), tok) as match_sim from public.rag_aliases where enabled and owner_id is null and length(alias) between 4 and 40 and length(canonical) between 4 and 40 and lower(alias) % tok order by similarity(lower(alias), tok) desc, lower(alias) limit 32 ) union all ( select lower(canonical) as term, similarity(lower(canonical), tok) as match_sim from public.rag_aliases where enabled and owner_id is null and length(canonical) between 4 and 40 and lower(canonical) % tok order by similarity(lower(canonical), tok) desc, lower(canonical) limit 32 ) union all ( select word as term, similarity(word, tok) as match_sim from public.document_title_words where length(word) between 4 and 40 and word % tok order by similarity(word, tok) desc, word limit 32 ) ) candidate order by candidate.match_sim desc, candidate.term limit 1; if best is not null and best_sim >= min_sim and best <> tok and length(best) >= length(tok) then corrected := corrected || best; changed := true; else corrected := corrected || tok; end if; end loop; if not changed then return input_query; end if; return array_to_string(corrected, ' '); end; $$; " ❯ tests/supabase-schema.test.ts:1398:25
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");
Expand Down
2 changes: 1 addition & 1 deletion tests/ui-accessibility.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions tests/ui-smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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*$/);
Expand Down Expand Up @@ -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);
});
Expand Down
Loading
Loading