diff --git a/supabase/migrations/20260625033425_strict_enrichment_gate_repair.sql b/supabase/migrations/20260625033425_strict_enrichment_gate_repair.sql index 39ab71ab9d..d1f21bffd9 100644 --- a/supabase/migrations/20260625033425_strict_enrichment_gate_repair.sql +++ b/supabase/migrations/20260625033425_strict_enrichment_gate_repair.sql @@ -99,6 +99,8 @@ language plpgsql security invoker set search_path = public, extensions, pg_temp as $$ +declare + v_processing_lock_timeout interval := make_interval(mins => 45); begin return query with candidates as ( @@ -158,26 +160,55 @@ begin ) ) else - jsonb_strip_nulls( - (coalesce(d.metadata, '{}'::jsonb) - - 'indexing_v3_agent_locked_by' - - 'indexing_v3_agent_locked_at' - - 'indexing_v3_agent_next_run_at' - - 'indexing_v3_agent_last_error') - || jsonb_build_object( - 'indexing_v3_agent_status', 'deferred', - 'indexing_v3_agent_updated_at', now(), - 'completion_gate_missing', to_jsonb(c.missing), - 'completion_gate', jsonb_build_object( - 'result', 'deferred', - 'missing', to_jsonb(c.missing), - 'counts', c.counts, - 'presence', c.presence, - 'source', 'repair_strict_enrichment_gate_batch' - ), - 'enrichment_status', 'pending' - ) - ) + case + when coalesce(d.metadata->>'indexing_v3_agent_status', '') = 'processing' + and ( + case + when coalesce(d.metadata->>'indexing_v3_agent_locked_at', '') ~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}' + then (d.metadata->>'indexing_v3_agent_locked_at')::timestamptz + else null + end + ) >= now() - v_processing_lock_timeout + then jsonb_strip_nulls( + (coalesce(d.metadata, '{}'::jsonb) + - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_last_error') + || jsonb_build_object( + 'indexing_v3_agent_status', 'processing', + 'indexing_v3_agent_updated_at', now(), + 'completion_gate_missing', to_jsonb(c.missing), + 'completion_gate', jsonb_build_object( + 'result', 'deferred', + 'missing', to_jsonb(c.missing), + 'counts', c.counts, + 'presence', c.presence, + 'source', 'repair_strict_enrichment_gate_batch' + ), + 'enrichment_status', 'processing' + ) + ) + else + jsonb_strip_nulls( + (coalesce(d.metadata, '{}'::jsonb) + - 'indexing_v3_agent_locked_by' + - 'indexing_v3_agent_locked_at' + - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_last_error') + || jsonb_build_object( + 'indexing_v3_agent_status', 'deferred', + 'indexing_v3_agent_updated_at', now(), + 'completion_gate_missing', to_jsonb(c.missing), + 'completion_gate', jsonb_build_object( + 'result', 'deferred', + 'missing', to_jsonb(c.missing), + 'counts', c.counts, + 'presence', c.presence, + 'source', 'repair_strict_enrichment_gate_batch' + ), + 'enrichment_status', 'pending' + ) + ) + end end, updated_at = now() from candidates c diff --git a/supabase/schema.sql b/supabase/schema.sql index ec9b58a638..ef11456ff9 100644 --- a/supabase/schema.sql +++ b/supabase/schema.sql @@ -3002,6 +3002,8 @@ language plpgsql security invoker set search_path = public, extensions, pg_temp as $$ +declare + v_processing_lock_timeout interval := make_interval(mins => 45); begin return query with candidates as ( @@ -3061,26 +3063,55 @@ begin ) ) else - jsonb_strip_nulls( - (coalesce(d.metadata, '{}'::jsonb) - - 'indexing_v3_agent_locked_by' - - 'indexing_v3_agent_locked_at' - - 'indexing_v3_agent_next_run_at' - - 'indexing_v3_agent_last_error') - || jsonb_build_object( - 'indexing_v3_agent_status', 'deferred', - 'indexing_v3_agent_updated_at', now(), - 'completion_gate_missing', to_jsonb(c.missing), - 'completion_gate', jsonb_build_object( - 'result', 'deferred', - 'missing', to_jsonb(c.missing), - 'counts', c.counts, - 'presence', c.presence, - 'source', 'repair_strict_enrichment_gate_batch' - ), - 'enrichment_status', 'pending' - ) - ) + case + when coalesce(d.metadata->>'indexing_v3_agent_status', '') = 'processing' + and ( + case + when coalesce(d.metadata->>'indexing_v3_agent_locked_at', '') ~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}' + then (d.metadata->>'indexing_v3_agent_locked_at')::timestamptz + else null + end + ) >= now() - v_processing_lock_timeout + then jsonb_strip_nulls( + (coalesce(d.metadata, '{}'::jsonb) + - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_last_error') + || jsonb_build_object( + 'indexing_v3_agent_status', 'processing', + 'indexing_v3_agent_updated_at', now(), + 'completion_gate_missing', to_jsonb(c.missing), + 'completion_gate', jsonb_build_object( + 'result', 'deferred', + 'missing', to_jsonb(c.missing), + 'counts', c.counts, + 'presence', c.presence, + 'source', 'repair_strict_enrichment_gate_batch' + ), + 'enrichment_status', 'processing' + ) + ) + else + jsonb_strip_nulls( + (coalesce(d.metadata, '{}'::jsonb) + - 'indexing_v3_agent_locked_by' + - 'indexing_v3_agent_locked_at' + - 'indexing_v3_agent_next_run_at' + - 'indexing_v3_agent_last_error') + || jsonb_build_object( + 'indexing_v3_agent_status', 'deferred', + 'indexing_v3_agent_updated_at', now(), + 'completion_gate_missing', to_jsonb(c.missing), + 'completion_gate', jsonb_build_object( + 'result', 'deferred', + 'missing', to_jsonb(c.missing), + 'counts', c.counts, + 'presence', c.presence, + 'source', 'repair_strict_enrichment_gate_batch' + ), + 'enrichment_status', 'pending' + ) + ) + end end, updated_at = now() from candidates c diff --git a/tests/supabase-schema.test.ts b/tests/supabase-schema.test.ts index eb2f87f9c5..e1f556deea 100644 --- a/tests/supabase-schema.test.ts +++ b/tests/supabase-schema.test.ts @@ -279,6 +279,9 @@ describe("Supabase schema Data API grants", () => { ); expect(sql).toContain("'indexing_v3_agent_status', 'completed'"); expect(sql).toContain("'indexing_v3_agent_status', 'deferred'"); + expect(sql).toContain("coalesce(d.metadata->>'indexing_v3_agent_status', '') = 'processing'"); + expect(sql).toContain("then (d.metadata->>'indexing_v3_agent_locked_at')::timestamptz"); + expect(sql).toContain("'enrichment_status', 'processing'"); expect(sql).toContain("stage = 'strict_gate_deferred'"); expect(sql).toContain("'strict_gate_repair'"); expect(sql).toContain("extraction_quality = 'good'");