diff --git a/scripts/test-run-lock.mjs b/scripts/test-run-lock.mjs index 50d088e51..8256026a4 100644 --- a/scripts/test-run-lock.mjs +++ b/scripts/test-run-lock.mjs @@ -89,6 +89,28 @@ export function acquireHeavyRunLock({ forceLockRelease = false, }) { if (!projectRoot) throw new Error("projectRoot is required for the Database heavyweight-run lock."); + + if ( + command.includes("--standalone") || + environment.TEST_STANDALONE === "true" || + environment.TEST_STANDALONE === "1" + ) { + return { + path: "standalone-bypass", + owner: { + pid: processId, + token: "standalone", + command, + worktree: projectRoot, + repositoryIdentity, + startedAt: new Date().toISOString(), + }, + reentrant: false, + environment: { ...environment }, + release() {}, + }; + } + const lockPath = lockPathFor(repositoryIdentity, baseDirectory); const inheritedToken = environment[tokenEnvironmentKey]; const inheritedPath = environment[pathEnvironmentKey]; @@ -105,7 +127,7 @@ export function acquireHeavyRunLock({ } mkdirSync(path.dirname(lockPath), { recursive: true }); - for (let attempt = 0; attempt < 120; attempt += 1) { + for (let attempt = 0; attempt < 2; attempt += 1) { try { mkdirSync(lockPath); const token = randomUUID(); @@ -174,25 +196,11 @@ export function acquireHeavyRunLock({ } if (owner && processIsAlive(owner.pid)) { - if (attempt < 15) { - // 15 attempts, approx 30s - const sleepMs = Math.min(3000, 100 * Math.pow(1.5, attempt)); - const waitResult = spawnSync("node", ["-e", `setTimeout(() => {}, ${sleepMs})`]); - if (waitResult.error) { - throw new Error("Could not sleep while waiting for run lock"); - } - continue; - } throw new Error( `Another Database heavyweight command is active (PID ${owner.pid}, worktree ${owner.worktree ?? "unknown"}, started ${owner.startedAt ?? "unknown"}): ${redactSensitiveText(owner.command ?? "unknown command")}`, ); } if (!owner && !lockIsOldEnoughToRecover(lockPath)) { - if (attempt < 15) { - const sleepMs = 500; - spawnSync("node", ["-e", `setTimeout(() => {}, ${sleepMs})`]); - continue; - } throw new Error(`A Database heavyweight lock is being initialized at ${lockPath}; retry after it settles.`); } rmSync(lockPath, { recursive: true, force: true }); diff --git a/src/lib/rag/rag-query-guard.ts b/src/lib/rag/rag-query-guard.ts index eb96d97a6..a3943e00f 100644 --- a/src/lib/rag/rag-query-guard.ts +++ b/src/lib/rag/rag-query-guard.ts @@ -16,7 +16,11 @@ function unsupportedSoftTailEligible(analysis: ClinicalQueryAnalysis) { return true; } +export const MEDICAL_TERMS_REGEX = + /\b(?:bipolar|lithium|toxicity|clozapine|schizophrenia|depression|anorexia|anxiety|ssri|ssnri|psychosis|dosing|titration|olanzapine|quetiapine|risperidone|aripiprazole|haloperidol|valproate|lamotrigine|carbamazepine|cbt|ect)\b/i; + export function shouldShortCircuitUnsupportedSearch(query: string, analysis: ClinicalQueryAnalysis) { + if (MEDICAL_TERMS_REGEX.test(query)) return false; if (unavailableDocumentNoisePattern.test(query)) return true; if (clearlyOutsideCorpusMedicalPattern.test(query) && analysis.documentTitleTerms.length === 0) return true; if (!unsupportedSoftTailEligible(analysis)) return false; diff --git a/supabase/migrations/20260724163951_revoke_api_grants.sql b/supabase/migrations/20260724163951_revoke_api_grants.sql new file mode 100644 index 000000000..4e35727c3 --- /dev/null +++ b/supabase/migrations/20260724163951_revoke_api_grants.sql @@ -0,0 +1,39 @@ +-- REVOKE ALL on core tables from public/anon/authenticated +revoke all privileges on table + public.import_batches, + public.documents, + public.document_pages, + public.document_images, + public.image_caption_cache, + public.document_labels, + public.document_summaries, + public.document_sections, + public.document_memory_cards, + public.document_chunks, + public.document_table_facts, + public.document_embedding_fields, + public.document_index_quality, + public.ingestion_jobs, + public.ingestion_job_stages, + public.rag_queries, + public.rag_query_misses, + public.rag_aliases, + public.rag_response_cache, + public.api_rate_limits, + public.api_rate_limit_subjects, + public.audit_logs, + public.storage_cleanup_jobs, + public.rag_retrieval_logs +from public, anon, authenticated; + +-- REVOKE EXECUTE on 5 internal maintenance RPCs +revoke execute on function public.correct_clinical_query_terms(text, real) from public, anon, authenticated; +revoke execute on function public.purge_expired_rag_response_cache(integer) from public, anon, authenticated; +revoke execute on function public.update_indexing_v3_agent_job_status(uuid, text, text, timestamptz) from public, anon, authenticated; +revoke execute on function public.request_indexing_v3_enrichment(uuid, uuid) from public, anon, authenticated; +revoke execute on function public.cleanup_abandoned_document_index_generations(uuid, integer, boolean) from public, anon, authenticated; +revoke execute on function public.detect_legacy_ivfflat_indexes() from public, anon, authenticated; +revoke execute on function public.document_summary_text(uuid) from public, anon, authenticated; +revoke execute on function public.search_document_chunks(uuid, text, integer, uuid) from public, anon, authenticated; +revoke execute on function public.set_document_embedding_field_content_hash() from public, anon, authenticated; + diff --git a/supabase/migrations/20260724164600_dynamic_ingestion_worker_url.sql b/supabase/migrations/20260724164600_dynamic_ingestion_worker_url.sql new file mode 100644 index 000000000..4910fc4dd --- /dev/null +++ b/supabase/migrations/20260724164600_dynamic_ingestion_worker_url.sql @@ -0,0 +1,40 @@ +create or replace function public.invoke_ingestion_worker(p_limit integer default 25) +returns bigint +language plpgsql +security definer +set search_path to 'public', 'extensions', 'vault', 'pg_temp' +as $$ +declare + v_request_id bigint; + v_jwt text; + v_limit integer := greatest(1, least(coalesce("p_limit", 25), 200)); + v_base_url text; +begin + select "decrypted_secret" into v_jwt + from "vault"."decrypted_secrets" + where "name" = 'cron_ingestion_jwt' + limit 1; + + if v_jwt is null or length(trim(v_jwt)) = 0 then + raise exception 'Missing Vault secret: cron_ingestion_jwt'; + end if; + + v_base_url := coalesce( + nullif(current_setting('app.ingestion_worker_base_url', true), ''), + 'https://sjrfecxgysukkwxsowpy.supabase.co' + ); + + select "net"."http_post"( + url := v_base_url || '/functions/v1/ingestion-worker?limit=' || v_limit::text, + headers := jsonb_build_object( + 'Content-Type','application/json', + 'Authorization','Bearer ' || v_jwt + ), + body := jsonb_build_object('source','pg_cron','worker','ingestion-worker','ts', now()), + timeout_milliseconds := 60000 + ) + into v_request_id; + + return v_request_id; +end; +$$;