fix(db): reconcile live database drift and harden security without RAG regression - #300
Conversation
- Degrade invalid bearer tokens to anonymous scope instead of 401 - Allow public document read routes (list, detail, signed-url, search, images) - Align registry routes with medications/differentials auth-signal short-circuit - Let DocumentViewer load public sources without requiring sign-in - Add regression tests and update access-control expectations
Create missing retrieval-support indexes (trgm, composite btree, partial miss log) that were absent or only present under legacy names on live. Update search_schema_health() to accept verified functional equivalents during rollout. Set search_path for pg_trgm gin_trgm_ops in extensions. Verified on linked project: search_schema_health() ok=true, missing=[].
- Re-apply indexing_v3_agent_jobs table and claim/update RPCs on live - Codify match_document_embedding_fields_text with service_role-only execute - Enable RLS on rag_visual_eval_* tables - Fix edge function JSONB status RPC parsing - Harden owner-scope and health deep-probe gating - Restore .env.example; remove unused postgres npm dep - Make gold-label governance advisory-only
- Re-apply indexing_v3_agent_jobs table and claim/update RPCs on live - Codify match_document_embedding_fields_text with service_role-only execute - Enable RLS on rag_visual_eval_* tables - Fix edge function JSONB status RPC parsing - Harden owner-scope and health deep-probe gating - Restore .env.example; remove unused postgres npm dep - Make gold-label governance advisory-only
Updates to Preview Branch (cursor/fix-all-db-issues-5f13) ↗︎
Tasks are run on every commit but only new migration files are pushed.
View logs for this Workflow Run ↗︎. |
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
09b3567 to
8ab94eaCompareCo-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
Co-authored-by: BigSimmo <BigSimmo@users.noreply.github.com>
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR reconciles observed drift between the repository and the live Supabase project schema, tightens owner-scoped retrieval boundaries, and hardens operational endpoints (health + indexing agent RPC parsing) while aiming to avoid any RAG scoring/ranking/chunking changes.
Changes:
- Codifies live database drift via new/idempotent migrations and updates
supabase/schema.sqlto match (jobs table/RPCs, embedding-fields RPC grants, visual eval tables + RLS, search_document_chunks scope). - Hardens
/api/health?deep=1by requiring an operator secret header for the deep Supabase probe, and updates deep public-access tests accordingly. - Fixes indexing-v3-agent edge function handling of JSONB-returning status RPCs via robust result parsing.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/supabase-schema.test.ts | Adds assertions to ensure schema + key migrations mirror live drift fixes and owner-scope tightening. |
| tests/public-access-deep.test.ts | Updates health deep-probe expectations to reflect new secret-gated behavior. |
| supabase/schema.sql | Mirrors drift reconciliation (drop/recreate function signature, adds search_document_chunks, embedding-fields RPC, visual eval tables + RLS/policies, grants). |
| supabase/migrations/20260705220000_reconcile_live_database_drift.sql | New idempotent migration to re-apply missing live objects and lock down permissions/RLS. |
| supabase/migrations/20260705210000_retrieval_owner_filter_sentinel.sql | Adds a defensive DROP to handle signature/OUT-column drift before CREATE OR REPLACE. |
| supabase/migrations/20260705133000_tighten_search_document_chunks_owner_scope.sql | Documents/codifies live owner-scope tightening and revokes public/anon/auth execute. |
| supabase/functions/indexing-v3-agent/index.ts | Uses typed RPC rows + parsing helper for JSONB-returning job-status RPCs. |
| src/lib/env.ts | Adds SUPABASE_DB_URL and HEALTH_DEEP_PROBE_SECRET to the validated env schema. |
| src/lib/document-label-governance.ts | Treats missing gold-label rows as non-blocking (advisory) in the overall pass/fail result. |
| src/app/api/health/route.ts | Adds constant-time header token check for deep health probe authorization; returns 503 on unauthorized deep probe. |
| scripts/check-document-label-governance.ts | Clarifies CLI output that missing gold-label rows are advisory. |
| package.json | Removes the postgres dependency. |
| docs/supabase-migration-reconciliation.md | Documents the newly codified live drift and how it’s reconciled. |
| docs/process-hardening.md | Adds a dated log entry describing drift reconciliation and follow-ups. |
| .env.example | Adds SUPABASE_DB_URL and HEALTH_DEEP_PROBE_SECRET examples for operators/devs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
BigSimmo
commented
Jul 6, 2026
@copilot resolve the merge conflicts in this pull request |
…ope and commit-gen RPC test cases
Done — merged
Resolved in merge commit |
Uh oh!
There was an error while loading. Please reload this page.
…1786) * feat(design-system): build ErrorState, the gate with nothing behind it GATES.md §3 lists the prohibition "Render '0 matches' after a failed request" with the gate "ErrorState adoption + check", status planned. Measured at origin/main 199b303, ErrorState existed nowhere in src or tests — only in COMPONENTS.md, GATES.md and SPEC.md. This builds it. The invariant is clinical, not cosmetic. A search that failed has no count to report, so reporting zero is a false statement about the corpus: on the services page "0 matches" asserts there are no crisis services when the search never ran, and on favourites it reads as "you have saved nothing" rather than "we could not load them". COMPONENTS.md:322 draws the same line from the other side — "no result count is available" is not a MissingValue. The component therefore takes no count and no children. There is no prop through which a number can arrive, and the generated dtsPropsFor entry now records that as the published API. The one remaining route, a caller writing a count into title or body, is covered by a development-time tripwire that matches a figure against a counted noun ("0 matches", "no results") so an error code or a duration does not trip it. It warns and never throws: on the one screen already reporting a failure, a thrown error is a blank page. Requirements came from the surfaces that hand-roll this guard today. Three do, and their comments state the rule outright: search-results-header-band.tsx:210 ("no number may reach the DOM"), services-navigator-page.tsx:634 ("a blocked registry must not reach the band as '0 matches'") and favourites-command-library-page.tsx:1182. They are correct, just not shared; converting them is a live-look change and deliberately not here. Three further sites carried into this task as hand-rolled guards are not that, measured at this HEAD, and are recorded so the next reader does not convert them: differentials-home.tsx:716,729 renders "0 matches"/"No matches" when sourcesChecked is true, i.e. a legitimate zero after a search that SUCCEEDED; specifiers-home-page.tsx:211 is a comment about not showing a stale zero above real catalogue results, and lives in src/components/specifiers/, not clinical-dashboard/; document-search-results gates on recordStatus for loading, not for a failed count. Registered per gates 11 and 12: source, design-sync export, preview, prop contract, publication test entry, behavioural DOM tests, adoption-contract family, and both generators regenerated (54 components, 59 roots). Uses the shared floatingControl recipe rather than a hand-rolled control, so the tap floor, focus ring and forced-colors border come from one owner. Adds zero arbitrary padding/gap/radius/line-height, so the ratchet landed in the previous commit is unaffected and the two stay separately revertible. Scope: ErrorState only. OfflineState, PermissionDeniedState, NotFoundState and UnavailableState share the pattern but have no gate pointing at them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(design-system): ratchet raw gap literals, the family #1780 left uncovered #1780 landed rawPaddingLiterals, rawRadiusLiterals and rawLineHeightLiterals for #262 part 3. Gap was the one remaining spacing surface a hand-picked value could hide in: gap-[9px] and `gap: 18px` were counted by no ratchet at all. This adds rawGapLiterals on that commit's own predicate and wiring. Measured against origin/main ef9bb51: 34 sites across 9 files — 21 Tailwind utilities, every one under src/components/therapy-compass/, plus 13 CSS declarations in globals.css that a utility-only scan misses. Covering both spellings is the same reason #1780 counts both: otherwise a literal escapes by moving from a class into globals.css. Kept as its own metric rather than folded into rawPaddingLiterals so the therapy-compass cleanup can be paid down and re-pinned independently of the padding debt, which is spread across fifteen unrelated files. Reuses RAW_LITERAL_VALUE unchanged, so a value containing a CSS function (env(, clamp(, max(, calc() stays a sanctioned computed form and is exempt. Also corrects the §3 prohibition row, which #1780 left reading "implemented-partial (colour/shadow/tap literals only)" and which named none of the metrics it had just shipped. A row that understates shipped work is what sends the next session to rebuild it — this change was itself started as a duplicate of #262 part 3 for exactly that reason. Mutation-verified in both halves, each naming the metric and the path: a gap-[3px] utility gives "rawGapLiterals increased from 34 to 35" plus "at src/components/ui/missing-value.tsx increased from 0 to 1"; a `gap: 19px` declaration gives the same total plus "at src/app/globals.css increased from 13 to 14". Baseline diff is additive only. legacyShadowAliases measures 218 against its pinned 220 on main; that slack is left exactly as found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(issues): capture the ErrorState and duplicate-work follow-ups Four rows (#298-#301), added with npm run issues:add. Ids 295-297 were claimed by main while this branch was open, which is ledger #156's read-modify-write race behaving exactly as recorded. - #298 (P2 task) the ErrorState enforcement check. GATES.md still reads "planned" for the 0-matches prohibition and nothing in scripts/ or eslint-rules/ references ErrorState, so the component exists but is not required. Deliberately not flipped to implemented. - #299 (P3 task) adopting ErrorState at the three surfaces that genuinely hand-roll the guard. Live-look change, downstream of the redesign. - #300 (P2 issue) three sites miscarried into M4 as guards that are not, so the next reader does not convert them. differentials-home renders its zero after a search that SUCCEEDED. - #301 (P3 issue) two sessions built #262 part 3 in parallel because the §3 row understated what had shipped. Proposes asserting that every baseline metric key appears in GATES.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(design-system): restrict the ErrorState copy tripwire to development Codex review on #1786 (P2). The doc comment described the tripwire as development-only, but the emitter only silenced NODE_ENV === "test", so a production caller supplying count-bearing title/body copy had the full caller-provided string written to console.warn. On a clinical surface that copy can quote the query — "0 results for <query>" — which turns a copy defect into a disclosure risk. Nobody reads a production browser console for design-system warnings, so the emit is now development-only and an unset NODE_ENV is treated as production: fail quiet. The gate is an exported predicate rather than an inline comparison because an inline comparison is untestable here. Vite statically replaces process.env.NODE_ENV inside src/ modules, so under Vitest the check compiles to `"test" === "development"` and no stubEnv can move it. The review asked for a production console-spy check; written that way it would have passed while proving nothing, staying silent for the wrong reason and continuing to pass even if the guard were deleted. shouldEmitErrorStateDiagnostic is asserted directly instead — development true; production, test and unset false — with the console spy kept alongside as the weaker check that catches an emitter which warns unconditionally. Verified: typecheck 0 errors, lint 0, format:check 0, check:design-system-contract 0, Tests 70 passed (70) across error-state.dom and design-sync-visual-exports. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
… queue text (#1890) * docs(issues): retire 22 non-actionable ledger rows and correct the #231 queue text A yield review of all 114 open rows against current main. The queue had become roughly 60 tasks and 50 notes; this removes the notes and fixes two places where the ledger was actively misdirecting. The correction that matters most: the recommended-queue entry for #231, the top clinical P1, told every session to "measure and fix the fast-route budget / generation timeout" — an approach #231's own detail records as tested and rejected, because the decisive 40-second probe completed generation in 25.272s with route_deadline_exceeded=false and still failed quality. The session-start hook prints the queue, not the row, so the refuted text was the text agents read. Closed 22 rows: - #304 was already done on main (commit d182844 refreshed the ranking snapshot; generatedAt is 0 days old, not 2026-07-20), yet sat in the queue advertising a freshness fuse that is not armed. - #241#244#272#294#300#257 were standing cautions whose own text says "no action". Each one's knowledge now lives in the code it protects, so closing the row loses nothing. - #196-#200 are five steps of the disaster-recovery checklist that is canonical in docs/operator-backlog.md, with no trigger until a restore. - #86#188 were index rows over children that are individually findable. - #250#253#254 were superseded; #250 and #253 say so themselves. - #156#301#152#236#260 merged into #168, #292 and #169 respectively — each pair or group was one problem recorded two to four times. Demoted 20 rows with a stated reason (premature ops for a single-user prototype, upstream-blocked, measurement-gated, or design-system adoption competing with an open clinical P1). The Pri cell is unchanged because the writer has no --pri flag — which is now #313. Added three rows for mechanism gaps this sweep exposed: rows outliving their own completion (#312), the missing --pri flag (#313), and the queue being able to contradict the row it cites with no guard (#314). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU * docs(ledger): record the ledger yield review handoff Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU * Keep recovery work visible and pin forced colors --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Reconciles live Supabase drift on
sjrfecxgysukkwxsowpy, hardens security boundaries, and fixes CI blockers — without changing RAG scoring, ranking, or chunking behavior.Changes
20260705133000(search_document_chunks owner scope),20260705220000(jobs table, RPCs, embedding-fields grants, eval RLS)indexing-v3-agentJSONB status RPC parsing fixVerification
npm run format:check— PASSverifyjob — PASS (run https://github.com/BigSimmo/Database/actions/runs/28750309235)ui-smoke— PASS (same run)npm run check:edge:functions— PASS locally20260705220000CI fixes in this branch
6f1d525e309b3567c8allowGlobalSearch?tofetchBestDocumentLookupChunksargs typeClinical Governance Preflight
Clinical KB Database(sjrfecxgysukkwxsowpy)Merge note
Branch may conflict with
mainwhere overlapping access-rollout work landed via other PRs (#291, #297). Resolve at merge without importing unrelated UI commits onto this branch.Operator follow-ups (not in this PR)
HEALTH_DEEP_PROBE_SECRETin production