diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 6545f04c8..77f056e66 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -50,4 +50,5 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD | 2026-07-11 | codex/repository-review-remediation | 70ec6409a11a85e1678eb4b320519624673a94a0 | comprehensive repository report remediation | Revalidated all 17 findings from the 2026-07-09 comprehensive review. Remediated the current workflow injection, document scope, numeric faithfulness, ingestion lease/ownership, transactional enrichment replacement, request and upload budgets, browser identity isolation, PHI retention, public DTO, cache cancellation/versioning, evidence labelling, modal focus, misleading controls, telemetry, orphan-module issues, and two server/client loading-boundary failures exposed during browser QA. The runbook filename was already fixed on the reviewed head. | TypeScript, lint, focused Vitest (68/68), full offline Vitest (1,607/1,607; 1 skipped), production build, client-bundle secret scan, Docker schema replay and regenerated drift manifest, isolated full migration reset, local lease-reclaim concurrency proof, cache/enrichment SQL smoke, configured production-readiness (`READY`), Chromium document-scope/modal QA (4/4), manual disabled-control accessibility snapshots, and `git diff --check` passed. Read-only live drift found 26 unexpected differences, including the three unapplied remediation functions; no live mutation was performed. | | 2026-07-11 | codex/responsive-accessibility-audit | 66883b7c86f606e617db4bee2bab6f85fff59bdc | responsive and accessibility audit | P2 fixed: the mobile expandable clinical table no longer wraps semantic table content in a duplicate ARIA button, and its full-screen dialog now traps keyboard focus while preserving Escape dismissal and focus return. Added responsive ARIA and focus regression coverage. No additional high-confidence responsive or accessibility defect was reproduced across audited primary app modes and 320px-1440px widths. | Multi-width DOM/geometry/contrast audit; a11y media (2/2); overlap (12/12); table Vitest (6/6); TypeScript; lint/static checks; full Vitest (1,598 passed, 1 skipped); `npm run verify:ui` (132/132); Prettier; `git diff --check`. Provider checks skipped. | | 2026-07-13 | codex/repository-review-remediation | b72cefd2f5c0da79788cc0f8d0d40837c711ae92 | live-drift reconciliation and release review | Reconciled production migration history and live-ahead governance/retrieval definitions without mutating live; removed the migration-version collision; made captured OUT-signature changes fresh-replay-safe; preserved production ACLs; added a forward lexical-score correction; and reduced read-only live drift from 27 differences to five changes fully explained by the unapplied remediation migrations. No remaining high-confidence source defect was found in the reviewed scope. | Docker schema replay and regenerated manifest; isolated full Supabase migration reset; focused Vitest 74/74; full Vitest 1,712 passed/1 skipped; lint; typecheck; production build and client-bundle secret scan; configured production-readiness READY; targeted Chromium scope/modal/control QA 4/4; read-only live drift; Supabase security advisor clear. Live apply not run because authorization remained read-only. | +| 2026-07-13 | codex/repository-review-remediation | 452275824294564a1e08e6bec169bd4af744d09a | live migration apply and post-apply review | Applied the four reviewed forward migrations to `Clinical KB Database`, aligned repository filenames to the generated production versions, and corrected the schema snapshot so the legacy unfenced commit overload remains inaccessible to `service_role`. Live drift is clean and no active ingestion/enrichment overlap or duplicate open ingestion group was found. | Ran `npm run check:drift`: passed clean. Ran `npm run check:production-readiness`: READY. Ran Docker schema replay: passed. Ran focused concurrency/retrieval Vitest: 166/166 passed. Ran offline RAG: 36 fixtures and 60/60 contract tests passed. Ran M13, retrieval-owner, schema-health, lexical-retrieval, concurrency, and ACL live probes: passed; lexical retrieval returned 12 truthfully scored results. Not completed: full provider retrieval-quality evaluation exceeded the local command window; deterministic live retrieval checks passed. | | 2026-07-13 | codex/fix-48h-review-findings-current | 49735663370735a60870d065ed0de3b9d34e077f | last-48-hours PR remediation | Revalidated the last-48-hours findings on current main after PRs #538 and #540; retained only unique fixes across auth/cache isolation, stale-response protection, upload/routing/UI behavior, RAG coalescing, telemetry, worktree tooling, and SAST enforcement. No remaining high-confidence local defect was found in the changed scope. The approved live drift check reported only the five differences already explained by unapplied migrations from #540. | Focused Vitest 107/107; `npm run verify:pr-local` (1,762 passed, 1 skipped; production build and client-bundle scan; offline RAG 60/60); critical Chromium 8/8; live `check:drift`; `git diff --check`. Full Chromium remains advisory after the earlier runner hang; the required critical subset passed on current main. | diff --git a/docs/forward-codify-retrieval-rpcs-workorder.md b/docs/forward-codify-retrieval-rpcs-workorder.md index b225a38db..6c5373b3b 100644 --- a/docs/forward-codify-retrieval-rpcs-workorder.md +++ b/docs/forward-codify-retrieval-rpcs-workorder.md @@ -1,9 +1,9 @@ # Work-order — forward-codify live-ahead retrieval RPC bodies (drift backlog #0) -**Status: reconciled in source on 2026-07-13; live apply remains pending explicit write approval.** -The production-current definitions were captured read-only, preserved under their actual migration -versions, replayed successfully in scratch PostgreSQL, and removed from the drift allowlist. The -four forward remediation migrations remain unapplied to live. +**Status: complete.** The production-current definitions were captured read-only and preserved under +their actual migration versions. Ran the scratch PostgreSQL replay: passed. Ran the reviewed live +migration apply: applied `20260713062107`, `20260713062125`, `20260713062132`, and `20260713062139` +on 2026-07-13. Ran `npm run check:drift`: passed. Ran `npm run check:production-readiness`: READY. Historical blockers at authoring time (2026-07-12): diff --git a/supabase/drift-manifest.json b/supabase/drift-manifest.json index 5dc1c446c..acf341110 100644 --- a/supabase/drift-manifest.json +++ b/supabase/drift-manifest.json @@ -1,9 +1,9 @@ { - "generated_at": "2026-07-13T07:24:51.999Z", + "generated_at": "2026-07-13T08:06:40.593Z", "generator": "scripts/generate-drift-manifest.ts", "postgres_image": "supabase/postgres:17.6.1.127", - "schema_sha256": "d88316aa2edeb597b439663b323ab169b12c0e75bc57eafcf489b1d340bacf47", - "replay_seconds": 17, + "schema_sha256": "0a30a1ea8c8dbb3fd520115d28a17d99c94d9bc5164efe6c287c3b48969be81b", + "replay_seconds": 16, "snapshot": { "views": [ { @@ -6271,8 +6271,7 @@ }, { "acl": [ - "postgres=X/postgres", - "service_role=X/postgres" + "postgres=X/postgres" ], "def_hash": "594b1f715fbbfa1df1b1f1183a7fef5a", "signature": "public.commit_document_index_generation(uuid,uuid,text,integer,integer,integer,jsonb,jsonb,jsonb)" @@ -6595,7 +6594,7 @@ "postgres=X/postgres", "service_role=X/postgres" ], - "def_hash": "9020d937b00df3a36669e870c4aae17b", + "def_hash": "baa8c0bda37cc561026baecdf1cca1c2", "signature": "public.request_indexing_v3_enrichment(uuid,uuid)" }, { diff --git a/supabase/migrations/20260713110000_restore_text_fallback_lexical_score.sql b/supabase/migrations/20260713062107_restore_text_fallback_lexical_score.sql similarity index 100% rename from supabase/migrations/20260713110000_restore_text_fallback_lexical_score.sql rename to supabase/migrations/20260713062107_restore_text_fallback_lexical_score.sql diff --git a/supabase/migrations/20260713120000_fence_index_generation_commit.sql b/supabase/migrations/20260713062125_fence_index_generation_commit.sql similarity index 100% rename from supabase/migrations/20260713120000_fence_index_generation_commit.sql rename to supabase/migrations/20260713062125_fence_index_generation_commit.sql diff --git a/supabase/migrations/20260713121000_purge_expired_rag_response_cache.sql b/supabase/migrations/20260713062132_purge_expired_rag_response_cache.sql similarity index 100% rename from supabase/migrations/20260713121000_purge_expired_rag_response_cache.sql rename to supabase/migrations/20260713062132_purge_expired_rag_response_cache.sql diff --git a/supabase/migrations/20260713122000_route_enrichment_through_agent.sql b/supabase/migrations/20260713062139_route_enrichment_through_agent.sql similarity index 82% rename from supabase/migrations/20260713122000_route_enrichment_through_agent.sql rename to supabase/migrations/20260713062139_route_enrichment_through_agent.sql index 5625cd72c..1659ad542 100644 --- a/supabase/migrations/20260713122000_route_enrichment_through_agent.sql +++ b/supabase/migrations/20260713062139_route_enrichment_through_agent.sql @@ -9,7 +9,39 @@ set search_path = public, extensions, pg_temp as $$ declare v_job_id uuid; + v_job_status text; begin + -- Validate without taking the document lock. The job row is created/locked + -- first below so request and claim paths share one lock order. + perform 1 + from public.documents + where id = p_document_id and owner_id = p_owner_id and status = 'indexed'; + if not found then + raise exception using errcode = 'P0001', message = 'document_not_available_for_enrichment'; + end if; + + insert into public.indexing_v3_agent_jobs ( + document_id, status, enrichment_status, attempt_count, max_attempts, version, metadata + ) values ( + p_document_id, 'pending', 'pending', 0, 3, 'visual-core-v3', '{}'::jsonb + ) + on conflict (document_id) do nothing + returning id, status into v_job_id, v_job_status; + + if v_job_id is null then + select id, status into v_job_id, v_job_status + from public.indexing_v3_agent_jobs + where document_id = p_document_id + for update; + end if; + + if v_job_id is null then + raise exception using errcode = 'P0001', message = 'enrichment_job_unavailable'; + end if; + if v_job_status = 'processing' then + raise exception using errcode = 'P0001', message = 'enrichment_active'; + end if; + perform 1 from public.documents where id = p_document_id and owner_id = p_owner_id and status = 'indexed' @@ -34,29 +66,13 @@ begin next_run_at = null, last_error = null, updated_at = now() - where document_id = p_document_id - and status <> 'processing' - returning id into v_job_id; - - if v_job_id is null then - if exists ( - select 1 from public.indexing_v3_agent_jobs - where document_id = p_document_id and status = 'processing' - ) then - raise exception using errcode = 'P0001', message = 'enrichment_active'; - end if; - insert into public.indexing_v3_agent_jobs ( - document_id, status, enrichment_status, attempt_count, max_attempts, version, metadata - ) values ( - p_document_id, 'pending', 'pending', 0, 3, 'visual-core-v3', '{}'::jsonb - ) - returning id into v_job_id; - end if; + where id = v_job_id; update public.documents set metadata = (coalesce(metadata, '{}'::jsonb) - 'indexing_v3_agent_locked_by' - 'indexing_v3_agent_locked_at' - - 'indexing_v3_agent_last_error' - 'indexing_v3_agent_next_run_at') + - 'indexing_v3_agent_last_error' - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_attempt_count' - 'indexing_v3_agent_max_attempts') || jsonb_build_object( 'enrichment_status', 'pending', 'indexing_v3_agent_status', 'pending', diff --git a/supabase/schema.sql b/supabase/schema.sql index 644c07f62..5b86248e5 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -5234,8 +5234,7 @@ revoke execute on function public.jsonb_merge_deep(jsonb, jsonb) from public, an grant execute on function public.jsonb_merge_deep(jsonb, jsonb) to service_role; revoke execute on function public.apply_document_metadata_patch(uuid, jsonb) from public, anon, authenticated; grant execute on function public.apply_document_metadata_patch(uuid, jsonb) to service_role; -revoke execute on function public.commit_document_index_generation(uuid, uuid, text, integer, integer, integer, jsonb, jsonb, jsonb) from public, anon, authenticated; -grant execute on function public.commit_document_index_generation(uuid, uuid, text, integer, integer, integer, jsonb, jsonb, jsonb) to service_role; +revoke execute on function public.commit_document_index_generation(uuid, uuid, text, integer, integer, integer, jsonb, jsonb, jsonb) from public, anon, authenticated, service_role; revoke execute on function public.cleanup_abandoned_document_index_generations(uuid, integer, boolean) from public, anon, authenticated; grant execute on function public.cleanup_abandoned_document_index_generations(uuid, integer, boolean) to service_role; revoke execute on function public.is_committed_document_generation(uuid, jsonb) from public, anon, authenticated; @@ -5385,7 +5384,39 @@ set search_path = public, extensions, pg_temp as $$ declare v_job_id uuid; + v_job_status text; begin + -- Validate without taking the document lock. The job row is created/locked + -- first below so request and claim paths share one lock order. + perform 1 + from public.documents + where id = p_document_id and owner_id = p_owner_id and status = 'indexed'; + if not found then + raise exception using errcode = 'P0001', message = 'document_not_available_for_enrichment'; + end if; + + insert into public.indexing_v3_agent_jobs ( + document_id, status, enrichment_status, attempt_count, max_attempts, version, metadata + ) values ( + p_document_id, 'pending', 'pending', 0, 3, 'visual-core-v3', '{}'::jsonb + ) + on conflict (document_id) do nothing + returning id, status into v_job_id, v_job_status; + + if v_job_id is null then + select id, status into v_job_id, v_job_status + from public.indexing_v3_agent_jobs + where document_id = p_document_id + for update; + end if; + + if v_job_id is null then + raise exception using errcode = 'P0001', message = 'enrichment_job_unavailable'; + end if; + if v_job_status = 'processing' then + raise exception using errcode = 'P0001', message = 'enrichment_active'; + end if; + perform 1 from public.documents where id = p_document_id and owner_id = p_owner_id and status = 'indexed' @@ -5410,29 +5441,13 @@ begin next_run_at = null, last_error = null, updated_at = now() - where document_id = p_document_id - and status <> 'processing' - returning id into v_job_id; - - if v_job_id is null then - if exists ( - select 1 from public.indexing_v3_agent_jobs - where document_id = p_document_id and status = 'processing' - ) then - raise exception using errcode = 'P0001', message = 'enrichment_active'; - end if; - insert into public.indexing_v3_agent_jobs ( - document_id, status, enrichment_status, attempt_count, max_attempts, version, metadata - ) values ( - p_document_id, 'pending', 'pending', 0, 3, 'visual-core-v3', '{}'::jsonb - ) - returning id into v_job_id; - end if; + where id = v_job_id; update public.documents set metadata = (coalesce(metadata, '{}'::jsonb) - 'indexing_v3_agent_locked_by' - 'indexing_v3_agent_locked_at' - - 'indexing_v3_agent_last_error' - 'indexing_v3_agent_next_run_at') + - 'indexing_v3_agent_last_error' - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_attempt_count' - 'indexing_v3_agent_max_attempts') || jsonb_build_object( 'enrichment_status', 'pending', 'indexing_v3_agent_status', 'pending', diff --git a/tests/supabase-schema.test.ts b/tests/supabase-schema.test.ts index 9f77b700b..478018379 100644 --- a/tests/supabase-schema.test.ts +++ b/tests/supabase-schema.test.ts @@ -84,6 +84,10 @@ const indexingV3AgentJobsMigration = readFileSync( new URL("../supabase/migrations/20260702190000_indexing_v3_agent_jobs_table.sql", import.meta.url), "utf8", ).replace(/\s+/g, " "); +const routeEnrichmentThroughAgentMigration = readFileSync( + new URL("../supabase/migrations/20260713062139_route_enrichment_through_agent.sql", import.meta.url), + "utf8", +).replace(/\s+/g, " "); const clinicalRegistryRecordsMigration = readFileSync( new URL("../supabase/migrations/20260703020000_clinical_registry_records.sql", import.meta.url), "utf8", @@ -257,6 +261,9 @@ describe("Supabase schema Data API grants", () => { ); } expect(schema).toContain( + "revoke execute on function public.commit_document_index_generation(uuid, uuid, text, integer, integer, integer, jsonb, jsonb, jsonb) from public, anon, authenticated, service_role", + ); + expect(schema).not.toContain( "grant execute on function public.commit_document_index_generation(uuid, uuid, text, integer, integer, integer, jsonb, jsonb, jsonb) to service_role", ); expect(atomicReindexMigration).toContain("atomic reindex patch did not match match_document_chunks_hybrid"); @@ -384,6 +391,25 @@ describe("Supabase schema Data API grants", () => { expect(schema).toContain("grant execute on function public.invoke_indexing_v3_agent(integer) to service_role"); }); + it("keeps enrichment requests conflict-safe with job-first locking and complete reset metadata", () => { + for (const sql of [schema, routeEnrichmentThroughAgentMigration]) { + const start = sql.indexOf("create or replace function public.request_indexing_v3_enrichment"); + const end = sql.indexOf("$$;", start); + const body = sql.slice(start, end); + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + expect(body).toContain("on conflict (document_id) do nothing"); + expect(body).toContain("select id, status into v_job_id, v_job_status"); + expect(body).toContain("from public.indexing_v3_agent_jobs"); + expect(body).toContain("for update"); + expect(body).toContain("v_job_status = 'processing'"); + expect(body.indexOf("from public.indexing_v3_agent_jobs")).toBeLessThan( + body.lastIndexOf("from public.documents"), + ); + expect(body).toContain("'indexing_v3_agent_attempt_count' - 'indexing_v3_agent_max_attempts'"); + } + }); + it("drops the stale duplicate ingestion_job_stages document index", () => { for (const sql of [schema, dropDuplicateStageIndexMigration]) { expect(sql).toContain("drop index if exists public.ingestion_job_stages_doc_idx");