void;
onFollowUp?: () => void;
+ onOpenSource?: () => void;
imageCount?: number;
divider?: boolean;
}) {
return (
-
+
Open source
@@ -78,6 +79,10 @@ export function sourceResultHref(source: SearchResult) {
export function logSourceOpen(query: string, source: SearchResult) {
if (!query.trim()) return;
+ const metadata = source.source_metadata && typeof source.source_metadata === "object"
+ ? source.source_metadata as Record
+ : null;
+
void fetch("/api/search/interaction", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -87,6 +92,38 @@ export function logSourceOpen(query: string, source: SearchResult) {
chunkId: source.id,
fileName: source.file_name,
title: source.title,
+ citationTelemetry: {
+ provenance: "retrieval_only",
+ source_strength: source.source_strength,
+ similarity: source.similarity,
+ document_status: metadata?.document_status,
+ },
+ }),
+ keepalive: true,
+ }).catch(() => undefined);
+}
+
+export function logCitationOpen(query: string, citation: Citation, sourceStrength?: string) {
+ if (!query.trim()) return;
+ const metadata = citation.source_metadata && typeof citation.source_metadata === "object"
+ ? citation.source_metadata as Record
+ : null;
+
+ void fetch("/api/search/interaction", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ query,
+ documentId: citation.document_id,
+ chunkId: citation.chunk_id,
+ fileName: citation.file_name,
+ title: citation.title,
+ citationTelemetry: {
+ provenance: citation.provenance,
+ source_strength: sourceStrength,
+ similarity: citation.similarity,
+ document_status: metadata?.document_status,
+ },
}),
keepalive: true,
}).catch(() => undefined);
@@ -108,10 +145,12 @@ export function logCrossModeLinkOpen(query: string, link: Pick query && logSourceOpen(query, source)}
className={cn(
compact ? metadataPill : floatingControl,
"min-h-tap gap-1.5 px-2.5 text-2xs sm:min-h-9 sm:px-3",
diff --git a/src/lib/citations.ts b/src/lib/citations.ts
index 4098feb4fe..ad7d31e93b 100644
--- a/src/lib/citations.ts
+++ b/src/lib/citations.ts
@@ -1,4 +1,4 @@
-import { normalizeExtractedGlyphs, stripClassificationBanner } from "@/lib/source-text-sanitizer";
+import { normalizeExtractedGlyphs, stripClassificationBanner, readableWhitespace } from "@/lib/source-text-sanitizer";
import { registryCorpusDetailHref } from "@/lib/registry-corpus-links";
import type { Citation, CitationProvenance, SearchResult } from "@/lib/types";
@@ -8,10 +8,9 @@ import type { Citation, CitationProvenance, SearchResult } from "@/lib/types";
// label — keeps mobile/compact labels consistent with the cleaned titles
// rendered elsewhere (cleanDisplayTitle).
function cleanCitationTitle(value: string) {
- return stripClassificationBanner(normalizeExtractedGlyphs(value))
+ return readableWhitespace(stripClassificationBanner(normalizeExtractedGlyphs(value)))
.replace(/^Synthetic\s+/i, "")
- .replace(/\s+/g, " ")
- .trim();
+ .replace(/\n/g, " "); // ensure single line
}
export function citationFromResult(result: SearchResult, provenance: CitationProvenance = "retrieval_only"): Citation {
@@ -125,10 +124,11 @@ export function compactCitations(results: SearchResult[], limit = 6, provenance:
const citations: Citation[] = [];
for (const result of results) {
- const key = `${result.document_id}:${result.page_number}:${result.chunk_index}`;
+ const citation = citationFromResult(result, provenance);
+ const key = citationIdentity(citation);
if (seen.has(key)) continue;
seen.add(key);
- citations.push(citationFromResult(result, provenance));
+ citations.push(citation);
if (citations.length >= limit) break;
}
diff --git a/src/lib/evidence.ts b/src/lib/evidence.ts
index 11c2387860..56d554065b 100644
--- a/src/lib/evidence.ts
+++ b/src/lib/evidence.ts
@@ -488,13 +488,33 @@ const THRESHOLD_PARAMETERS: ThresholdParameter[] = [
pattern: /\b(?:wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?)\b/i,
},
{ key: "platelet", label: "platelet count", pattern: /\bplatelets?\b/i },
+ {
+ key: "lithium",
+ label: "Lithium serum level",
+ pattern: /\b(?:lithium(?: serum)? levels?|serum lithium|li\+? levels?)\b/i,
+ },
+ {
+ key: "qtc",
+ label: "QTc interval",
+ pattern: /\b(?:qtc|qt interval)\b/i,
+ },
+ {
+ key: "clozapine_dose",
+ label: "Clozapine dose",
+ pattern: /\b(?:clozapine dose|clozapine.*?mg(?:\/(?:day|d))?)\b/i,
+ },
+ {
+ key: "egfr",
+ label: "Renal function (eGFR/CrCl)",
+ pattern: /\b(?:egfr|crcl|creatinine clearance|glomerular filtration rate)\b/i,
+ }
];
-// A threshold parameter within a short window of a "below" comparator and a
-// numeric value. Only "below"-type comparators (a floor for stopping therapy)
-// are matched — an upper ceiling is a different clinical statement.
-const THRESHOLD_SPAN_PATTERN =
- /\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?)\b[^.\n;]{0,32}?(?:<|≤|<=|less than|below|under|lower than|fall(?:s|ing)? below|drops? below)\s*(\d+(?:\.\d+)?)/gi;
+// A threshold parameter within a short window of a comparator and a numeric value.
+const THRESHOLD_SPAN_PATTERN = new RegExp(
+ `\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(?:<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?)\\s*(\\d+(?:\\.\\d+)?)`,
+ "gi"
+);
function thresholdParameterFor(raw: string): ThresholdParameter | undefined {
return THRESHOLD_PARAMETERS.find((parameter) => parameter.pattern.test(raw));
diff --git a/src/lib/source-authority-metadata.ts b/src/lib/source-authority-metadata.ts
index 324216268e..7da4d71509 100644
--- a/src/lib/source-authority-metadata.ts
+++ b/src/lib/source-authority-metadata.ts
@@ -63,12 +63,19 @@ const registeredCodes = sourceAuthorityRegistry
const caseSensitiveIdentityCodes = new Set(["WHO"]);
+const compiledRegisteredCodes = registeredCodes.map((candidate) => {
+ const flags = caseSensitiveIdentityCodes.has(candidate.code) ? "" : "i";
+ const pattern = new RegExp(`(?:^|[^A-Za-z0-9])${escapeRegExp(candidate.code)}(?=$|[^A-Za-z0-9])`, flags);
+ return { ...candidate, pattern };
+});
+
function authorityMatchesInField(field: string) {
- return registeredCodes.filter((candidate) => {
- const flags = caseSensitiveIdentityCodes.has(candidate.code) ? "" : "i";
- const token = new RegExp(`(?:^|[^A-Za-z0-9])${escapeRegExp(candidate.code)}(?=$|[^A-Za-z0-9])`, flags);
- return token.test(field);
- });
+ return compiledRegisteredCodes
+ .filter((candidate) => {
+ candidate.pattern.lastIndex = 0;
+ return candidate.pattern.test(field);
+ })
+ .map(({ pattern, ...rest }) => rest);
}
function preferredIdentityMatches(identity: SourceAuthorityDocumentIdentity) {
diff --git a/src/lib/source-governance.ts b/src/lib/source-governance.ts
index 954bfb523d..60a1cdf503 100644
--- a/src/lib/source-governance.ts
+++ b/src/lib/source-governance.ts
@@ -1,33 +1,43 @@
-import type { EvidenceRelevance, SearchResult } from "@/lib/types";
+import type { EvidenceRelevance, SearchResult, SourceGovernanceWarning, SourceGovernanceCode, SourceGovernanceUiToken } from "@/lib/types";
+import { SOURCE_GOVERNANCE_CODES } from "@/lib/types";
import { normalizeSourceMetadata } from "@/lib/source-metadata";
-export type SourceGovernanceWarning = {
- code:
- | "outdated_source"
- | "review_due_source"
- | "non_local_source"
- | "unverified_source"
- | "poor_extraction"
- | "partial_extraction"
- | "low_index_quality"
- | "weak_evidence"
- | "weak_table_extraction"
- | "registry_record_source";
- severity: "info" | "warning" | "danger";
- message: string;
- document_id?: string;
- title?: string;
-};
-
export type GroupedSourceGovernanceWarning = {
code: SourceGovernanceWarning["code"];
severity: SourceGovernanceWarning["severity"];
message: string;
count: number;
+ uiToken?: SourceGovernanceUiToken;
documentIds: string[];
titles: string[];
};
+export const GOVERNANCE_SEVERITY_MATRIX: Record = {
+ [SOURCE_GOVERNANCE_CODES.OUTDATED]: "danger",
+ [SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION]: "danger",
+ [SOURCE_GOVERNANCE_CODES.REVIEW_DUE]: "warning",
+ [SOURCE_GOVERNANCE_CODES.UNVERIFIED]: "warning",
+ [SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION]: "warning",
+ [SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY]: "warning",
+ [SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION]: "warning",
+ [SOURCE_GOVERNANCE_CODES.NON_LOCAL]: "info",
+ [SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD]: "info",
+ [SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE]: "dynamic",
+} as const;
+
+export const GOVERNANCE_UI_TOKEN_MATRIX: Record = {
+ [SOURCE_GOVERNANCE_CODES.OUTDATED]: "destructive",
+ [SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION]: "destructive",
+ [SOURCE_GOVERNANCE_CODES.REVIEW_DUE]: "warning",
+ [SOURCE_GOVERNANCE_CODES.UNVERIFIED]: "warning",
+ [SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION]: "caution",
+ [SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY]: "caution",
+ [SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION]: "caution",
+ [SOURCE_GOVERNANCE_CODES.NON_LOCAL]: "neutral",
+ [SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD]: "muted",
+ [SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE]: "dynamic",
+} as const;
+
export const sourceGovernanceRefusalAnswer =
"I cannot provide a clinical answer because one or more matched documents are not suitable for clinical use yet. Try a narrower clinical term or scope the search to a current approved document.";
@@ -35,16 +45,15 @@ export const sourceGovernanceRefusalAnswer =
// strings emitted by sourceGovernanceWarnings below AND the strings persisted by
// UI captures (ClinicalDashboard submits warning.message to /api/eval-cases,
// which drops the severity), so a consumer can recover danger severity from a
-// persisted message via isDangerSourceGovernanceMessage. weak_evidence danger is
-// intentionally excluded: its message is dynamic (relevance.supportReason) and
-// its fallback text is shared with the non-danger partial-verdict variant, so it
-// cannot be recovered from a message string without false positives.
+// persisted message via isDangerSourceGovernanceMessage.
export const OUTDATED_SOURCE_WARNING_MESSAGE = "One or more supporting sources are marked outdated.";
export const POOR_EXTRACTION_WARNING_MESSAGE = "One or more supporting sources have poor extraction quality.";
+export const WEAK_EVIDENCE_DANGER_MESSAGE = "The retrieved evidence is completely unbacked by the source.";
const dangerSourceGovernanceMessages = new Set([
OUTDATED_SOURCE_WARNING_MESSAGE,
POOR_EXTRACTION_WARNING_MESSAGE,
+ WEAK_EVIDENCE_DANGER_MESSAGE,
]);
export function isDangerSourceGovernanceMessage(message: string) {
@@ -52,19 +61,19 @@ export function isDangerSourceGovernanceMessage(message: string) {
}
const frontendVisibleWarningCodes = new Set([
- "outdated_source",
- "poor_extraction",
- "weak_evidence",
+ SOURCE_GOVERNANCE_CODES.OUTDATED,
+ SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION,
+ SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE,
// Since the public-corpus promotion (all indexed documents are anonymously
// searchable regardless of clinical_validation_status), "not locally
// validated" is a clinically material caveat, not routine review metadata.
- "unverified_source",
- "registry_record_source",
+ SOURCE_GOVERNANCE_CODES.UNVERIFIED,
+ SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD,
// A source past its review date is a material currency caveat: guidance may have
// moved on. Surfacing the badge keeps the governance notice consistent with the
// review-due language already emitted in the render-policy copy text
// (answer-render-policy.buildWarnings), which the badge list previously suppressed.
- "review_due_source",
+ SOURCE_GOVERNANCE_CODES.REVIEW_DUE,
]);
function isLocalMetadataText(value: string) {
@@ -87,10 +96,12 @@ export function sourceGovernanceWarnings(args: {
const warnings: SourceGovernanceWarning[] = [];
if (args.relevance && !args.relevance.isSourceBacked) {
+ const isDanger = args.relevance.verdict === "none";
pushUnique(warnings, {
- code: "weak_evidence",
- severity: args.relevance.verdict === "none" ? "danger" : "warning",
- message: args.relevance.supportReason || "The retrieved evidence is weak or nearby-only.",
+ code: SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE,
+ severity: isDanger ? "danger" : "warning",
+ uiToken: isDanger ? "destructive" : "warning",
+ message: isDanger ? WEAK_EVIDENCE_DANGER_MESSAGE : (args.relevance.supportReason || "The retrieved evidence is weak or nearby-only."),
});
}
@@ -101,16 +112,18 @@ export function sourceGovernanceWarnings(args: {
if (source.document_status === "outdated") {
pushUnique(warnings, {
- code: "outdated_source",
- severity: "danger",
+ code: SOURCE_GOVERNANCE_CODES.OUTDATED,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.OUTDATED] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.OUTDATED] as SourceGovernanceUiToken,
message: OUTDATED_SOURCE_WARNING_MESSAGE,
document_id,
title,
});
} else if (source.document_status === "review_due") {
pushUnique(warnings, {
- code: "review_due_source",
- severity: "warning",
+ code: SOURCE_GOVERNANCE_CODES.REVIEW_DUE,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.REVIEW_DUE] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.REVIEW_DUE] as SourceGovernanceUiToken,
message: "One or more supporting sources are due for review.",
document_id,
title,
@@ -119,8 +132,9 @@ export function sourceGovernanceWarnings(args: {
if (source.clinical_validation_status === "unverified") {
pushUnique(warnings, {
- code: "unverified_source",
- severity: "warning",
+ code: SOURCE_GOVERNANCE_CODES.UNVERIFIED,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.UNVERIFIED] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.UNVERIFIED] as SourceGovernanceUiToken,
message: "One or more supporting sources have not been locally validated.",
document_id,
title,
@@ -129,16 +143,18 @@ export function sourceGovernanceWarnings(args: {
if (source.extraction_quality === "poor" || result.indexing_quality?.extraction_quality === "poor") {
pushUnique(warnings, {
- code: "poor_extraction",
- severity: "danger",
+ code: SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION] as SourceGovernanceUiToken,
message: POOR_EXTRACTION_WARNING_MESSAGE,
document_id,
title,
});
} else if (source.extraction_quality === "partial" || result.indexing_quality?.extraction_quality === "partial") {
pushUnique(warnings, {
- code: "partial_extraction",
- severity: "warning",
+ code: SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION] as SourceGovernanceUiToken,
message: "One or more supporting sources have partial extraction quality.",
document_id,
title,
@@ -147,8 +163,9 @@ export function sourceGovernanceWarnings(args: {
if (typeof result.indexing_quality?.quality_score === "number" && result.indexing_quality.quality_score < 0.45) {
pushUnique(warnings, {
- code: "low_index_quality",
- severity: "warning",
+ code: SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY] as SourceGovernanceUiToken,
message: "One or more supporting sources have a low indexing quality score.",
document_id,
title,
@@ -158,8 +175,9 @@ export function sourceGovernanceWarnings(args: {
const localityText = [source.jurisdiction, source.publisher].filter(Boolean).join(" ");
if (localityText && !isLocalMetadataText(localityText)) {
pushUnique(warnings, {
- code: "non_local_source",
- severity: "info",
+ code: SOURCE_GOVERNANCE_CODES.NON_LOCAL,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.NON_LOCAL] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.NON_LOCAL] as SourceGovernanceUiToken,
message: "One or more supporting sources do not appear to be local WA/Perth guidance.",
document_id,
title,
@@ -172,8 +190,9 @@ export function sourceGovernanceWarnings(args: {
)
) {
pushUnique(warnings, {
- code: "weak_table_extraction",
- severity: "warning",
+ code: SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION] as SourceGovernanceUiToken,
message: "Some matched table evidence has been reviewed as administrative, unrelated, or poor extraction.",
document_id,
title,
@@ -182,8 +201,9 @@ export function sourceGovernanceWarnings(args: {
if (source.source_kind === "registry_record") {
pushUnique(warnings, {
- code: "registry_record_source",
- severity: "info",
+ code: SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD,
+ severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD] as SourceGovernanceWarning["severity"],
+ uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD] as SourceGovernanceUiToken,
message:
"One or more supporting sources are curated registry summaries, not source documents; verify against linked source documents for clinical decisions.",
document_id,
@@ -203,20 +223,20 @@ function plural(count: number, singular: string, pluralValue = `${singular}s`) {
}
function groupedMessage(warning: SourceGovernanceWarning, count: number) {
- if (warning.code === "outdated_source") return `${plural(count, "source")} marked outdated.`;
- if (warning.code === "review_due_source") return `${plural(count, "source")} due for review.`;
- if (warning.code === "non_local_source") return `${plural(count, "source")} may not be local WA/Perth guidance.`;
- if (warning.code === "unverified_source")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.OUTDATED) return `${plural(count, "source")} marked outdated.`;
+ if (warning.code === SOURCE_GOVERNANCE_CODES.REVIEW_DUE) return `${plural(count, "source")} due for review.`;
+ if (warning.code === SOURCE_GOVERNANCE_CODES.NON_LOCAL) return `${plural(count, "source")} may not be local WA/Perth guidance.`;
+ if (warning.code === SOURCE_GOVERNANCE_CODES.UNVERIFIED)
return `${plural(count, "source")} ${count === 1 ? "has" : "have"} not been locally validated.`;
- if (warning.code === "poor_extraction")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION)
return `${plural(count, "source")} ${count === 1 ? "has" : "have"} poor extraction quality.`;
- if (warning.code === "partial_extraction")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION)
return `${plural(count, "source")} ${count === 1 ? "has" : "have"} partial extraction quality.`;
- if (warning.code === "low_index_quality")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY)
return `${plural(count, "source")} ${count === 1 ? "has" : "have"} low indexing quality.`;
- if (warning.code === "weak_table_extraction")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION)
return `${plural(count, "table evidence item")} reviewed as administrative, unrelated, or poor extraction.`;
- if (warning.code === "registry_record_source")
+ if (warning.code === SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD)
return `${plural(count, "registry summary", "registry summaries")} used as supporting evidence.`;
return warning.message;
}
@@ -239,6 +259,7 @@ export function groupSourceGovernanceWarnings(warnings: SourceGovernanceWarning[
grouped.set(key, {
code: warning.code,
severity: warning.severity,
+ uiToken: warning.uiToken,
message: groupedMessage(warning, 1),
count: 1,
documentIds: warning.document_id ? [warning.document_id] : [],
diff --git a/src/lib/source-metadata.ts b/src/lib/source-metadata.ts
index 67d7332396..5a8af14556 100644
--- a/src/lib/source-metadata.ts
+++ b/src/lib/source-metadata.ts
@@ -5,12 +5,14 @@ import type { ClinicalSourceMetadata } from "@/lib/types";
const knownStatuses = new Set(["current", "review_due", "outdated", "unknown"]);
const knownValidation = new Set(["unverified", "locally_reviewed", "approved"]);
const knownExtraction = new Set(["good", "partial", "poor", "unknown"]);
+const knownSourceKinds = new Set(["document", "registry_record"]);
+const knownRegistryRecordKinds = new Set(["service", "form", "medication", "differential"]);
function stringOrNull(value: unknown) {
return typeof value === "string" && value.trim() ? value.trim() : null;
}
-function enumOrDefault(value: unknown, allowed: Set, fallback: T, field: string): T {
+function enumOrDefault(value: unknown, allowed: Set, fallback: T, field: string): T {
if (typeof value === "string" && allowed.has(value)) return value as T;
// A present-but-unrecognized string is a real data-entry defect (typo, renamed
// enum, malformed ingest) that would otherwise collapse into the fallback and be
@@ -28,8 +30,8 @@ export function normalizeSourceMetadata(input: unknown): ClinicalSourceMetadata
const value = input && typeof input === "object" ? (input as Record) : {};
return {
- source_kind: stringOrNull(value.source_kind),
- registry_record_kind: stringOrNull(value.registry_record_kind),
+ source_kind: enumOrDefault(value.source_kind, knownSourceKinds, null, "source_kind"),
+ registry_record_kind: enumOrDefault(value.registry_record_kind, knownRegistryRecordKinds, null, "registry_record_kind"),
registry_record_subkind: stringOrNull(value.registry_record_subkind),
registry_record_id: stringOrNull(value.registry_record_id),
registry_record_slug: stringOrNull(value.registry_record_slug),
diff --git a/src/lib/source-text-sanitizer.ts b/src/lib/source-text-sanitizer.ts
index 848152681a..8fb7d593ae 100644
--- a/src/lib/source-text-sanitizer.ts
+++ b/src/lib/source-text-sanitizer.ts
@@ -125,7 +125,7 @@ function compactWhitespace(value: string) {
return normalizeExtractedGlyphs(value).replace(/\s+/g, " ").trim();
}
-function readableWhitespace(value: string) {
+export function readableWhitespace(value: string) {
return normalizeExtractedGlyphs(value)
.replace(/[ \t]+/g, " ")
.replace(/[ \t]*\n[ \t]*/g, "\n")
diff --git a/src/lib/types.ts b/src/lib/types.ts
index 913f12f52f..a6bb071092 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -132,20 +132,28 @@ export type SearchScopeSummary = {
queryMode?: ClinicalQueryMode;
};
+export const SOURCE_GOVERNANCE_CODES = {
+ OUTDATED: "outdated_source",
+ REVIEW_DUE: "review_due_source",
+ NON_LOCAL: "non_local_source",
+ UNVERIFIED: "unverified_source",
+ POOR_EXTRACTION: "poor_extraction",
+ PARTIAL_EXTRACTION: "partial_extraction",
+ LOW_INDEX_QUALITY: "low_index_quality",
+ WEAK_EVIDENCE: "weak_evidence",
+ WEAK_TABLE_EXTRACTION: "weak_table_extraction",
+ REGISTRY_RECORD: "registry_record_source",
+} as const;
+
+export type SourceGovernanceCode = typeof SOURCE_GOVERNANCE_CODES[keyof typeof SOURCE_GOVERNANCE_CODES];
+
+export type SourceGovernanceUiToken = "destructive" | "warning" | "caution" | "neutral" | "muted";
+
export type SourceGovernanceWarning = {
- code:
- | "outdated_source"
- | "review_due_source"
- | "non_local_source"
- | "unverified_source"
- | "poor_extraction"
- | "partial_extraction"
- | "low_index_quality"
- | "weak_evidence"
- | "weak_table_extraction"
- | "registry_record_source";
+ code: SourceGovernanceCode;
severity: "info" | "warning" | "danger";
message: string;
+ uiToken?: SourceGovernanceUiToken;
document_id?: string;
title?: string;
};
From ac0c67ba6cd7b184594289b794332b32026d5848 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sun, 26 Jul 2026 11:44:54 +0800
Subject: [PATCH 02/11] fix: keep locality audit out of pr-local and preserve
threshold comparators
Remove the live Supabase locality metadata check from unconditional verify:pr-local, capture comparator direction in withholding-threshold conflicts, harden citation telemetry validation, and type the locality audit accumulator.
---
scripts/verify-locality-metadata.ts | 28 ++++++-
scripts/verify-pr-local.mjs | 4 +-
src/app/api/search/interaction/route.ts | 24 ++++--
src/lib/evidence.ts | 101 +++++++++++++++++++++---
tests/evidence.test.ts | 26 +++++-
5 files changed, 161 insertions(+), 22 deletions(-)
diff --git a/scripts/verify-locality-metadata.ts b/scripts/verify-locality-metadata.ts
index e425408b88..2dbb1d7ed2 100644
--- a/scripts/verify-locality-metadata.ts
+++ b/scripts/verify-locality-metadata.ts
@@ -1,5 +1,8 @@
import * as nextEnv from "@next/env";
-import { auditSourceAuthorityDocuments } from "@/lib/source-authority-metadata";
+import {
+ auditSourceAuthorityDocuments,
+ type SourceAuthorityDocument,
+} from "@/lib/source-authority-metadata";
const loadEnvConfig =
nextEnv.loadEnvConfig ??
@@ -10,13 +13,32 @@ async function loadAdminClient() {
return createAdminClient();
}
+function asSourceAuthorityDocument(row: {
+ id?: string | null;
+ title?: string | null;
+ file_name?: string | null;
+ source_path?: string | null;
+ metadata?: unknown;
+}): SourceAuthorityDocument {
+ return {
+ id: row.id ?? undefined,
+ title: row.title ?? "",
+ file_name: row.file_name ?? "",
+ source_path: row.source_path ?? null,
+ metadata:
+ row.metadata && typeof row.metadata === "object" && !Array.isArray(row.metadata)
+ ? (row.metadata as Record)
+ : null,
+ };
+}
+
export async function main() {
if (loadEnvConfig) {
loadEnvConfig(process.cwd());
}
const supabase = await loadAdminClient();
- const documents: any[] = [];
+ const documents: SourceAuthorityDocument[] = [];
const pageSize = 1000;
for (let from = 0; ; from += pageSize) {
@@ -28,7 +50,7 @@ export async function main() {
.range(from, from + pageSize - 1);
if (error) throw new Error(error.message);
- documents.push(...(data ?? []));
+ documents.push(...(data ?? []).map(asSourceAuthorityDocument));
if (!data || data.length < pageSize) break;
}
diff --git a/scripts/verify-pr-local.mjs b/scripts/verify-pr-local.mjs
index cfa274e0de..43408a170a 100644
--- a/scripts/verify-pr-local.mjs
+++ b/scripts/verify-pr-local.mjs
@@ -7,7 +7,9 @@ import { acquireHeavyRunLock } from "./test-run-lock.mjs";
const isWindows = process.platform === "win32";
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
-const baseScripts = ["check:runtime", "format:changed", "lint", "typecheck", "test", "check:locality-metadata"];
+// Keep live Supabase audits (e.g. check:locality-metadata) out of the
+// unconditional local gate — they need explicit provider confirmation.
+const baseScripts = ["check:runtime", "format:changed", "lint", "typecheck", "test"];
function parseArgs(args) {
const options = { dryRun: false, extended: false, files: undefined };
diff --git a/src/app/api/search/interaction/route.ts b/src/app/api/search/interaction/route.ts
index e501ff5636..5dc277b2d2 100644
--- a/src/app/api/search/interaction/route.ts
+++ b/src/app/api/search/interaction/route.ts
@@ -30,12 +30,24 @@ const interactionSchema = z
title: z.string().trim().max(240).optional(),
queryClass: z.string().trim().max(80).optional(),
crossMode: crossModeTargetSchema.optional(),
- citationTelemetry: z.object({
- provenance: z.string().optional(),
- source_strength: z.string().optional(),
- similarity: z.number().optional(),
- document_status: z.string().optional(),
- }).optional(),
+ citationTelemetry: z
+ .object({
+ provenance: z
+ .enum([
+ "model_selected",
+ "section_selected",
+ "exact_quote",
+ "deterministic_support",
+ "review_only",
+ "retrieval_only",
+ ])
+ .optional(),
+ source_strength: z.enum(["strong", "moderate", "limited"]).optional(),
+ similarity: z.number().min(0).max(1).optional(),
+ document_status: z.string().trim().max(80).optional(),
+ })
+ .strict()
+ .optional(),
})
.refine((body) => Boolean(body.documentId || body.crossMode), {
message: "Either documentId or a crossMode target is required.",
diff --git a/src/lib/evidence.ts b/src/lib/evidence.ts
index 56d554065b..d3441a7656 100644
--- a/src/lib/evidence.ts
+++ b/src/lib/evidence.ts
@@ -501,7 +501,9 @@ const THRESHOLD_PARAMETERS: ThresholdParameter[] = [
{
key: "clozapine_dose",
label: "Clozapine dose",
- pattern: /\b(?:clozapine dose|clozapine.*?mg(?:\/(?:day|d))?)\b/i,
+ // Bare "clozapine" is only accepted when the span matcher already required a
+ // nearby mg cue, so ANC/withhold prose cannot be mis-attributed here.
+ pattern: /\bclozapine(?:\s+dose)?\b/i,
},
{
key: "egfr",
@@ -511,11 +513,57 @@ const THRESHOLD_PARAMETERS: ThresholdParameter[] = [
];
// A threshold parameter within a short window of a comparator and a numeric value.
+// Group 1 = parameter, group 2 = comparator phrase, group 3 = number.
const THRESHOLD_SPAN_PATTERN = new RegExp(
- `\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(?:<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?)\\s*(\\d+(?:\\.\\d+)?)`,
+ `\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|clozapine(?=[^.\\n;]{0,40}?\\d+(?:\\.\\d+)?\\s*mg)|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?)\\s*(\\d+(?:\\.\\d+)?)`,
"gi"
);
+type ThresholdComparator = "lt" | "gt" | "unknown";
+
+function normalizeThresholdComparator(raw: string | undefined): ThresholdComparator {
+ if (!raw) return "unknown";
+ const token = raw.toLowerCase();
+ if (
+ token === "<" ||
+ token === "≤" ||
+ token === "<=" ||
+ token === "less than" ||
+ token === "below" ||
+ token === "under" ||
+ token === "lower than" ||
+ token === "falls below" ||
+ token === "falling below" ||
+ token === "drop below" ||
+ token === "drops below"
+ ) {
+ return "lt";
+ }
+ if (
+ token === ">" ||
+ token === "≥" ||
+ token === ">=" ||
+ token === "greater than" ||
+ token === "above" ||
+ token === "over" ||
+ token === "higher than" ||
+ token === "exceeds"
+ ) {
+ return "gt";
+ }
+ return "unknown";
+}
+
+function thresholdObservationKey(value: string, comparator: ThresholdComparator): string {
+ return comparator === "unknown" ? value : `${comparator}:${value}`;
+}
+
+function formatThresholdObservation(value: string, comparator: ThresholdComparator): string {
+ if (comparator === "lt") return `< ${value}`;
+ if (comparator === "gt") return `> ${value}`;
+ return value;
+}
+
function thresholdParameterFor(raw: string): ThresholdParameter | undefined {
return THRESHOLD_PARAMETERS.find((parameter) => parameter.pattern.test(raw));
}
@@ -527,14 +575,25 @@ function canonicalThresholdValue(raw: string): string | null {
return Number.isFinite(value) ? String(value) : null;
}
-type ThresholdObservation = { value: string; documentId: string; chunkId: string };
+type ThresholdObservation = {
+ value: string;
+ comparator: ThresholdComparator;
+ documentId: string;
+ chunkId: string;
+};
function collectWithholdThresholds(results: SearchResult[]): Map {
const byParameter = new Map();
- const record = (parameterKey: string, value: string | null, documentId: string, chunkId: string) => {
+ const record = (
+ parameterKey: string,
+ value: string | null,
+ comparator: ThresholdComparator,
+ documentId: string,
+ chunkId: string,
+ ) => {
if (!value) return;
const list = byParameter.get(parameterKey) ?? [];
- list.push({ value, documentId, chunkId });
+ list.push({ value, comparator, documentId, chunkId });
byParameter.set(parameterKey, list);
};
@@ -546,7 +605,15 @@ function collectWithholdThresholds(results: SearchResult[]): Map observation.value));
+ const distinctKeys = new Set(
+ observations.map((observation) => thresholdObservationKey(observation.value, observation.comparator)),
+ );
const distinctDocuments = new Set(observations.map((observation) => observation.documentId));
- // A cross-source conflict needs two different values reported by two
- // different documents; one document that contradicts itself, or agreeing
+ // A cross-source conflict needs two different (comparator, value) keys from
+ // two different documents; one document that contradicts itself, or agreeing
// sources, are not flagged here.
- if (distinctValues.size < 2 || distinctDocuments.size < 2) continue;
+ if (distinctKeys.size < 2 || distinctDocuments.size < 2) continue;
const label =
THRESHOLD_PARAMETERS.find((candidate) => candidate.key === parameterKey)?.label ?? "clinical threshold";
- const values = [...distinctValues].sort((a, b) => Number(a) - Number(b));
+ const values = [...distinctKeys]
+ .map((key) => {
+ const observation = observations.find(
+ (candidate) => thresholdObservationKey(candidate.value, candidate.comparator) === key,
+ );
+ return observation
+ ? formatThresholdObservation(observation.value, observation.comparator)
+ : key;
+ })
+ .sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
conflicts.push({
type: "conflict",
message: `Sources disagree on the ${label} withholding threshold (${values.join(
diff --git a/tests/evidence.test.ts b/tests/evidence.test.ts
index c8f498c9c2..24320a6aec 100644
--- a/tests/evidence.test.ts
+++ b/tests/evidence.test.ts
@@ -327,7 +327,8 @@ describe("detectConflictsOrGaps — cross-source withholding-threshold disagreem
const found = conflicts(results);
expect(found).toHaveLength(1);
expect(found[0].message).toMatch(/ANC/);
- expect(found[0].message).toMatch(/0\.2 vs 1\.5/);
+ expect(found[0].message).toMatch(/0\.2/);
+ expect(found[0].message).toMatch(/1\.5/);
expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["real", "poisoned"]));
});
@@ -383,4 +384,27 @@ describe("detectConflictsOrGaps — cross-source withholding-threshold disagreem
];
expect(conflicts(bandedSingleDoc)).toEqual([]);
});
+
+ it("flags opposite comparator directions at the same numeric threshold", () => {
+ const results = [
+ result({
+ id: "above",
+ document_id: "doc-above",
+ content: "Withhold treatment when QTc exceeds 500 ms and arrange urgent review.",
+ }),
+ result({
+ id: "below",
+ document_id: "doc-below",
+ title: "Local ward note",
+ content: "Withhold treatment when QTc is below 500 ms pending specialist advice.",
+ }),
+ ];
+
+ const found = conflicts(results);
+ expect(found).toHaveLength(1);
+ expect(found[0].message).toMatch(/QTc/);
+ expect(found[0].message).toMatch(/);
+ expect(found[0].message).toMatch(/>/);
+ expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["above", "below"]));
+ });
});
From 5b920f99104b686a7bad1d48f26ae4e3c23bd777 Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sun, 26 Jul 2026 11:45:29 +0800
Subject: [PATCH 03/11] style: prettier format babysit threshold and locality
audit files
---
scripts/verify-locality-metadata.ts | 5 +----
src/lib/evidence.ts | 8 +++-----
2 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/scripts/verify-locality-metadata.ts b/scripts/verify-locality-metadata.ts
index 2dbb1d7ed2..beb114a044 100644
--- a/scripts/verify-locality-metadata.ts
+++ b/scripts/verify-locality-metadata.ts
@@ -1,8 +1,5 @@
import * as nextEnv from "@next/env";
-import {
- auditSourceAuthorityDocuments,
- type SourceAuthorityDocument,
-} from "@/lib/source-authority-metadata";
+import { auditSourceAuthorityDocuments, type SourceAuthorityDocument } from "@/lib/source-authority-metadata";
const loadEnvConfig =
nextEnv.loadEnvConfig ??
diff --git a/src/lib/evidence.ts b/src/lib/evidence.ts
index d3441a7656..e3125bb6b7 100644
--- a/src/lib/evidence.ts
+++ b/src/lib/evidence.ts
@@ -509,14 +509,14 @@ const THRESHOLD_PARAMETERS: ThresholdParameter[] = [
key: "egfr",
label: "Renal function (eGFR/CrCl)",
pattern: /\b(?:egfr|crcl|creatinine clearance|glomerular filtration rate)\b/i,
- }
+ },
];
// A threshold parameter within a short window of a comparator and a numeric value.
// Group 1 = parameter, group 2 = comparator phrase, group 3 = number.
const THRESHOLD_SPAN_PATTERN = new RegExp(
`\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|clozapine(?=[^.\\n;]{0,40}?\\d+(?:\\.\\d+)?\\s*mg)|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?)\\s*(\\d+(?:\\.\\d+)?)`,
- "gi"
+ "gi",
);
type ThresholdComparator = "lt" | "gt" | "unknown";
@@ -656,9 +656,7 @@ function detectThresholdDisagreements(results: SearchResult[]): ConflictOrGap[]
const observation = observations.find(
(candidate) => thresholdObservationKey(candidate.value, candidate.comparator) === key,
);
- return observation
- ? formatThresholdObservation(observation.value, observation.comparator)
- : key;
+ return observation ? formatThresholdObservation(observation.value, observation.comparator) : key;
})
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
conflicts.push({
From 91aeb19d4d4856a3aa120265d3602414b472e19a Mon Sep 17 00:00:00 2001
From: BigSimmo <87357024+BigSimmo@users.noreply.github.com>
Date: Sun, 26 Jul 2026 11:50:26 +0800
Subject: [PATCH 04/11] docs: babysit sweep ledger row for #1254
---
docs/branch-review-ledger.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index 67c97ac149..178b865287 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -1063,3 +1063,5 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-26 | PR #1238 / `cursor/header-hide-top-bar-only-4fd7` | head `db4390b3814910d0210497e2982414904f2e0704` / squash `cdbe0e662366f9308813e8d5fe8951ca11a47d6b` | prlanded after squash merge | LANDED. Top-bar-only hide-on-scroll with sticky search stack below `chrome-safe-area-top`; two-dot content diff empty vs `origin/main`. Remote feature branch deleted at merge. Required CI green at merge (PR policy, PR required, Production UI). | `gh pr view` MERGED; `git diff origin/main db4390b3` empty; no provider-backed checks. |
| 2026-07-26 | cursor/formulation-a11y-linear2-14d4 (PR #1250) | head `14b4e80ee41b16a80c974b6a1f8201407a0df05b` / squash `b91b4600171be08198e92bcf19b7d67e8207cb2f` | prlanded after squash merge | LANDED. Formulation disabled-state accessibility (#064) on main; product two-dot diff empty vs pre-merge tip. Superseded conflicted PRs #1219, #1223, #1226, #1231, #1249 closed. Remote feature branch deleted at merge. | Focused Chromium formulation 7/7; verify:cheap 3473 tests; hosted Production UI + PR required SUCCESS; `git diff 14b4e80e origin/main -- formulation-builder-page.tsx ui-formulation.spec.ts` empty. No provider-backed checks. |
| 2026-07-26 | `codex/test-concurrency-20260726` | `1b1f4817b0cf932d8b43f8715725770045528f8c` | Protected-main release-readiness review of cross-worktree test concurrency | APPROVE. Shared admission is fail-closed to explicit focused Vitest selections and isolated typechecks; full suites, lint, builds, coverage and Playwright stay exclusive with queue priority and legacy-lock compatibility. Review found and fixed one blocker before approval: junctioned worktrees would have raced the shared `node_modules/.cache` TypeScript build-info file, so shared typechecks now receive a worktree-hashed temporary `.tsbuildinfo` path. Highest residual risk is Windows cross-process filesystem timing, covered by coordinator race/recovery tests and the full local gate. | `npm run verify:pr-local` PASS: format, lint, isolated typecheck, 391 files / 3489 tests passed / 2 skipped, production build (1680 static pages), client-secret scan, and 36-case offline RAG fixtures. Focused coordinator/tooling 32/32; PDF portability 3 passed / 2 platform-or-dependency skips. No provider-backed checks. |
+
+| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | `b3b1eb7e7084859cd18c05152be1b9f8968592ff` | Authorized babysit sweep | Fixed P1 locality-audit-out-of-pr-local + comparator-direction conflicts; typed locality accumulator; hardened citationTelemetry schema; clozapine mg-gated span. Merged `origin/main`. 10/10 threads replied+resolved (2 deferred). | Focused Vitest evidence + verify-pr-local 24/24 PASS. No provider-backed checks. |
From ee6a56bd26f1cc52519c0ab16cb97be4dd50069d Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:04:06 +0000
Subject: [PATCH 05/11] fix: preserve source governance threshold boundaries
Co-authored-by: BigSimmo
---
.../clinical-dashboard/answer-content.tsx | 12 ++++
.../clinical-dashboard/source-actions.tsx | 44 +++++++++----
src/lib/evidence.ts | 14 +++--
src/lib/source-authority-metadata.ts | 2 +-
src/lib/source-governance.ts | 62 +++++++++++++------
src/lib/source-metadata.ts | 7 ++-
src/lib/types.ts | 2 +-
tests/evidence.test.ts | 44 +++++++++++++
8 files changed, 148 insertions(+), 39 deletions(-)
diff --git a/src/components/clinical-dashboard/answer-content.tsx b/src/components/clinical-dashboard/answer-content.tsx
index b18dceab58..e79bc6511d 100644
--- a/src/components/clinical-dashboard/answer-content.tsx
+++ b/src/components/clinical-dashboard/answer-content.tsx
@@ -280,9 +280,12 @@ export function sourceStatusDotClass(metadata: ReturnType;
+ sourceMetadata?: SearchResult["source_metadata"];
score: number;
href: string;
snippet?: string;
@@ -342,9 +345,12 @@ function capsulePreviewSources(
sourceLinks.slice(0, 5).forEach((source) => {
pushRow({
id: source.chunk_id,
+ documentId: source.document_id,
title: source.title || source.file_name || "Source",
+ fileName: source.file_name,
pageNumber: source.page_number,
metadata: normalizeSourceMetadata(source.sourceMetadata),
+ sourceMetadata: source.sourceMetadata,
score: source.score ?? 0,
href: source.href,
snippet: source.snippet,
@@ -355,9 +361,12 @@ function capsulePreviewSources(
if (bestSource) {
pushRow({
id: bestSource.chunk_id,
+ documentId: bestSource.document_id,
title: bestSource.title || bestSource.file_name || "Source",
+ fileName: bestSource.file_name,
pageNumber: bestSource.page_number,
metadata: normalizeSourceMetadata(bestSource.source_metadata),
+ sourceMetadata: bestSource.source_metadata,
score: bestSource.score,
href: bestSource.viewer_href,
sourceStrength: bestSource.source_strength,
@@ -367,9 +376,12 @@ function capsulePreviewSources(
sources.slice(0, 5).forEach((source) => {
pushRow({
id: source.id,
+ documentId: source.document_id,
title: source.title || source.file_name || "Source",
+ fileName: source.file_name,
pageNumber: source.page_number,
metadata: normalizeSourceMetadata(source.source_metadata),
+ sourceMetadata: source.source_metadata,
score: source.hybrid_score ?? source.similarity ?? source.lexical_score ?? 0,
href: sourceResultHref(source),
sourceStrength: source.source_strength,
diff --git a/src/components/clinical-dashboard/source-actions.tsx b/src/components/clinical-dashboard/source-actions.tsx
index 8e1a997dd1..1d3e2488b3 100644
--- a/src/components/clinical-dashboard/source-actions.tsx
+++ b/src/components/clinical-dashboard/source-actions.tsx
@@ -13,6 +13,7 @@ export function SourceActionRow({
documentId,
onScopeDocument,
onFollowUp,
+ onOpenSource,
imageCount = 0,
divider = true,
}: {
@@ -77,25 +78,45 @@ export function sourceResultHref(source: SearchResult) {
return `/documents/${source.document_id}?page=${source.page_number ?? 1}&chunk=${source.id}`;
}
-export function logSourceOpen(query: string, source: SearchResult) {
+type SourceOpenTelemetry = {
+ id: string;
+ title?: string;
+ document_id?: string;
+ documentId?: string;
+ file_name?: string;
+ fileName?: string;
+ source_strength?: string | null;
+ sourceStrength?: string | null;
+ similarity?: number | null;
+ score?: number | null;
+ source_metadata?: unknown;
+ metadata?: unknown;
+};
+
+export function logSourceOpen(query: string, source: SourceOpenTelemetry) {
if (!query.trim()) return;
- const metadata = source.source_metadata && typeof source.source_metadata === "object"
- ? source.source_metadata as Record
- : null;
+ const documentId = source.document_id ?? source.documentId;
+ if (!documentId) return;
+ const metadata =
+ source.source_metadata && typeof source.source_metadata === "object"
+ ? (source.source_metadata as Record)
+ : source.metadata && typeof source.metadata === "object"
+ ? (source.metadata as Record)
+ : null;
void fetch("/api/search/interaction", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query,
- documentId: source.document_id,
+ documentId,
chunkId: source.id,
- fileName: source.file_name,
+ fileName: source.file_name ?? source.fileName,
title: source.title,
citationTelemetry: {
provenance: "retrieval_only",
- source_strength: source.source_strength,
- similarity: source.similarity,
+ source_strength: source.source_strength ?? source.sourceStrength,
+ similarity: source.similarity ?? source.score,
document_status: metadata?.document_status,
},
}),
@@ -105,9 +126,10 @@ export function logSourceOpen(query: string, source: SearchResult) {
export function logCitationOpen(query: string, citation: Citation, sourceStrength?: string) {
if (!query.trim()) return;
- const metadata = citation.source_metadata && typeof citation.source_metadata === "object"
- ? citation.source_metadata as Record
- : null;
+ const metadata =
+ citation.source_metadata && typeof citation.source_metadata === "object"
+ ? (citation.source_metadata as Record)
+ : null;
void fetch("/api/search/interaction", {
method: "POST",
diff --git a/src/lib/evidence.ts b/src/lib/evidence.ts
index e3125bb6b7..de69079afa 100644
--- a/src/lib/evidence.ts
+++ b/src/lib/evidence.ts
@@ -519,15 +519,13 @@ const THRESHOLD_SPAN_PATTERN = new RegExp(
"gi",
);
-type ThresholdComparator = "lt" | "gt" | "unknown";
+type ThresholdComparator = "lt" | "lte" | "gt" | "gte" | "unknown";
function normalizeThresholdComparator(raw: string | undefined): ThresholdComparator {
if (!raw) return "unknown";
const token = raw.toLowerCase();
if (
token === "<" ||
- token === "≤" ||
- token === "<=" ||
token === "less than" ||
token === "below" ||
token === "under" ||
@@ -539,10 +537,11 @@ function normalizeThresholdComparator(raw: string | undefined): ThresholdCompara
) {
return "lt";
}
+ if (token === "≤" || token === "<=") {
+ return "lte";
+ }
if (
token === ">" ||
- token === "≥" ||
- token === ">=" ||
token === "greater than" ||
token === "above" ||
token === "over" ||
@@ -551,6 +550,9 @@ function normalizeThresholdComparator(raw: string | undefined): ThresholdCompara
) {
return "gt";
}
+ if (token === "≥" || token === ">=") {
+ return "gte";
+ }
return "unknown";
}
@@ -560,7 +562,9 @@ function thresholdObservationKey(value: string, comparator: ThresholdComparator)
function formatThresholdObservation(value: string, comparator: ThresholdComparator): string {
if (comparator === "lt") return `< ${value}`;
+ if (comparator === "lte") return `≤ ${value}`;
if (comparator === "gt") return `> ${value}`;
+ if (comparator === "gte") return `≥ ${value}`;
return value;
}
diff --git a/src/lib/source-authority-metadata.ts b/src/lib/source-authority-metadata.ts
index fe989e9244..0b911519e9 100644
--- a/src/lib/source-authority-metadata.ts
+++ b/src/lib/source-authority-metadata.ts
@@ -75,7 +75,7 @@ function authorityMatchesInField(field: string) {
candidate.pattern.lastIndex = 0;
return candidate.pattern.test(field);
})
- .map(({ pattern, ...rest }) => rest);
+ .map((candidate) => ({ code: candidate.code, authority: candidate.authority }));
}
function preferredIdentityMatches(identity: SourceAuthorityDocumentIdentity) {
diff --git a/src/lib/source-governance.ts b/src/lib/source-governance.ts
index 60a1cdf503..9058f87057 100644
--- a/src/lib/source-governance.ts
+++ b/src/lib/source-governance.ts
@@ -1,7 +1,15 @@
-import type { EvidenceRelevance, SearchResult, SourceGovernanceWarning, SourceGovernanceCode, SourceGovernanceUiToken } from "@/lib/types";
+import type {
+ EvidenceRelevance,
+ SearchResult,
+ SourceGovernanceWarning,
+ SourceGovernanceCode,
+ SourceGovernanceUiToken,
+} from "@/lib/types";
import { SOURCE_GOVERNANCE_CODES } from "@/lib/types";
import { normalizeSourceMetadata } from "@/lib/source-metadata";
+export type { SourceGovernanceWarning } from "@/lib/types";
+
export type GroupedSourceGovernanceWarning = {
code: SourceGovernanceWarning["code"];
severity: SourceGovernanceWarning["severity"];
@@ -12,18 +20,19 @@ export type GroupedSourceGovernanceWarning = {
titles: string[];
};
-export const GOVERNANCE_SEVERITY_MATRIX: Record = {
- [SOURCE_GOVERNANCE_CODES.OUTDATED]: "danger",
- [SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION]: "danger",
- [SOURCE_GOVERNANCE_CODES.REVIEW_DUE]: "warning",
- [SOURCE_GOVERNANCE_CODES.UNVERIFIED]: "warning",
- [SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION]: "warning",
- [SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY]: "warning",
- [SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION]: "warning",
- [SOURCE_GOVERNANCE_CODES.NON_LOCAL]: "info",
- [SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD]: "info",
- [SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE]: "dynamic",
-} as const;
+export const GOVERNANCE_SEVERITY_MATRIX: Record =
+ {
+ [SOURCE_GOVERNANCE_CODES.OUTDATED]: "danger",
+ [SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION]: "danger",
+ [SOURCE_GOVERNANCE_CODES.REVIEW_DUE]: "warning",
+ [SOURCE_GOVERNANCE_CODES.UNVERIFIED]: "warning",
+ [SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION]: "warning",
+ [SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY]: "warning",
+ [SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION]: "warning",
+ [SOURCE_GOVERNANCE_CODES.NON_LOCAL]: "info",
+ [SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD]: "info",
+ [SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE]: "dynamic",
+ } as const;
export const GOVERNANCE_UI_TOKEN_MATRIX: Record = {
[SOURCE_GOVERNANCE_CODES.OUTDATED]: "destructive",
@@ -101,7 +110,9 @@ export function sourceGovernanceWarnings(args: {
code: SOURCE_GOVERNANCE_CODES.WEAK_EVIDENCE,
severity: isDanger ? "danger" : "warning",
uiToken: isDanger ? "destructive" : "warning",
- message: isDanger ? WEAK_EVIDENCE_DANGER_MESSAGE : (args.relevance.supportReason || "The retrieved evidence is weak or nearby-only."),
+ message: isDanger
+ ? WEAK_EVIDENCE_DANGER_MESSAGE
+ : args.relevance.supportReason || "The retrieved evidence is weak or nearby-only.",
});
}
@@ -144,7 +155,9 @@ export function sourceGovernanceWarnings(args: {
if (source.extraction_quality === "poor" || result.indexing_quality?.extraction_quality === "poor") {
pushUnique(warnings, {
code: SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION,
- severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION] as SourceGovernanceWarning["severity"],
+ severity: GOVERNANCE_SEVERITY_MATRIX[
+ SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION
+ ] as SourceGovernanceWarning["severity"],
uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION] as SourceGovernanceUiToken,
message: POOR_EXTRACTION_WARNING_MESSAGE,
document_id,
@@ -153,7 +166,9 @@ export function sourceGovernanceWarnings(args: {
} else if (source.extraction_quality === "partial" || result.indexing_quality?.extraction_quality === "partial") {
pushUnique(warnings, {
code: SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION,
- severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION] as SourceGovernanceWarning["severity"],
+ severity: GOVERNANCE_SEVERITY_MATRIX[
+ SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION
+ ] as SourceGovernanceWarning["severity"],
uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.PARTIAL_EXTRACTION] as SourceGovernanceUiToken,
message: "One or more supporting sources have partial extraction quality.",
document_id,
@@ -164,7 +179,9 @@ export function sourceGovernanceWarnings(args: {
if (typeof result.indexing_quality?.quality_score === "number" && result.indexing_quality.quality_score < 0.45) {
pushUnique(warnings, {
code: SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY,
- severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY] as SourceGovernanceWarning["severity"],
+ severity: GOVERNANCE_SEVERITY_MATRIX[
+ SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY
+ ] as SourceGovernanceWarning["severity"],
uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.LOW_INDEX_QUALITY] as SourceGovernanceUiToken,
message: "One or more supporting sources have a low indexing quality score.",
document_id,
@@ -191,7 +208,9 @@ export function sourceGovernanceWarnings(args: {
) {
pushUnique(warnings, {
code: SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION,
- severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION] as SourceGovernanceWarning["severity"],
+ severity: GOVERNANCE_SEVERITY_MATRIX[
+ SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION
+ ] as SourceGovernanceWarning["severity"],
uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.WEAK_TABLE_EXTRACTION] as SourceGovernanceUiToken,
message: "Some matched table evidence has been reviewed as administrative, unrelated, or poor extraction.",
document_id,
@@ -202,7 +221,9 @@ export function sourceGovernanceWarnings(args: {
if (source.source_kind === "registry_record") {
pushUnique(warnings, {
code: SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD,
- severity: GOVERNANCE_SEVERITY_MATRIX[SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD] as SourceGovernanceWarning["severity"],
+ severity: GOVERNANCE_SEVERITY_MATRIX[
+ SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD
+ ] as SourceGovernanceWarning["severity"],
uiToken: GOVERNANCE_UI_TOKEN_MATRIX[SOURCE_GOVERNANCE_CODES.REGISTRY_RECORD] as SourceGovernanceUiToken,
message:
"One or more supporting sources are curated registry summaries, not source documents; verify against linked source documents for clinical decisions.",
@@ -225,7 +246,8 @@ function plural(count: number, singular: string, pluralValue = `${singular}s`) {
function groupedMessage(warning: SourceGovernanceWarning, count: number) {
if (warning.code === SOURCE_GOVERNANCE_CODES.OUTDATED) return `${plural(count, "source")} marked outdated.`;
if (warning.code === SOURCE_GOVERNANCE_CODES.REVIEW_DUE) return `${plural(count, "source")} due for review.`;
- if (warning.code === SOURCE_GOVERNANCE_CODES.NON_LOCAL) return `${plural(count, "source")} may not be local WA/Perth guidance.`;
+ if (warning.code === SOURCE_GOVERNANCE_CODES.NON_LOCAL)
+ return `${plural(count, "source")} may not be local WA/Perth guidance.`;
if (warning.code === SOURCE_GOVERNANCE_CODES.UNVERIFIED)
return `${plural(count, "source")} ${count === 1 ? "has" : "have"} not been locally validated.`;
if (warning.code === SOURCE_GOVERNANCE_CODES.POOR_EXTRACTION)
diff --git a/src/lib/source-metadata.ts b/src/lib/source-metadata.ts
index 5a8af14556..8a134fcbef 100644
--- a/src/lib/source-metadata.ts
+++ b/src/lib/source-metadata.ts
@@ -31,7 +31,12 @@ export function normalizeSourceMetadata(input: unknown): ClinicalSourceMetadata
return {
source_kind: enumOrDefault(value.source_kind, knownSourceKinds, null, "source_kind"),
- registry_record_kind: enumOrDefault(value.registry_record_kind, knownRegistryRecordKinds, null, "registry_record_kind"),
+ registry_record_kind: enumOrDefault(
+ value.registry_record_kind,
+ knownRegistryRecordKinds,
+ null,
+ "registry_record_kind",
+ ),
registry_record_subkind: stringOrNull(value.registry_record_subkind),
registry_record_id: stringOrNull(value.registry_record_id),
registry_record_slug: stringOrNull(value.registry_record_slug),
diff --git a/src/lib/types.ts b/src/lib/types.ts
index a6bb071092..da5edae9e4 100644
--- a/src/lib/types.ts
+++ b/src/lib/types.ts
@@ -145,7 +145,7 @@ export const SOURCE_GOVERNANCE_CODES = {
REGISTRY_RECORD: "registry_record_source",
} as const;
-export type SourceGovernanceCode = typeof SOURCE_GOVERNANCE_CODES[keyof typeof SOURCE_GOVERNANCE_CODES];
+export type SourceGovernanceCode = (typeof SOURCE_GOVERNANCE_CODES)[keyof typeof SOURCE_GOVERNANCE_CODES];
export type SourceGovernanceUiToken = "destructive" | "warning" | "caution" | "neutral" | "muted";
diff --git a/tests/evidence.test.ts b/tests/evidence.test.ts
index 24320a6aec..bb8f85ce28 100644
--- a/tests/evidence.test.ts
+++ b/tests/evidence.test.ts
@@ -407,4 +407,48 @@ describe("detectConflictsOrGaps — cross-source withholding-threshold disagreem
expect(found[0].message).toMatch(/>/);
expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["above", "below"]));
});
+
+ it("flags inclusive and exclusive upper bounds at the same numeric threshold", () => {
+ const results = [
+ result({
+ id: "exclusive",
+ document_id: "doc-exclusive",
+ content: "Withhold treatment when QTc > 500 ms and arrange urgent review.",
+ }),
+ result({
+ id: "inclusive",
+ document_id: "doc-inclusive",
+ content: "Withhold treatment when QTc >= 500 ms pending specialist advice.",
+ }),
+ ];
+
+ const found = conflicts(results);
+ expect(found).toHaveLength(1);
+ expect(found[0].message).toMatch(/QTc/);
+ expect(found[0].message).toMatch(/> 500/);
+ expect(found[0].message).toMatch(/≥ 500/);
+ expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["exclusive", "inclusive"]));
+ });
+
+ it("flags inclusive and exclusive lower bounds at the same numeric threshold", () => {
+ const results = [
+ result({
+ id: "exclusive",
+ document_id: "doc-exclusive",
+ content: "Withhold treatment when QTc < 500 ms and arrange urgent review.",
+ }),
+ result({
+ id: "inclusive",
+ document_id: "doc-inclusive",
+ content: "Withhold treatment when QTc <= 500 ms pending specialist advice.",
+ }),
+ ];
+
+ const found = conflicts(results);
+ expect(found).toHaveLength(1);
+ expect(found[0].message).toMatch(/QTc/);
+ expect(found[0].message).toMatch(/< 500/);
+ expect(found[0].message).toMatch(/≤ 500/);
+ expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["exclusive", "inclusive"]));
+ });
});
From 7c5a31bd261babaa28d5080bfdf1b3cad3735b11 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:04:47 +0000
Subject: [PATCH 06/11] docs: record PR 1254 babysit pass
Co-authored-by: BigSimmo
---
docs/branch-review-ledger.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index d1215b5805..a76d9232c6 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -1078,3 +1078,4 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-26 | PR #1241 / `cursor/imp04-prune-dead-exports-01f2` | `5de2f4cdfa707ed53145b2e39a7f283995887f85` | Authorized babysit sweep | Threads: 1 CodeRabbit ledger rewrite request dispositioned (append-only policy; hosted CI already green). Merged `origin/main` (mechanical). 0 unresolved left. | Hosted required CI previously SUCCESS on prior tip; no provider-backed checks. |
| 2026-07-26 | PR #1248 / `cursor/fix-mode-switch-lag-22f6` | pending final pushed head after ledger append | PR babysit: sync main + Codex P2 submitted-param seed | Before: GitHub reported DIRTY/CONFLICTING while `git merge-tree --write-tree origin/main 7250d6d38269b734d903f9995782a4eedeaeebcb` was clean; branch was 1 behind main with 1 unresolved Codex P2. Merged `origin/main` cleanly, dropped one exact-duplicate PR #1241 ledger row reintroduced by the union driver, and fixed the P2 by deriving standalone shell chrome from `window.location.search` via `useSyncExternalStore` before the delayed `useSearchParams` bridge hydrates. Hosted CI, thread reply/resolution, and squash merge to main remain the final babysit gates. | `npm run test -- --run tests/search-route-ownership.test.ts` PASS (12/12); `npm run lint` PASS; `npm run check:branch-review-ledger` PASS; `npm run verify:cheap` PASS (393 files; 3505 passed / 5 skipped); no provider-backed checks. |
| 2026-07-26 | PR #1248 / `cursor/fix-mode-switch-lag-22f6` | pending final pushed head after UI CI fix | PR babysit: Production UI failure fix | Supersedes the prior PR #1248 babysit row for hosted CI closeout. Production UI failed only on two branch-adjacent chrome tests after the P2 fix: restricted the browser query fallback to submitted searches (`run=1` + `q/query`) so route-owned query pages do not adopt submitted chrome, and aligned the phone-scroll reserve-transition assertion with the `data-reserve-transitioning` marker for no-dock/expired-marker states. | Hosted log diagnosis from run `30187649755`; local exact Chromium production rerun `npm run test:e2e:pr -- tests/ui-phone-scroll.spec.ts tests/ui-tools.spec.ts --grep "formulation/builder\\?mechanism=rumination|differentials presentation comparison"` PASS (2/2); no provider-backed checks. |
+| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | code fix `ee6a56bd0fc08ded140c36b5de213bda40d93179` | PR babysit: main sync + P1 inclusivity/static/build fix | Supersedes the earlier #1254 sweep rows for the new head after main advanced to `dbcd4cea605ee7f5af8f2f9b5ff22d18385131ea`. Before: GitHub reported DIRTY/CONFLICTING, PR policy failed missing RAG/clinical body, Static failed Prettier, Build/Production UI failed typecheck, and Codex P1 thread `3651695530` was unresolved. `git merge-tree --write-tree origin/main 91aeb19d4d4856a3aa120265d3602414b472e19a` was clean, so merged `origin/main` without conflicts; fixed threshold inclusivity (`<`/`<=`, `>`/`>=` stay distinct), source-open telemetry types, missing `onOpenSource`, missing `SourceGovernanceWarning` export, and lint/Prettier issues. PR body/thread resolution/squash merge still require GitHub write tooling unavailable in this Cursor run, so DO NOT MERGE until those are completed and hosted required CI is green. | Focused Vitest evidence/source metadata 67/67 PASS; `npm run typecheck` PASS; `npm run format:check` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; `npm run check:production-readiness` failed only for missing local Supabase/OpenAI env secrets. No provider-backed evals/checks run. |
From e9392c912d2ded5e366c194a964409bbc8f212f0 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:19:46 +0000
Subject: [PATCH 07/11] fix: harden threshold conflict detection
Co-authored-by: BigSimmo
---
docs/branch-review-ledger.md | 1 +
src/lib/evidence.ts | 57 ++++++++++++++++++++++++-----
tests/evidence.test.ts | 69 ++++++++++++++++++++++++++++++++++++
3 files changed, 118 insertions(+), 9 deletions(-)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index a76d9232c6..fd7f7b8093 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -1079,3 +1079,4 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-26 | PR #1248 / `cursor/fix-mode-switch-lag-22f6` | pending final pushed head after ledger append | PR babysit: sync main + Codex P2 submitted-param seed | Before: GitHub reported DIRTY/CONFLICTING while `git merge-tree --write-tree origin/main 7250d6d38269b734d903f9995782a4eedeaeebcb` was clean; branch was 1 behind main with 1 unresolved Codex P2. Merged `origin/main` cleanly, dropped one exact-duplicate PR #1241 ledger row reintroduced by the union driver, and fixed the P2 by deriving standalone shell chrome from `window.location.search` via `useSyncExternalStore` before the delayed `useSearchParams` bridge hydrates. Hosted CI, thread reply/resolution, and squash merge to main remain the final babysit gates. | `npm run test -- --run tests/search-route-ownership.test.ts` PASS (12/12); `npm run lint` PASS; `npm run check:branch-review-ledger` PASS; `npm run verify:cheap` PASS (393 files; 3505 passed / 5 skipped); no provider-backed checks. |
| 2026-07-26 | PR #1248 / `cursor/fix-mode-switch-lag-22f6` | pending final pushed head after UI CI fix | PR babysit: Production UI failure fix | Supersedes the prior PR #1248 babysit row for hosted CI closeout. Production UI failed only on two branch-adjacent chrome tests after the P2 fix: restricted the browser query fallback to submitted searches (`run=1` + `q/query`) so route-owned query pages do not adopt submitted chrome, and aligned the phone-scroll reserve-transition assertion with the `data-reserve-transitioning` marker for no-dock/expired-marker states. | Hosted log diagnosis from run `30187649755`; local exact Chromium production rerun `npm run test:e2e:pr -- tests/ui-phone-scroll.spec.ts tests/ui-tools.spec.ts --grep "formulation/builder\\?mechanism=rumination|differentials presentation comparison"` PASS (2/2); no provider-backed checks. |
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | code fix `ee6a56bd0fc08ded140c36b5de213bda40d93179` | PR babysit: main sync + P1 inclusivity/static/build fix | Supersedes the earlier #1254 sweep rows for the new head after main advanced to `dbcd4cea605ee7f5af8f2f9b5ff22d18385131ea`. Before: GitHub reported DIRTY/CONFLICTING, PR policy failed missing RAG/clinical body, Static failed Prettier, Build/Production UI failed typecheck, and Codex P1 thread `3651695530` was unresolved. `git merge-tree --write-tree origin/main 91aeb19d4d4856a3aa120265d3602414b472e19a` was clean, so merged `origin/main` without conflicts; fixed threshold inclusivity (`<`/`<=`, `>`/`>=` stay distinct), source-open telemetry types, missing `onOpenSource`, missing `SourceGovernanceWarning` export, and lint/Prettier issues. PR body/thread resolution/squash merge still require GitHub write tooling unavailable in this Cursor run, so DO NOT MERGE until those are completed and hosted required CI is green. | Focused Vitest evidence/source metadata 67/67 PASS; `npm run typecheck` PASS; `npm run format:check` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; `npm run check:production-readiness` failed only for missing local Supabase/OpenAI env secrets. No provider-backed evals/checks run. |
+| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | pending pushed head after CodeRabbit follow-up | PR babysit: evidence false-positive hardening | Follow-up to the prior #1254 row after CodeRabbit re-opened evidence threads on the pushed head. Fixed scoped source-governance issues: bare `clozapine` now only binds its own dose comparator when the captured threshold is mg-qualified; table/prose same-value unknown comparator is compatible with known comparator; cross-source conflicts require document-level disagreement rather than one internally inconsistent document plus another source repeating one side. Production UI hosted failure was a single `/tools` strict-locator browser flake; exact local production rerun passed. PR body metadata and reply/resolve remain blocked by missing GitHub write tooling in this run. | `npm run test -- tests/evidence.test.ts` PASS (26/26); `npm run typecheck` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; exact local `npm run test:e2e:pr -- tests/ui-tools.spec.ts --grep "mode home search is centered at desktop width on /tools"` PASS (1/1). No provider-backed checks run. |
diff --git a/src/lib/evidence.ts b/src/lib/evidence.ts
index de69079afa..08fbd0cc34 100644
--- a/src/lib/evidence.ts
+++ b/src/lib/evidence.ts
@@ -512,10 +512,13 @@ const THRESHOLD_PARAMETERS: ThresholdParameter[] = [
},
];
+const THRESHOLD_COMPARATOR_PATTERN =
+ "<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?";
+
// A threshold parameter within a short window of a comparator and a numeric value.
// Group 1 = parameter, group 2 = comparator phrase, group 3 = number.
const THRESHOLD_SPAN_PATTERN = new RegExp(
- `\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|clozapine(?=[^.\\n;]{0,40}?\\d+(?:\\.\\d+)?\\s*mg)|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(<|>|≤|≥|<=|>=|less than|greater than|below|above|under|over|lower than|higher than|fall(?:s|ing)? below|drops? below|exceeds?)\\s*(\\d+(?:\\.\\d+)?)`,
+ `\\b(anc|absolute neutrophil count|neutrophils?|wbc|white (?:blood )?cell(?: count)?|leu[ck]ocytes?|platelets?|lithium(?: serum)? levels?|serum lithium|li\\+? levels?|qtc|qt interval|clozapine dose|clozapine(?=[^.\\n;]{0,40}?(?:${THRESHOLD_COMPARATOR_PATTERN})\\s*\\d+(?:\\.\\d+)?\\s*mg\\b)|egfr|crcl|creatinine clearance|glomerular filtration rate)\\b[^.\\n;]{0,48}?(${THRESHOLD_COMPARATOR_PATTERN})\\s*(\\d+(?:\\.\\d+)?)`,
"gi",
);
@@ -568,6 +571,17 @@ function formatThresholdObservation(value: string, comparator: ThresholdComparat
return value;
}
+function thresholdObservationsCompatible(left: ThresholdObservation, right: ThresholdObservation) {
+ if (left.value !== right.value) return false;
+ return left.comparator === "unknown" || right.comparator === "unknown" || left.comparator === right.comparator;
+}
+
+function thresholdObservationsConflict(left: ThresholdObservation, right: ThresholdObservation) {
+ if (left.value !== right.value) return true;
+ if (left.comparator === "unknown" || right.comparator === "unknown") return false;
+ return left.comparator !== right.comparator;
+}
+
function thresholdParameterFor(raw: string): ThresholdParameter | undefined {
return THRESHOLD_PARAMETERS.find((parameter) => parameter.pattern.test(raw));
}
@@ -645,19 +659,44 @@ function collectWithholdThresholds(results: SearchResult[]): Map();
+ for (const observation of observations) {
+ const list = byDocument.get(observation.documentId) ?? [];
+ list.push(observation);
+ byDocument.set(observation.documentId, list);
+ }
+
+ const conflictingObservations: ThresholdObservation[] = [];
+ const documentEntries = [...byDocument.entries()];
+ for (let leftIndex = 0; leftIndex < documentEntries.length; leftIndex += 1) {
+ for (let rightIndex = leftIndex + 1; rightIndex < documentEntries.length; rightIndex += 1) {
+ const leftObservations = documentEntries[leftIndex][1];
+ const rightObservations = documentEntries[rightIndex][1];
+ const hasCompatibleObservation = leftObservations.some((left) =>
+ rightObservations.some((right) => thresholdObservationsCompatible(left, right)),
+ );
+ const conflictPair = leftObservations
+ .flatMap((left) =>
+ rightObservations
+ .filter((right) => thresholdObservationsConflict(left, right))
+ .map((right) => [left, right] as const),
+ )
+ .at(0);
+ if (!hasCompatibleObservation && conflictPair) {
+ conflictingObservations.push(...conflictPair);
+ }
+ }
+ }
+ if (conflictingObservations.length === 0) continue;
+
const distinctKeys = new Set(
- observations.map((observation) => thresholdObservationKey(observation.value, observation.comparator)),
+ conflictingObservations.map((observation) => thresholdObservationKey(observation.value, observation.comparator)),
);
- const distinctDocuments = new Set(observations.map((observation) => observation.documentId));
- // A cross-source conflict needs two different (comparator, value) keys from
- // two different documents; one document that contradicts itself, or agreeing
- // sources, are not flagged here.
- if (distinctKeys.size < 2 || distinctDocuments.size < 2) continue;
const label =
THRESHOLD_PARAMETERS.find((candidate) => candidate.key === parameterKey)?.label ?? "clinical threshold";
const values = [...distinctKeys]
.map((key) => {
- const observation = observations.find(
+ const observation = conflictingObservations.find(
(candidate) => thresholdObservationKey(candidate.value, candidate.comparator) === key,
);
return observation ? formatThresholdObservation(observation.value, observation.comparator) : key;
@@ -668,7 +707,7 @@ function detectThresholdDisagreements(results: SearchResult[]): ConflictOrGap[]
message: `Sources disagree on the ${label} withholding threshold (${values.join(
" vs ",
)}). Confirm the correct cut-off against the primary guideline before acting on any single source.`,
- source_chunk_ids: [...new Set(observations.map((observation) => observation.chunkId))].slice(0, 4),
+ source_chunk_ids: [...new Set(conflictingObservations.map((observation) => observation.chunkId))].slice(0, 4),
});
}
return conflicts;
diff --git a/tests/evidence.test.ts b/tests/evidence.test.ts
index bb8f85ce28..9440b7a1aa 100644
--- a/tests/evidence.test.ts
+++ b/tests/evidence.test.ts
@@ -451,4 +451,73 @@ describe("detectConflictsOrGaps — cross-source withholding-threshold disagreem
expect(found[0].message).toMatch(/≤ 500/);
expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["exclusive", "inclusive"]));
});
+
+ it("does not bind an incidental clozapine dose to a later QTc comparator", () => {
+ const results = [
+ result({
+ id: "qtc-upper",
+ document_id: "doc-qtc-upper",
+ content: "Withhold clozapine 300 mg daily if QTc > 500 ms.",
+ }),
+ result({
+ id: "qtc-lower",
+ document_id: "doc-qtc-lower",
+ content: "Withhold treatment when QTc < 500 ms pending cardiology review.",
+ }),
+ ];
+
+ const found = conflicts(results);
+ expect(found).toHaveLength(1);
+ expect(found[0].message).toMatch(/QTc/);
+ expect(found[0].message).not.toMatch(/Clozapine dose/);
+ expect(found[0].source_chunk_ids).toEqual(expect.arrayContaining(["qtc-upper", "qtc-lower"]));
+ });
+
+ it("treats a table fact with unknown comparator as compatible with prose at the same value", () => {
+ const results = [
+ result({
+ id: "table-doc",
+ document_id: "doc-table",
+ content: "See the monitoring table.",
+ table_facts: [
+ {
+ id: "tf-1",
+ document_id: "doc-table",
+ source_chunk_id: "table-doc",
+ source_image_id: null,
+ page_number: 1,
+ table_title: "QTc monitoring",
+ row_label: "Red",
+ clinical_parameter: "QTc",
+ threshold_value: "500",
+ action: "Withhold treatment",
+ },
+ ],
+ }),
+ result({
+ id: "prose-doc",
+ document_id: "doc-prose",
+ content: "Withhold treatment when QTc > 500 ms.",
+ }),
+ ];
+
+ expect(conflicts(results)).toEqual([]);
+ });
+
+ it("ignores one internally inconsistent document when another source repeats one side", () => {
+ const results = [
+ result({
+ id: "mixed-doc",
+ document_id: "doc-mixed",
+ content: "Withhold treatment when QTc > 500 ms. Withhold treatment when QTc < 500 ms.",
+ }),
+ result({
+ id: "agreeing-doc",
+ document_id: "doc-agreeing",
+ content: "Withhold treatment when QTc > 500 ms.",
+ }),
+ ];
+
+ expect(conflicts(results)).toEqual([]);
+ });
});
From 5b616da1f84ffde127473e327e3ab63369244749 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:34:45 +0000
Subject: [PATCH 08/11] test: scope ui assertions to visible containers
Co-authored-by: BigSimmo
---
docs/branch-review-ledger.md | 1 +
tests/ui-tools.spec.ts | 18 +++++++++++++-----
2 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index fd7f7b8093..23699d7772 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -1080,3 +1080,4 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-26 | PR #1248 / `cursor/fix-mode-switch-lag-22f6` | pending final pushed head after UI CI fix | PR babysit: Production UI failure fix | Supersedes the prior PR #1248 babysit row for hosted CI closeout. Production UI failed only on two branch-adjacent chrome tests after the P2 fix: restricted the browser query fallback to submitted searches (`run=1` + `q/query`) so route-owned query pages do not adopt submitted chrome, and aligned the phone-scroll reserve-transition assertion with the `data-reserve-transitioning` marker for no-dock/expired-marker states. | Hosted log diagnosis from run `30187649755`; local exact Chromium production rerun `npm run test:e2e:pr -- tests/ui-phone-scroll.spec.ts tests/ui-tools.spec.ts --grep "formulation/builder\\?mechanism=rumination|differentials presentation comparison"` PASS (2/2); no provider-backed checks. |
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | code fix `ee6a56bd0fc08ded140c36b5de213bda40d93179` | PR babysit: main sync + P1 inclusivity/static/build fix | Supersedes the earlier #1254 sweep rows for the new head after main advanced to `dbcd4cea605ee7f5af8f2f9b5ff22d18385131ea`. Before: GitHub reported DIRTY/CONFLICTING, PR policy failed missing RAG/clinical body, Static failed Prettier, Build/Production UI failed typecheck, and Codex P1 thread `3651695530` was unresolved. `git merge-tree --write-tree origin/main 91aeb19d4d4856a3aa120265d3602414b472e19a` was clean, so merged `origin/main` without conflicts; fixed threshold inclusivity (`<`/`<=`, `>`/`>=` stay distinct), source-open telemetry types, missing `onOpenSource`, missing `SourceGovernanceWarning` export, and lint/Prettier issues. PR body/thread resolution/squash merge still require GitHub write tooling unavailable in this Cursor run, so DO NOT MERGE until those are completed and hosted required CI is green. | Focused Vitest evidence/source metadata 67/67 PASS; `npm run typecheck` PASS; `npm run format:check` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; `npm run check:production-readiness` failed only for missing local Supabase/OpenAI env secrets. No provider-backed evals/checks run. |
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | pending pushed head after CodeRabbit follow-up | PR babysit: evidence false-positive hardening | Follow-up to the prior #1254 row after CodeRabbit re-opened evidence threads on the pushed head. Fixed scoped source-governance issues: bare `clozapine` now only binds its own dose comparator when the captured threshold is mg-qualified; table/prose same-value unknown comparator is compatible with known comparator; cross-source conflicts require document-level disagreement rather than one internally inconsistent document plus another source repeating one side. Production UI hosted failure was a single `/tools` strict-locator browser flake; exact local production rerun passed. PR body metadata and reply/resolve remain blocked by missing GitHub write tooling in this run. | `npm run test -- tests/evidence.test.ts` PASS (26/26); `npm run typecheck` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; exact local `npm run test:e2e:pr -- tests/ui-tools.spec.ts --grep "mode home search is centered at desktop width on /tools"` PASS (1/1). No provider-backed checks run. |
+| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | pending pushed head after Production UI locator hardening | PR babysit: Production UI strict-locator rerun fix | Hosted Production UI failed twice on different `tests/ui-tools.spec.ts` strict locators because duplicated page containers under `mobile-composer-reserve-pad` made `getByTestId(...)` ambiguous in full-suite browser state. Product code unchanged; tests now scope to visible/current page containers and the metrics helper measures a visible home container. PR body metadata and review-thread reply/resolve still require GitHub write tooling unavailable in this run. | Exact local production rerun `npm run test:e2e:pr -- tests/ui-tools.spec.ts --grep "mode home search is centered at desktop width on /tools|13YARN service detail is usable at mobile"` PASS (2/2). No provider-backed checks run. |
diff --git a/tests/ui-tools.spec.ts b/tests/ui-tools.spec.ts
index 5085802a6f..34b4a1168e 100644
--- a/tests/ui-tools.spec.ts
+++ b/tests/ui-tools.spec.ts
@@ -307,7 +307,13 @@ async function globalSearchComposerMetrics(page: Page, homeTestId?: string) {
.evaluate((input, homeTestId) => {
const form = input.closest("form");
const pill = input.closest(".answer-footer-search-pill");
- const home = homeTestId ? document.querySelector(`[data-testid="${homeTestId}"]`) : null;
+ const home = homeTestId
+ ? [...document.querySelectorAll(`[data-testid="${homeTestId}"]`)].find((candidate) => {
+ const rect = candidate.getBoundingClientRect();
+ const style = window.getComputedStyle(candidate);
+ return rect.width > 0 && rect.height > 0 && style.display !== "none" && style.visibility !== "hidden";
+ })
+ : null;
if (!form) return null;
const formRect = form.getBoundingClientRect();
@@ -837,12 +843,13 @@ test.describe("Clinical KB tools launcher", () => {
await mockAnswerDashboardApi(page);
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await gotoLauncher(page, home.path);
- await expect(page.getByTestId(home.testId)).toBeVisible();
+ const homeRoot = page.locator(`[data-testid="${home.testId}"]:visible`).last();
+ await expect(homeRoot).toBeVisible();
await expect(visibleGlobalSearchInput(page)).toHaveCount(1);
// From the tablet breakpoint up the composer is portaled into the hero
// (inside the mode-home container) rather than floated over the heading.
- const heroSearch = page.getByTestId(home.testId).getByTestId("global-search-input");
+ const heroSearch = homeRoot.getByTestId("global-search-input");
await expect(heroSearch).toBeVisible();
const searchBox = await heroSearch.boundingBox();
@@ -850,7 +857,8 @@ test.describe("Clinical KB tools launcher", () => {
// "Medication" hero title is otherwise a substring of the answer
// section's sr-only "Medication matches" heading (strict-mode clash).
const headingBox = await page
- .getByTestId(home.testId)
+ .locator(`[data-testid="${home.testId}"]:visible`)
+ .last()
.getByRole("heading", { level: home.headingLevel, name: home.heading, exact: true })
.boundingBox();
expect(searchBox).not.toBeNull();
@@ -2043,7 +2051,7 @@ test.describe("Clinical KB service detail page", () => {
await page.setViewportSize({ width: viewport.width, height: viewport.height });
await gotoLauncher(page, "/services/13yarn");
- const servicePage = page.getByTestId("service-detail-page");
+ const servicePage = page.locator('[data-testid="service-detail-page"]:visible').last();
const copyContactButton = servicePage.getByRole("button", { name: "Copy contact" }).last();
await expect(servicePage).toBeVisible();
await expect(servicePage.getByRole("heading", { level: 1, name: "13YARN" })).toBeVisible();
From a4c5f28606f106630d957c7f68869f00af0a6784 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:40:33 +0000
Subject: [PATCH 09/11] docs: clarify duplicate PR 1254 ledger row
Co-authored-by: BigSimmo
---
docs/branch-review-ledger.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md
index 23699d7772..268f6e8aa7 100644
--- a/docs/branch-review-ledger.md
+++ b/docs/branch-review-ledger.md
@@ -1081,3 +1081,4 @@ This file is append-only. Never rewrite or delete an existing review record; app
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | code fix `ee6a56bd0fc08ded140c36b5de213bda40d93179` | PR babysit: main sync + P1 inclusivity/static/build fix | Supersedes the earlier #1254 sweep rows for the new head after main advanced to `dbcd4cea605ee7f5af8f2f9b5ff22d18385131ea`. Before: GitHub reported DIRTY/CONFLICTING, PR policy failed missing RAG/clinical body, Static failed Prettier, Build/Production UI failed typecheck, and Codex P1 thread `3651695530` was unresolved. `git merge-tree --write-tree origin/main 91aeb19d4d4856a3aa120265d3602414b472e19a` was clean, so merged `origin/main` without conflicts; fixed threshold inclusivity (`<`/`<=`, `>`/`>=` stay distinct), source-open telemetry types, missing `onOpenSource`, missing `SourceGovernanceWarning` export, and lint/Prettier issues. PR body/thread resolution/squash merge still require GitHub write tooling unavailable in this Cursor run, so DO NOT MERGE until those are completed and hosted required CI is green. | Focused Vitest evidence/source metadata 67/67 PASS; `npm run typecheck` PASS; `npm run format:check` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; `npm run check:production-readiness` failed only for missing local Supabase/OpenAI env secrets. No provider-backed evals/checks run. |
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | pending pushed head after CodeRabbit follow-up | PR babysit: evidence false-positive hardening | Follow-up to the prior #1254 row after CodeRabbit re-opened evidence threads on the pushed head. Fixed scoped source-governance issues: bare `clozapine` now only binds its own dose comparator when the captured threshold is mg-qualified; table/prose same-value unknown comparator is compatible with known comparator; cross-source conflicts require document-level disagreement rather than one internally inconsistent document plus another source repeating one side. Production UI hosted failure was a single `/tools` strict-locator browser flake; exact local production rerun passed. PR body metadata and reply/resolve remain blocked by missing GitHub write tooling in this run. | `npm run test -- tests/evidence.test.ts` PASS (26/26); `npm run typecheck` PASS; `npm run lint` PASS; `npm run build` PASS; `npm run check:rag:fixtures` PASS; exact local `npm run test:e2e:pr -- tests/ui-tools.spec.ts --grep "mode home search is centered at desktop width on /tools"` PASS (1/1). No provider-backed checks run. |
| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | pending pushed head after Production UI locator hardening | PR babysit: Production UI strict-locator rerun fix | Hosted Production UI failed twice on different `tests/ui-tools.spec.ts` strict locators because duplicated page containers under `mobile-composer-reserve-pad` made `getByTestId(...)` ambiguous in full-suite browser state. Product code unchanged; tests now scope to visible/current page containers and the metrics helper measures a visible home container. PR body metadata and review-thread reply/resolve still require GitHub write tooling unavailable in this run. | Exact local production rerun `npm run test:e2e:pr -- tests/ui-tools.spec.ts --grep "mode home search is centered at desktop width on /tools|13YARN service detail is usable at mobile"` PASS (2/2). No provider-backed checks run. |
+| 2026-07-26 | PR #1254 / `apply-audit-remediation-fixes` | `5b616da1f84ffde127473e327e3ab63369244749` | PR babysit: ledger duplicate clarification | Clarifies the CodeRabbit duplicate-ledger thread without rewriting append-only history: the later `b3b1eb7e7084859cd18c05152be1b9f8968592ff` row at prior line 1072 is a superseding clarification of the earlier same-commit #1254 row, not a second independent sweep. PR body metadata and review-thread reply/resolve still require GitHub write tooling unavailable in this run, so DO NOT MERGE until those are completed and hosted required CI is green. | `npm run check:branch-review-ledger` required after this append; no provider-backed checks run. |
From c37ae5959dfae8befc9a65a9a9104442b7aefeef Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:52:55 +0000
Subject: [PATCH 10/11] docs: sync PR 1254 policy body
Co-authored-by: BigSimmo
---
PR_POLICY_BODY.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 PR_POLICY_BODY.md
diff --git a/PR_POLICY_BODY.md b/PR_POLICY_BODY.md
new file mode 100644
index 0000000000..fc749d8a83
--- /dev/null
+++ b/PR_POLICY_BODY.md
@@ -0,0 +1,46 @@
+## Summary
+
+- Implements audit remediation for provenance and safety: locality metadata verification tooling, centralized source-governance codes/UI tokens, expanded threshold disagreement handling, and citation/source-open telemetry.
+- Keeps live Supabase locality auditing explicit via `check:locality-metadata` and out of the unconditional offline `verify:pr-local` base script.
+- Follow-up babysit fixes preserve threshold comparator direction and inclusivity, harden false-positive source-governance threshold extraction, and stabilize PR-scoped UI assertions.
+
+RAG impact: no retrieval behaviour change — provenance/governance UI tokens, citation telemetry, and locality audit only; ranking/imputation formulas untouched
+
+## Verification
+
+- [x] `npm run test -- tests/evidence.test.ts` — PASS (26/26) after threshold false-positive hardening.
+- [x] Focused Vitest evidence/source metadata — PASS (67/67) after inclusivity/source telemetry fixes.
+- [x] `npm run typecheck` — PASS.
+- [x] `npm run format:check` — PASS.
+- [x] `npm run lint` — PASS.
+- [x] `npm run build` — PASS.
+- [x] `npm run check:rag:fixtures` — PASS.
+- [x] `npm run check:branch-review-ledger` — PASS after the append-only ledger clarification.
+- [x] Focused Production UI reruns for `tests/ui-tools.spec.ts` — PASS for the PR-scoped strict-locator failures.
+- [x] Hosted CI on head `a4c5f286`: Static PR, Safety and config, Unit coverage, Build, app image, Production UI, Migration replay, SAST, and secret scans passed.
+- [ ] `npm run verify:pr-local` — not rerun after the latest docs-only ledger clarification; narrower checks above cover the touched file.
+- [ ] `npm run verify:ui` — not rerun as the full local gate; focused production UI reruns and hosted Production UI passed.
+- UI verification not run: full local `npm run verify:ui` was not rerun after the docs-only ledger clarification; focused production UI reruns and hosted Production UI passed.
+- [ ] `npm run verify:release` — not run; release gate is out of scope for PR babysitting and includes provider-backed checks.
+- [ ] `npm run eval:retrieval:quality` — not run; no retrieval/ranking behaviour change is intended and live provider-backed eval was not authorized.
+- [ ] `npm run check:production-readiness` — attempted earlier and blocked by missing local Supabase/OpenAI env secrets; no live provider-backed rerun performed.
+
+## Risk and rollout
+
+- Risk: clinical/source-governance metadata changes can affect warnings and telemetry presentation; comparator parsing changes are covered by focused regressions and do not alter RAG ranking/imputation formulas.
+- Rollback: revert the PR commits; the live locality audit remains an explicit script and is not part of offline PR-local verification.
+- Provider or production effects: No OpenAI calls, live Supabase mutations, provider-backed evals, deployments, or production data changes were performed.
+
+## Clinical Governance Preflight
+
+- [x] Source-backed claims still require linked source verification before clinical use
+- [x] No patient-identifiable document workflow was introduced or expanded without explicit governance approval
+- [x] Supabase target remains `Clinical KB Database` (`sjrfecxgysukkwxsowpy`)
+- [x] Service-role keys and private document access remain server-only
+- [x] Demo/synthetic content remains clearly separated from real clinical sources
+- [x] Source metadata, review status, and outdated/unknown-source behavior remain conservative
+- [x] Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed
+
+## Notes
+
+- Remaining merge blockers at the time this body was prepared: direct review-thread reply/resolution was unavailable in this Cursor run, and the prior hosted worker-image failure was a Docker Hub/BuildKit timeout rather than a code failure.
From b5aa92f0febbd0425c5f3d809356946a7d2371f5 Mon Sep 17 00:00:00 2001
From: Cursor Agent
Date: Sun, 26 Jul 2026 05:53:24 +0000
Subject: [PATCH 11/11] docs: remove temporary PR policy body sync file
Co-authored-by: BigSimmo
---
PR_POLICY_BODY.md | 46 ----------------------------------------------
1 file changed, 46 deletions(-)
delete mode 100644 PR_POLICY_BODY.md
diff --git a/PR_POLICY_BODY.md b/PR_POLICY_BODY.md
deleted file mode 100644
index fc749d8a83..0000000000
--- a/PR_POLICY_BODY.md
+++ /dev/null
@@ -1,46 +0,0 @@
-## Summary
-
-- Implements audit remediation for provenance and safety: locality metadata verification tooling, centralized source-governance codes/UI tokens, expanded threshold disagreement handling, and citation/source-open telemetry.
-- Keeps live Supabase locality auditing explicit via `check:locality-metadata` and out of the unconditional offline `verify:pr-local` base script.
-- Follow-up babysit fixes preserve threshold comparator direction and inclusivity, harden false-positive source-governance threshold extraction, and stabilize PR-scoped UI assertions.
-
-RAG impact: no retrieval behaviour change — provenance/governance UI tokens, citation telemetry, and locality audit only; ranking/imputation formulas untouched
-
-## Verification
-
-- [x] `npm run test -- tests/evidence.test.ts` — PASS (26/26) after threshold false-positive hardening.
-- [x] Focused Vitest evidence/source metadata — PASS (67/67) after inclusivity/source telemetry fixes.
-- [x] `npm run typecheck` — PASS.
-- [x] `npm run format:check` — PASS.
-- [x] `npm run lint` — PASS.
-- [x] `npm run build` — PASS.
-- [x] `npm run check:rag:fixtures` — PASS.
-- [x] `npm run check:branch-review-ledger` — PASS after the append-only ledger clarification.
-- [x] Focused Production UI reruns for `tests/ui-tools.spec.ts` — PASS for the PR-scoped strict-locator failures.
-- [x] Hosted CI on head `a4c5f286`: Static PR, Safety and config, Unit coverage, Build, app image, Production UI, Migration replay, SAST, and secret scans passed.
-- [ ] `npm run verify:pr-local` — not rerun after the latest docs-only ledger clarification; narrower checks above cover the touched file.
-- [ ] `npm run verify:ui` — not rerun as the full local gate; focused production UI reruns and hosted Production UI passed.
-- UI verification not run: full local `npm run verify:ui` was not rerun after the docs-only ledger clarification; focused production UI reruns and hosted Production UI passed.
-- [ ] `npm run verify:release` — not run; release gate is out of scope for PR babysitting and includes provider-backed checks.
-- [ ] `npm run eval:retrieval:quality` — not run; no retrieval/ranking behaviour change is intended and live provider-backed eval was not authorized.
-- [ ] `npm run check:production-readiness` — attempted earlier and blocked by missing local Supabase/OpenAI env secrets; no live provider-backed rerun performed.
-
-## Risk and rollout
-
-- Risk: clinical/source-governance metadata changes can affect warnings and telemetry presentation; comparator parsing changes are covered by focused regressions and do not alter RAG ranking/imputation formulas.
-- Rollback: revert the PR commits; the live locality audit remains an explicit script and is not part of offline PR-local verification.
-- Provider or production effects: No OpenAI calls, live Supabase mutations, provider-backed evals, deployments, or production data changes were performed.
-
-## Clinical Governance Preflight
-
-- [x] Source-backed claims still require linked source verification before clinical use
-- [x] No patient-identifiable document workflow was introduced or expanded without explicit governance approval
-- [x] Supabase target remains `Clinical KB Database` (`sjrfecxgysukkwxsowpy`)
-- [x] Service-role keys and private document access remain server-only
-- [x] Demo/synthetic content remains clearly separated from real clinical sources
-- [x] Source metadata, review status, and outdated/unknown-source behavior remain conservative
-- [x] Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed
-
-## Notes
-
-- Remaining merge blockers at the time this body was prepared: direct review-thread reply/resolution was unavailable in this Cursor run, and the prior hosted worker-image failure was a Docker Hub/BuildKit timeout rather than a code failure.