From 129e3d5caccdb0539320e19053109edc17fc78e4 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 24 Jul 2026 15:37:41 +0800 Subject: [PATCH 1/4] fix: implement automated audit remediations --- scripts/guard-next-build.mjs | 14 ++++++++ src/app/globals.css | 14 ++++---- src/lib/env.ts | 1 + src/lib/rag/rag.ts | 15 ++++++++ supabase/drift-manifest.json | 4 +-- .../20260724000000_optimize_rpc_work_mem.sql | 13 +++++++ worker/main.ts | 36 +++++++++++++++---- 7 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 supabase/migrations/20260724000000_optimize_rpc_work_mem.sql diff --git a/scripts/guard-next-build.mjs b/scripts/guard-next-build.mjs index 618c782f6..29c8154da 100644 --- a/scripts/guard-next-build.mjs +++ b/scripts/guard-next-build.mjs @@ -1,10 +1,24 @@ #!/usr/bin/env node import http from "node:http"; import path from "node:path"; +import os from "node:os"; import { fileURLToPath } from "node:url"; import { appName, localProjectId, projectPortEnd, stableProjectPort } from "../src/lib/local-server-utils.mjs"; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const totalRamBytes = os.totalmem(); +const tenGiB = 10 * 1024 * 1024 * 1024; +if (totalRamBytes < tenGiB) { + console.error( + [ + `Host system has less than 10 GiB of total RAM (${(totalRamBytes / 1024 / 1024 / 1024).toFixed(1)} GiB).`, + "Building Next.js locally requires an 8 GiB Node heap. Your system may crash or OOM during the build.", + "If you are using Docker Desktop, increase the memory limit in settings.", + ].join("\n"), + ); + process.exit(1); +} const expectedProjectId = localProjectId(projectRoot); const identityPath = "/api/local-project-id"; const timeoutMs = 350; diff --git a/src/app/globals.css b/src/app/globals.css index 7e560bc47..34f850b57 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -475,12 +475,14 @@ body { overscroll-behavior-x: none; } -/* Interactive element defaults */ -button, -input, -textarea, -select { - font: inherit; +@layer base { + /* Interactive element defaults */ + button, + input, + textarea, + select { + font: inherit; + } } /* diff --git a/src/lib/env.ts b/src/lib/env.ts index 2441449b2..4c7c91112 100644 --- a/src/lib/env.ts +++ b/src/lib/env.ts @@ -34,6 +34,7 @@ const envSchema = z.object({ // with no destination configured accepts the event and reports it undelivered. SLACK_WEBHOOK_URL: z.string().url().optional(), DISCORD_WEBHOOK_URL: z.string().url().optional(), + WORKER_FAILURE_WEBHOOK_URL: z.string().url().optional(), NEXT_PUBLIC_LOCAL_NO_AUTH: z.enum(["true", "false"]).optional().default("false"), LOCAL_NO_AUTH: z.enum(["true", "false"]).optional().default("false"), LOCAL_NO_AUTH_OWNER_EMAIL: z.string().optional(), diff --git a/src/lib/rag/rag.ts b/src/lib/rag/rag.ts index 6f3836ed4..33842d404 100644 --- a/src/lib/rag/rag.ts +++ b/src/lib/rag/rag.ts @@ -1319,6 +1319,21 @@ export async function analyzeQueryWithClassifierFallback( analysis = { ...analysis, corpusGrounding: "inconclusive" }; } + // Finding #2: Deterministic fallback routing for short clinical queries. + // Short, bare clinical search queries (e.g., "bipolar disorder", "anorexia management") + // can be misclassified by the generative LLM. We route them deterministically. + if ( + query.trim().split(/\s+/).length <= 4 && + (analysis.documentTitleTerms.length > 0 || analysis.canonicalTerms.length > 0) + ) { + return { + ...analysis, + queryClass: "broad_summary", + needsClassifierFallback: false, + reasons: uniqueTextValues([...analysis.reasons, "deterministic_short_clinical_query_fallback"], 12), + } satisfies ClinicalQueryAnalysis; + } + if (!analysis.needsClassifierFallback || !env.OPENAI_API_KEY) return analysis; const memoKey = classifierVerdictMemoKey(query, analysis); diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json index b515a8845..2b85dc275 100644 --- a/supabase/drift-manifest.json +++ b/supabase/drift-manifest.json @@ -1,9 +1,9 @@ { - "generated_at": "2026-07-23T23:41:25.333Z", + "generated_at": "2026-07-24T07:36:57.656Z", "generator": "scripts/generate-drift-manifest.ts", "postgres_image": "supabase/postgres:17.6.1.127", "schema_sha256": "b00b143ba74e98525ba17f032245c05a7befff9adbc07c612f0f8de29855bc51", - "replay_seconds": 16, + "replay_seconds": 87, "snapshot": { "views": [ { diff --git a/supabase/migrations/20260724000000_optimize_rpc_work_mem.sql b/supabase/migrations/20260724000000_optimize_rpc_work_mem.sql new file mode 100644 index 000000000..268d9c14b --- /dev/null +++ b/supabase/migrations/20260724000000_optimize_rpc_work_mem.sql @@ -0,0 +1,13 @@ +set search_path = public, extensions, pg_temp; + +-- Hybrid RPCs +ALTER FUNCTION public.match_document_chunks_hybrid(extensions.vector, text, integer, double precision, uuid[], uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_embedding_fields_hybrid(extensions.vector, text, integer, double precision, uuid[], uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_index_units_hybrid(extensions.vector, text, integer, double precision, uuid[], uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_memory_cards_hybrid_v2(extensions.vector, text, integer, double precision, uuid[], uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_memory_cards_hybrid(extensions.vector, text, integer, double precision, uuid[], uuid) SET work_mem = '64MB'; + +-- Lexical RPCs +ALTER FUNCTION public.match_document_chunks_text(text, integer, uuid[], uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_lookup_chunks_text(text, uuid[], integer, uuid) SET work_mem = '64MB'; +ALTER FUNCTION public.match_document_table_facts_text(text, integer, uuid[], uuid) SET work_mem = '64MB'; diff --git a/worker/main.ts b/worker/main.ts index 0d0fef3e0..db79759dd 100644 --- a/worker/main.ts +++ b/worker/main.ts @@ -1612,11 +1612,22 @@ async function processJob(job: JobRow) { if (!atomicReindex) await resetDocumentIndex(job.document_id); const buffer = await downloadDocument(job.documents.storage_path); await updateJobProgress(job.id, { stage: "extracting text/images", progress: 20 }); - extracted = await extractDocument({ - buffer, - fileName: job.documents.file_name, - mimeType: job.documents.file_type, - }); + // Finding #7: Ingestion Worker Heartbeat. Prevent stale locks during long PDF extractions. + const heartbeat = setInterval( + () => { + updateJobProgress(job.id, { stage: "extracting text/images", progress: 20 }).catch(() => {}); + }, + Math.min(60_000, jobLeaseHeartbeatMs), + ); + try { + extracted = await extractDocument({ + buffer, + fileName: job.documents.file_name, + mimeType: job.documents.file_type, + }); + } finally { + clearInterval(heartbeat); + } await updateJobProgress(job.id, { stage: "saving pages", progress: 32 }); const pageRows = buildDocumentPageRows(job.document_id, extracted); @@ -1936,7 +1947,20 @@ async function main() { } } -main().catch((error) => { +main().catch(async (error) => { console.error("Clinical KB worker stopped unexpectedly", safeErrorLogDetails(error)); + if (env.WORKER_FAILURE_WEBHOOK_URL) { + try { + await fetch(env.WORKER_FAILURE_WEBHOOK_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + text: `CRITICAL: Clinical KB worker stopped unexpectedly. Error: ${error instanceof Error ? error.message : String(error)}`, + }), + }); + } catch (webhookError) { + console.error("Failed to dispatch worker failure webhook", safeErrorLogDetails(webhookError)); + } + } process.exitCode = 1; }); From e93703688f2747e97d74457fdc6fbc9c0d7442d1 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 24 Jul 2026 08:14:03 +0000 Subject: [PATCH 2/4] fix: preserve classifier fallthrough for RAG tests Co-authored-by: BigSimmo --- src/lib/rag/rag.ts | 2 ++ tests/corpus-grounding.test.ts | 5 +++-- tests/rag-answer-fallback.test.ts | 4 ++-- tests/rag-classifier-memo.test.ts | 6 +++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib/rag/rag.ts b/src/lib/rag/rag.ts index 33842d404..1ee6b0f9e 100644 --- a/src/lib/rag/rag.ts +++ b/src/lib/rag/rag.ts @@ -1323,6 +1323,8 @@ export async function analyzeQueryWithClassifierFallback( // Short, bare clinical search queries (e.g., "bipolar disorder", "anorexia management") // can be misclassified by the generative LLM. We route them deterministically. if ( + analysis.needsClassifierFallback && + analysis.corpusGrounding !== "inconclusive" && query.trim().split(/\s+/).length <= 4 && (analysis.documentTitleTerms.length > 0 || analysis.canonicalTerms.length > 0) ) { diff --git a/tests/corpus-grounding.test.ts b/tests/corpus-grounding.test.ts index ddf9a3772..88a2d1599 100644 --- a/tests/corpus-grounding.test.ts +++ b/tests/corpus-grounding.test.ts @@ -324,9 +324,10 @@ describe("analyzeQueryWithClassifierFallback corpus grounding", () => { }, })); const { rag, analyzeClinicalQuery } = await loadRag({ classifierMock, rows: [] }); - const analysis = analyzeClinicalQuery("bipolar disorder"); + const query = "bipolar disorder long term care"; + const analysis = analyzeClinicalQuery(query); - const result = await rag.analyzeQueryWithClassifierFallback("bipolar disorder", analysis); + const result = await rag.analyzeQueryWithClassifierFallback(query, analysis); expect(classifierMock).toHaveBeenCalledTimes(1); expect(result.queryClass).toBe("broad_summary"); diff --git a/tests/rag-answer-fallback.test.ts b/tests/rag-answer-fallback.test.ts index 57c54c7b0..9b88f18ae 100644 --- a/tests/rag-answer-fallback.test.ts +++ b/tests/rag-answer-fallback.test.ts @@ -887,7 +887,7 @@ describe("RAG structured-output fallback", () => { const { answerQuestionWithScope } = await import("../src/lib/rag/rag"); const answer = await answerQuestionWithScope({ - query: "what is bulimia nervosa", + query: "what is bulimia nervosa in adults", ownerId: undefined, logQuery: false, skipCache: true, @@ -1721,7 +1721,7 @@ describe("RAG structured-output fallback", () => { const { answerQuestionWithScope } = await import("../src/lib/rag/rag"); const answer = await answerQuestionWithScope({ - query: "what is bulimia nervosa", + query: "what is bulimia nervosa in adults", ownerId: undefined, logQuery: false, skipCache: true, diff --git a/tests/rag-classifier-memo.test.ts b/tests/rag-classifier-memo.test.ts index 6d98a3a9d..60958362b 100644 --- a/tests/rag-classifier-memo.test.ts +++ b/tests/rag-classifier-memo.test.ts @@ -40,9 +40,9 @@ function classifierResponse(overrides: Record = {}) { function fallbackQueryAnalysis( analyzeClinicalQuery: (typeof import("../src/lib/clinical-search"))["analyzeClinicalQuery"], ) { - // A bare condition query is exactly the class that needs the LLM fallback (deterministic - // confidence below 0.58 with class unsupported_or_general) — the finding #11 shape. - const query = "bipolar disorder"; + // A bare condition query above the short-query deterministic fallback threshold still needs + // the LLM fallback (confidence below 0.58 with class unsupported_or_general). + const query = "bipolar disorder long term care"; const analysis = analyzeClinicalQuery(query); expect(analysis.needsClassifierFallback).toBe(true); return { query, analysis }; From e35fa15df5c6a3376bcb9f628dbdf99f8fb6809c Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:28:37 +0800 Subject: [PATCH 3/4] docs: record Run PR behind-main sweep for #1158 --- 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 8717bb536..b3cd9e7c8 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -731,3 +731,4 @@ This file is append-only. Never rewrite or delete an existing review record; app - Outcome: No new P0/P1 search chrome defect found in the static review. Fixed one regression hazard: a stale ClinicalDashboard comment still instructed a 0.75rem hidden dock pad despite the implementation/tests requiring 0rem. Added durable search chrome behaviour rules in AGENTS.md and docs/search-chrome-behaviour.md, with a static guard tying the remembered rules to the hidden-reserve contract. - Checks: dependency shortcut section count; git diff --check; targeted rg for stale 0.75rem hidden-pad source wording (only negative test assertions remain); targeted Vitest command attempted but blocked by missing node_modules/vitest under Node 20.20.2 in this container. No provider-backed checks run. | 2026-07-24 | cursor/search-interactive-perf-af54 (PR #1138) | ff4b293d95f922e70ebf5ee9b0c156c41a8bff3b | Run PR sweep: CI fix + threads + drift | Before: CONFLICTING, CI green, 0 threads. RAG impact: no retrieval behaviour change — PR is client deferred-search/UI only (no src/lib/rag/**). After: merged origin/main; conflict resolved in src/components/ui/sheet.tsx by keeping main restoreTimersRef/unmountingRef focus-restore fix; pushed ff4b293d9. Threads: none. | merge origin/main only; no provider-backed checks run | +| 2026-07-24 | codex/audit-remediation-final (PR #1158) | 4bfaf2a77a5c8cc0e6c48ee27a72a2faad203dd3 | Run PR sweep: CI fix + threads + drift | Before: behind main by 64. After: merged origin/main cleanly (no conflicts). Threads: non-P0/P1 left open. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | From 2bd74cd2e5273f490d9eceebc056735a6fabca35 Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Fri, 24 Jul 2026 19:57:01 +0800 Subject: [PATCH 4/4] docs: ledger re-sync sweep for PR #1158 --- 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 ddd4d38c0..71cb3f583 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -738,3 +738,4 @@ This file is append-only. Never rewrite or delete an existing review record; app | 2026-07-24 | remediate-audit-system-issues (PR #1160) | 04201a87cc7ad7dd1477d17cd2b96544b7379789 | Run PR sweep: CI fix + threads + drift | re-merge origin/main after CONFLICTING relapse; resolved scripts/run-eval-safe.mjs (kept main taskkill /T /F + validPids); sitemap Prettier format() retained from fe0588b86; no unresolved threads; no conflict markers | merge + conflict resolve only; no provider-backed checks run | | 2026-07-24 | codex/fix-next.js-startup-failure-and-verify-pages (PR #1149) | 8ddddbab2a29a94b3f993cbd114889f72c95f4f1 | Run PR sweep: CI fix + threads + drift | Before: behind main. After: merged origin/main cleanly (no conflicts). Unresolved review threads left as non-P0/P1. CI not waited. | merge origin/main only; thread scan read-only; no provider-backed checks run | | 2026-07-24 | remediate-audit-system-issues (PR #1160) | bdf530fc8c6faaa4491c510396b47872fc39bf25 | Run PR sweep: CI fix + threads + drift | second re-merge after main moved to 2e68888f3 during first push; clean ort merge (ledger + layout.tsx); taskkill /T retained; sitemap prettier retained | merge only; no provider-backed checks run | +| 2026-07-24 | codex/audit-remediation-final (PR #1158) | 78fab6be0c43cf5e92361315399d393ee7742f2e | Run PR re-sync sweep | Before: CONFLICTING. After: merged origin/main clean (no RAG conflict markers). NOTE: PR still intentionally adds deterministic broad_summary queryClass shortcut in src/lib/rag/rag.ts (+17) — RAG impact behaviour change, not dropped during merge. | merge origin/main and/or conflict re-check only; no provider-backed checks run |