From cb519d4eb0e9a853203968585fc8919bb45838de Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 06:07:49 +0000 Subject: [PATCH] fix(rag): mirror scoped corrector into schema.sql + regenerate drift manifest (F10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the F10 corrector tenancy fix. Migration 20260717120000_corrector_public_titles_only (merged in #697) scoped the SECURITY DEFINER query-term corrector's vocabulary — both rag_aliases reads and the document-title scan — to the public corpus (owner_id is null). This mirrors that same predicate into the canonical supabase/schema.sql corrector definition so a disaster-recovery restore or manifest/schema reconciliation cannot recreate the unscoped, cross-tenant vocabulary (Codex review P1 on #697). Regenerated supabase/drift-manifest.json via `npm run drift:manifest` (real Docker Postgres replay): schema_sha256 and only the corrector function's def_hash change (8f1094c4… → a5ebdd1e…), confirming the sole schema delta is the scoped corrector. Offline drift-detection + schema tests pass (66). Live apply of the migration to the Clinical KB Database project remains a separate, confirmation-gated deploy-pipeline step (needs service-role env); after applying, `npm run check:drift` should report clean against live. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01FJpsbpsmjJ2tLLXFfSe9GK --- supabase/drift-manifest.json | 8 ++++---- supabase/schema.sql | 12 ++++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json index ca34076e2..c54ee62fd 100644 --- a/supabase/drift-manifest.json +++ b/supabase/drift-manifest.json @@ -1,9 +1,9 @@ { - "generated_at": "2026-07-14T06:09:26.377Z", + "generated_at": "2026-07-17T06:06:10.141Z", "generator": "scripts/generate-drift-manifest.ts", "postgres_image": "supabase/postgres:17.6.1.127", - "schema_sha256": "cb0ddc6b9c5926d6c959fdb561a0ecf0fdc644da20dcea732f2353cb7cf44111", - "replay_seconds": 17, + "schema_sha256": "b9faccf4dac098def5d9547ca1b4cf0bb24621f1b76e9aed1b57d2495b65d72b", + "replay_seconds": 60, "snapshot": { "views": [ { @@ -6337,7 +6337,7 @@ "postgres=X/postgres", "service_role=X/postgres" ], - "def_hash": "8f1094c4e0c40dfd99a87a455037a62e", + "def_hash": "a5ebdd1ec14008cdf0c8601a32b4d24c", "signature": "public.correct_clinical_query_terms(text,real)" }, { diff --git a/supabase/schema.sql b/supabase/schema.sql index 9171df2fc..163495444 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -3487,15 +3487,19 @@ begin return input_query; end if; - -- Build the known-term vocabulary once per call. + -- Build the known-term vocabulary once per call. Every source is scoped to the + -- public (null-owner) corpus: this function is SECURITY DEFINER and bypasses RLS, and + -- both rag_aliases and documents carry owner-scoped private rows (deep-memory persists + -- owner-scoped aliases/canonicals), so an unscoped read would leak private-document + -- terms across tenants. Mirrors migration 20260717120000_corrector_public_titles_only. select array_agg(distinct term) into vocab from ( - select lower(alias) as term from public.rag_aliases where enabled and length(alias) between 4 and 40 + select lower(alias) as term from public.rag_aliases where enabled and owner_id is null and length(alias) between 4 and 40 union - select lower(canonical) from public.rag_aliases where enabled and length(canonical) between 4 and 40 + select lower(canonical) from public.rag_aliases where enabled and owner_id is null and length(canonical) between 4 and 40 union select w from public.documents d, lateral unnest(regexp_split_to_array(lower(d.title), '[^a-z]+')) as w - where d.status = 'indexed' and length(w) between 4 and 40 + where d.status = 'indexed' and d.owner_id is null and length(w) between 4 and 40 ) t; tokens := regexp_split_to_array(lower(input_query), '\s+');