begin; create or replace function public.conversational_rectification_has_only_keys( p_value jsonb, p_allowed text[] ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'object' and not exists ( select 1 from pg_catalog.jsonb_object_keys(p_value) key where not (key = any (p_allowed)) ); $$; create or replace function public.conversational_rectification_valid_time_text(p_value text) returns boolean language sql immutable strict set search_path = '' as $$ select p_value ~ '^([01][0-9]|2[0-3]):[0-5][0-9]$'; $$; create or replace function public.conversational_rectification_valid_date_text(p_value text) returns boolean language plpgsql immutable strict set search_path = '' as $$ begin return p_value ~ '^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}$' and pg_catalog.to_char(p_value::date, 'YYYY-MM-DD') = p_value; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_uuid_text(p_value text) returns boolean language plpgsql immutable strict set search_path = '' as $$ begin if p_value !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' then return false; end if; perform p_value::uuid; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_text_utf16_length( p_value text ) returns integer language sql immutable strict set search_path = '' as $$ select pg_catalog.char_length(p_value) + ( select pg_catalog.count(*)::integer from pg_catalog.generate_series( 1, pg_catalog.char_length(p_value) ) character_positions(character_index) where pg_catalog.octet_length( pg_catalog.substr(p_value, character_index, 1) ) = 4 ); $$; create or replace function public.conversational_rectification_text_is_nonblank( p_value text ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.btrim( p_value, U&'\0009\000A\000B\000C\000D\0020\00A0\1680\2000\2001\2002\2003\2004\2005\2006\2007\2008\2009\200A\2028\2029\202F\205F\3000\FEFF' ) <> ''; $$; create or replace function public.conversational_rectification_numbers_are_stable( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ declare v_item jsonb; v_number numeric; begin if pg_catalog.jsonb_typeof(p_value) = 'number' then v_number := (p_value #>> '{}')::numeric; if v_number = pg_catalog.trunc(v_number) then return pg_catalog.abs(v_number) <= 9007199254740991; end if; return pg_catalog.abs(v_number) between 0.000001 and 1000000 and v_number = pg_catalog.trunc(v_number, 6); elsif pg_catalog.jsonb_typeof(p_value) = 'array' then for v_item in select value from pg_catalog.jsonb_array_elements(p_value) loop if public.conversational_rectification_numbers_are_stable(v_item) is not true then return false; end if; end loop; elsif pg_catalog.jsonb_typeof(p_value) = 'object' then for v_item in select value from pg_catalog.jsonb_each(p_value) loop if public.conversational_rectification_numbers_are_stable(v_item) is not true then return false; end if; end loop; end if; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_text_array_is_bounded( p_value jsonb, p_max_items integer, p_max_characters integer, p_max_bytes integer ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'array' and pg_catalog.jsonb_array_length(p_value) <= p_max_items and pg_catalog.octet_length(p_value::text) <= p_max_bytes and not exists ( select 1 from pg_catalog.jsonb_array_elements(p_value) item where pg_catalog.jsonb_typeof(item) is distinct from 'string' or public.conversational_rectification_text_utf16_length( item #>> '{}' ) not between 1 and p_max_characters or public.conversational_rectification_text_is_nonblank(item #>> '{}') is not true ); $$; create or replace function public.conversational_rectification_valid_candidate(p_value jsonb) returns boolean language plpgsql immutable strict set search_path = '' as $$ declare v_key text; begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'object' or pg_catalog.octet_length(p_value::text) > 512 or public.conversational_rectification_numbers_are_stable(p_value) is not true or not public.conversational_rectification_has_only_keys( p_value, array['status', 'representativeTime', 'rangeStart', 'rangeEnd']::text[] ) or not (p_value ?& array['status', 'representativeTime', 'rangeStart', 'rangeEnd']::text[]) or pg_catalog.jsonb_typeof(p_value -> 'status') is distinct from 'string' or p_value ->> 'status' not in ( 'declared', 'pending_validation', 'ready_for_confirmation', 'confirmed' ) then return false; end if; foreach v_key in array array['representativeTime', 'rangeStart', 'rangeEnd']::text[] loop if p_value -> v_key <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> v_key) is distinct from 'string' or not public.conversational_rectification_valid_time_text(p_value ->> v_key) ) then return false; end if; end loop; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_technical_receipt( p_value jsonb ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 8192 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array[ 'calculationVersion', 'stableLayers', 'sensitiveLayers', 'candidateDifferenceRefs' ]::text[] ) and p_value ?& array[ 'calculationVersion', 'stableLayers', 'sensitiveLayers', 'candidateDifferenceRefs' ]::text[] and pg_catalog.jsonb_typeof(p_value -> 'calculationVersion') = 'string' and public.conversational_rectification_text_utf16_length( p_value ->> 'calculationVersion' ) between 1 and 80 and public.conversational_rectification_text_is_nonblank( p_value ->> 'calculationVersion' ) and public.conversational_rectification_text_array_is_bounded( p_value -> 'stableLayers', 20, 80, 4096 ) and public.conversational_rectification_text_array_is_bounded( p_value -> 'sensitiveLayers', 20, 80, 4096 ) and public.conversational_rectification_text_array_is_bounded( p_value -> 'candidateDifferenceRefs', 40, 120, 8192 ); $$; create or replace function public.conversational_rectification_valid_evidence_request( p_value jsonb ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 2048 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array['domains', 'datePrecision', 'freeTextAllowed']::text[] ) and p_value ?& array['domains', 'datePrecision', 'freeTextAllowed']::text[] and pg_catalog.jsonb_typeof(p_value -> 'domains') = 'array' and pg_catalog.jsonb_array_length(p_value -> 'domains') between 2 and 4 and not exists ( select 1 from pg_catalog.jsonb_array_elements(p_value -> 'domains') domain where pg_catalog.jsonb_typeof(domain) is distinct from 'string' or domain #>> '{}' not in ( 'career', 'education', 'relocation', 'relationship', 'family', 'other' ) ) and pg_catalog.jsonb_typeof(p_value -> 'datePrecision') = 'string' and p_value ->> 'datePrecision' in ('month_preferred', 'year_accepted') and pg_catalog.jsonb_typeof(p_value -> 'freeTextAllowed') = 'boolean' and p_value -> 'freeTextAllowed' = 'true'::jsonb; $$; create or replace function public.conversational_rectification_valid_evidence_recap( p_value jsonb ) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'array' and pg_catalog.octet_length(p_value::text) <= 24576 and public.conversational_rectification_numbers_are_stable(p_value) and pg_catalog.jsonb_array_length(p_value) <= 20 and not exists ( select 1 from pg_catalog.jsonb_array_elements(p_value) item where pg_catalog.jsonb_typeof(item) <> 'object' or pg_catalog.octet_length(item::text) > 4096 or not public.conversational_rectification_has_only_keys( item, array['id', 'summary', 'dateLabel', 'isCorrection']::text[] ) or not (item ?& array['id', 'summary', 'dateLabel']::text[]) or pg_catalog.jsonb_typeof(item -> 'id') <> 'string' or not public.conversational_rectification_valid_uuid_text(item ->> 'id') or pg_catalog.jsonb_typeof(item -> 'summary') <> 'string' or public.conversational_rectification_text_utf16_length( item ->> 'summary' ) not between 1 and 1000 or public.conversational_rectification_text_is_nonblank( item ->> 'summary' ) is not true or pg_catalog.jsonb_typeof(item -> 'dateLabel') <> 'string' or public.conversational_rectification_text_utf16_length( item ->> 'dateLabel' ) not between 1 and 80 or public.conversational_rectification_text_is_nonblank( item ->> 'dateLabel' ) is not true or ( item ? 'isCorrection' and pg_catalog.jsonb_typeof(item -> 'isCorrection') <> 'boolean' ) ); $$; create or replace function public.conversational_rectification_valid_actions(p_value jsonb) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'array' and pg_catalog.octet_length(p_value::text) <= 512 and public.conversational_rectification_numbers_are_stable(p_value) and pg_catalog.jsonb_array_length(p_value) <= 5 and not exists ( select 1 from pg_catalog.jsonb_array_elements(p_value) action where pg_catalog.jsonb_typeof(action) is distinct from 'string' or action #>> '{}' not in ( 'answer', 'pause', 'abandon', 'confirm', 'continue_original_question' ) ); $$; create or replace function public.conversational_rectification_valid_validation_receipt( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'object' or pg_catalog.octet_length(p_value::text) > 8192 or public.conversational_rectification_numbers_are_stable(p_value) is not true or not public.conversational_rectification_has_only_keys( p_value, array[ 'modelId', 'schemaValidated', 'validatorVersion', 'validatedAt', 'retryCount', 'fallbackUsed', 'issues' ]::text[] ) or not (p_value ?& array['modelId', 'schemaValidated']::text[]) or pg_catalog.jsonb_typeof(p_value -> 'modelId') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'modelId' ) not between 1 and 120 or public.conversational_rectification_text_is_nonblank( p_value ->> 'modelId' ) is not true or pg_catalog.jsonb_typeof(p_value -> 'schemaValidated') is distinct from 'boolean' then return false; end if; if p_value ? 'validatorVersion' and ( pg_catalog.jsonb_typeof(p_value -> 'validatorVersion') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'validatorVersion' ) not between 1 and 80 or public.conversational_rectification_text_is_nonblank( p_value ->> 'validatorVersion' ) is not true ) then return false; end if; if p_value ? 'validatedAt' and ( pg_catalog.jsonb_typeof(p_value -> 'validatedAt') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'validatedAt' ) not between 20 and 40 or public.conversational_rectification_text_is_nonblank( p_value ->> 'validatedAt' ) is not true or p_value ->> 'validatedAt' !~ '^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]+)?(Z|[+-][0-9]{2}:[0-9]{2})$' or (p_value ->> 'validatedAt')::timestamptz is null ) then return false; end if; if p_value ? 'retryCount' and ( pg_catalog.jsonb_typeof(p_value -> 'retryCount') is distinct from 'number' or p_value ->> 'retryCount' !~ '^[0-9]+$' or (p_value ->> 'retryCount')::integer not between 0 and 2 ) then return false; end if; if p_value ? 'fallbackUsed' and pg_catalog.jsonb_typeof(p_value -> 'fallbackUsed') is distinct from 'boolean' then return false; end if; if p_value ? 'issues' and not public.conversational_rectification_text_array_is_bounded( p_value -> 'issues', 20, 240, 8192 ) then return false; end if; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_life_event_evidence( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'object' or pg_catalog.octet_length(p_value::text) > 16384 or public.conversational_rectification_numbers_are_stable(p_value) is not true or not public.conversational_rectification_has_only_keys( p_value, array[ 'id', 'rawText', 'domain', 'eventSummary', 'dateValue', 'datePrecision', 'extractionStatus', 'scoreable', 'correctsEvidenceIds' ]::text[] ) or not (p_value ?& array[ 'id', 'rawText', 'domain', 'eventSummary', 'dateValue', 'datePrecision', 'extractionStatus' ]::text[]) or pg_catalog.jsonb_typeof(p_value -> 'id') is distinct from 'string' or not public.conversational_rectification_valid_uuid_text(p_value ->> 'id') or p_value ->> 'id' !~* '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' or pg_catalog.jsonb_typeof(p_value -> 'rawText') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'rawText' ) not between 1 and 4000 or public.conversational_rectification_text_is_nonblank( p_value ->> 'rawText' ) is not true or pg_catalog.jsonb_typeof(p_value -> 'domain') is distinct from 'string' or p_value ->> 'domain' not in ( 'career', 'education', 'relocation', 'relationship', 'family', 'other' ) or pg_catalog.jsonb_typeof(p_value -> 'eventSummary') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'eventSummary' ) not between 1 and 1000 or public.conversational_rectification_text_is_nonblank( p_value ->> 'eventSummary' ) is not true or not (p_value ? 'dateValue') or ( p_value -> 'dateValue' <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> 'dateValue') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'dateValue' ) not between 1 and 80 or public.conversational_rectification_text_is_nonblank( p_value ->> 'dateValue' ) is not true ) ) or pg_catalog.jsonb_typeof(p_value -> 'datePrecision') is distinct from 'string' or p_value ->> 'datePrecision' not in ('day', 'month', 'year', 'range', 'unknown') or pg_catalog.jsonb_typeof(p_value -> 'extractionStatus') is distinct from 'string' or p_value ->> 'extractionStatus' not in ( 'clear', 'needs_clarification', 'corrected' ) or ( p_value ? 'scoreable' and pg_catalog.jsonb_typeof(p_value -> 'scoreable') is distinct from 'boolean' ) or ( p_value ? 'correctsEvidenceIds' and ( pg_catalog.jsonb_typeof(p_value -> 'correctsEvidenceIds') is distinct from 'array' or pg_catalog.octet_length((p_value -> 'correctsEvidenceIds')::text) > 64 or pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') > 1 or exists ( select 1 from pg_catalog.jsonb_array_elements(p_value -> 'correctsEvidenceIds') target where pg_catalog.jsonb_typeof(target) is distinct from 'string' or public.conversational_rectification_valid_uuid_text(target #>> '{}') is not true ) ) ) or ( p_value ->> 'extractionStatus' = 'corrected' and ( not (p_value ? 'correctsEvidenceIds') or pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') <> 1 ) ) or ( p_value ->> 'extractionStatus' = 'clear' and p_value ? 'correctsEvidenceIds' and pg_catalog.jsonb_array_length(p_value -> 'correctsEvidenceIds') <> 0 ) then return false; end if; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_life_event_evidence_array( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ declare v_item jsonb; begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'array' or pg_catalog.jsonb_array_length(p_value) > 20 then return false; end if; for v_item in select value from pg_catalog.jsonb_array_elements(p_value) loop if public.conversational_rectification_valid_life_event_evidence(v_item) is not true then return false; end if; end loop; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_private_candidate( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ declare v_item jsonb; v_key text; begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'object' or pg_catalog.octet_length(p_value::text) > 65536 or public.conversational_rectification_numbers_are_stable(p_value) is not true or not public.conversational_rectification_has_only_keys( p_value, array[ 'resultId', 'representativeTime', 'rangeStart', 'rangeEnd', 'calculationVersion', 'candidateWeights', 'candidateModelRefs', 'd1Stability', 'boundaryDistanceMinutes', 'supportedSensitiveLayers', 'scoredHistoricalEvidence', 'suggestedDomains', 'futureWindows', 'workingState' ]::text[] ) or not (p_value ? 'calculationVersion') or pg_catalog.jsonb_typeof(p_value -> 'calculationVersion') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'calculationVersion' ) not between 1 and 80 or public.conversational_rectification_text_is_nonblank( p_value ->> 'calculationVersion' ) is not true then return false; end if; if p_value ? 'resultId' and p_value -> 'resultId' <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> 'resultId') is distinct from 'string' or not public.conversational_rectification_valid_uuid_text(p_value ->> 'resultId') ) then return false; end if; foreach v_key in array array['representativeTime', 'rangeStart', 'rangeEnd']::text[] loop if p_value ? v_key and p_value -> v_key <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> v_key) is distinct from 'string' or not public.conversational_rectification_valid_time_text(p_value ->> v_key) ) then return false; end if; end loop; if (p_value ? 'rangeStart') <> (p_value ? 'rangeEnd') or (p_value -> 'rangeStart' = 'null'::jsonb) <> (p_value -> 'rangeEnd' = 'null'::jsonb) then return false; end if; if p_value ? 'candidateWeights' and ( pg_catalog.jsonb_typeof(p_value -> 'candidateWeights') is distinct from 'array' or pg_catalog.jsonb_array_length(p_value -> 'candidateWeights') > 1440 or exists ( select 1 from pg_catalog.jsonb_array_elements(p_value -> 'candidateWeights') weight where pg_catalog.jsonb_typeof(weight) <> 'number' or (weight #>> '{}')::numeric not between 0 and 1 ) ) then return false; end if; if p_value ? 'candidateModelRefs' and not public.conversational_rectification_text_array_is_bounded( p_value -> 'candidateModelRefs', 80, 120, 16384 ) then return false; end if; if p_value ? 'supportedSensitiveLayers' and not public.conversational_rectification_text_array_is_bounded( p_value -> 'supportedSensitiveLayers', 40, 80, 8192 ) then return false; end if; if p_value ? 'd1Stability' and p_value ->> 'd1Stability' not in ( 'stable', 'sensitive', 'unavailable' ) then return false; end if; if p_value ? 'd1Stability' and pg_catalog.jsonb_typeof(p_value -> 'd1Stability') is distinct from 'string' then return false; end if; if p_value ? 'boundaryDistanceMinutes' and p_value -> 'boundaryDistanceMinutes' <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> 'boundaryDistanceMinutes') is distinct from 'number' or p_value ->> 'boundaryDistanceMinutes' !~ '^[0-9]+$' or (p_value ->> 'boundaryDistanceMinutes')::integer not between 0 and 1440 ) then return false; end if; if p_value ? 'suggestedDomains' and ( pg_catalog.jsonb_typeof(p_value -> 'suggestedDomains') is distinct from 'array' or pg_catalog.jsonb_array_length(p_value -> 'suggestedDomains') > 6 or exists ( select 1 from pg_catalog.jsonb_array_elements(p_value -> 'suggestedDomains') domain where pg_catalog.jsonb_typeof(domain) is distinct from 'string' or domain #>> '{}' not in ( 'career', 'education', 'relocation', 'relationship', 'family', 'other' ) ) ) then return false; end if; if p_value ? 'scoredHistoricalEvidence' then if pg_catalog.jsonb_typeof(p_value -> 'scoredHistoricalEvidence') is distinct from 'array' or pg_catalog.jsonb_array_length(p_value -> 'scoredHistoricalEvidence') > 100 then return false; end if; for v_item in select value from pg_catalog.jsonb_array_elements(p_value -> 'scoredHistoricalEvidence') loop if pg_catalog.jsonb_typeof(v_item) is distinct from 'object' or pg_catalog.octet_length(v_item::text) > 8192 or not public.conversational_rectification_has_only_keys( v_item, array['evidenceId', 'domain', 'candidateTime', 'score', 'ruleRefs']::text[] ) or not (v_item ?& array['evidenceId', 'domain', 'candidateTime', 'score', 'ruleRefs']::text[]) or pg_catalog.jsonb_typeof(v_item -> 'evidenceId') is distinct from 'string' or not public.conversational_rectification_valid_uuid_text(v_item ->> 'evidenceId') or pg_catalog.jsonb_typeof(v_item -> 'domain') is distinct from 'string' or v_item ->> 'domain' not in ('career', 'education', 'relocation', 'relationship', 'family', 'other') or (v_item -> 'candidateTime' <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(v_item -> 'candidateTime') is distinct from 'string' or not public.conversational_rectification_valid_time_text(v_item ->> 'candidateTime') )) or pg_catalog.jsonb_typeof(v_item -> 'score') is distinct from 'number' or (v_item ->> 'score')::numeric not between -1000000 and 1000000 or not public.conversational_rectification_text_array_is_bounded( v_item -> 'ruleRefs', 40, 120, 8192 ) then return false; end if; end loop; end if; if p_value ? 'futureWindows' then if pg_catalog.jsonb_typeof(p_value -> 'futureWindows') is distinct from 'array' or pg_catalog.jsonb_array_length(p_value -> 'futureWindows') > 20 then return false; end if; for v_item in select value from pg_catalog.jsonb_array_elements(p_value -> 'futureWindows') loop if pg_catalog.jsonb_typeof(v_item) is distinct from 'object' or pg_catalog.octet_length(v_item::text) > 2048 or not public.conversational_rectification_has_only_keys( v_item, array['label', 'startDate', 'endDate', 'scoreable']::text[] ) or not (v_item ?& array['label', 'startDate', 'endDate', 'scoreable']::text[]) or pg_catalog.jsonb_typeof(v_item -> 'label') is distinct from 'string' or public.conversational_rectification_text_utf16_length( v_item ->> 'label' ) not between 1 and 240 or public.conversational_rectification_text_is_nonblank( v_item ->> 'label' ) is not true or pg_catalog.jsonb_typeof(v_item -> 'startDate') is distinct from 'string' or pg_catalog.jsonb_typeof(v_item -> 'endDate') is distinct from 'string' or not public.conversational_rectification_valid_date_text(v_item ->> 'startDate') or not public.conversational_rectification_valid_date_text(v_item ->> 'endDate') or v_item ->> 'startDate' > v_item ->> 'endDate' or v_item -> 'scoreable' <> 'false'::jsonb then return false; end if; end loop; end if; if p_value ? 'workingState' then v_item := p_value -> 'workingState'; if pg_catalog.jsonb_typeof(v_item) is distinct from 'object' or pg_catalog.octet_length(v_item::text) > 8192 or not public.conversational_rectification_has_only_keys( v_item, array['phase', 'iteration', 'notes']::text[] ) or not (v_item ?& array['phase', 'iteration', 'notes']::text[]) or pg_catalog.jsonb_typeof(v_item -> 'phase') is distinct from 'string' or v_item ->> 'phase' not in ('initial', 'collecting_evidence', 'rescoring', 'ready', 'confirmed') or pg_catalog.jsonb_typeof(v_item -> 'iteration') is distinct from 'number' or v_item ->> 'iteration' !~ '^[0-9]+$' or (v_item ->> 'iteration')::integer not between 0 and 100 or not public.conversational_rectification_text_array_is_bounded( v_item -> 'notes', 20, 240, 8192 ) then return false; end if; end if; return true; exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_declared_birth_input( p_value jsonb ) returns boolean language plpgsql immutable strict set search_path = '' as $$ declare v_place jsonb; v_source text; v_key text; v_before integer; v_after integer; begin if pg_catalog.jsonb_typeof(p_value) is distinct from 'object' or pg_catalog.octet_length(p_value::text) > 12000 or public.conversational_rectification_numbers_are_stable(p_value) is not true or not public.conversational_rectification_has_only_keys( p_value, array[ 'birthDate', 'source', 'birthTimeClue', 'birthplace', 'reportedTime', 'reportedPeriod', 'uncertaintyBeforeMinutes', 'uncertaintyAfterMinutes' ]::text[] ) or not (p_value ?& array['birthDate', 'source', 'birthTimeClue', 'birthplace']::text[]) or pg_catalog.jsonb_typeof(p_value -> 'birthDate') is distinct from 'string' or not public.conversational_rectification_valid_date_text(p_value ->> 'birthDate') or pg_catalog.jsonb_typeof(p_value -> 'source') is distinct from 'string' or public.conversational_rectification_text_is_nonblank( p_value ->> 'source' ) is not true or p_value -> 'birthTimeClue' <> 'null'::jsonb and ( pg_catalog.jsonb_typeof(p_value -> 'birthTimeClue') is distinct from 'string' or public.conversational_rectification_text_utf16_length( p_value ->> 'birthTimeClue' ) not between 1 and 240 or public.conversational_rectification_text_is_nonblank( p_value ->> 'birthTimeClue' ) is not true ) then return false; end if; v_place := p_value -> 'birthplace'; if pg_catalog.jsonb_typeof(v_place) is distinct from 'object' or pg_catalog.octet_length(v_place::text) > 4096 or not public.conversational_rectification_has_only_keys( v_place, array[ 'city', 'countryCode', 'provinceCode', 'cityCode', 'districtCode', 'latitude', 'longitude', 'timezoneOffset' ]::text[] ) or not (v_place ? 'timezoneOffset') or not ((v_place ? 'city') or (v_place ? 'cityCode')) or pg_catalog.jsonb_typeof(v_place -> 'timezoneOffset') is distinct from 'number' or (v_place ->> 'timezoneOffset')::numeric not between -12 and 14 or (v_place ? 'latitude') <> (v_place ? 'longitude') then return false; end if; foreach v_key in array array['city', 'provinceCode', 'cityCode', 'districtCode']::text[] loop if v_place ? v_key and ( pg_catalog.jsonb_typeof(v_place -> v_key) is distinct from 'string' or public.conversational_rectification_text_utf16_length( v_place ->> v_key ) not between 1 and case when v_key = 'city' then 120 else 80 end or public.conversational_rectification_text_is_nonblank( v_place ->> v_key ) is not true ) then return false; end if; end loop; if v_place ? 'countryCode' and ( pg_catalog.jsonb_typeof(v_place -> 'countryCode') is distinct from 'string' or v_place ->> 'countryCode' !~ '^[A-Z0-9-]{1,8}$' ) then return false; end if; if v_place ? 'latitude' and ( pg_catalog.jsonb_typeof(v_place -> 'latitude') is distinct from 'number' or pg_catalog.jsonb_typeof(v_place -> 'longitude') is distinct from 'number' or (v_place ->> 'latitude')::numeric not between -90 and 90 or (v_place ->> 'longitude')::numeric not between -180 and 180 ) then return false; end if; v_source := p_value ->> 'source'; if v_source not in ( 'hospital_record', 'family_exact', 'approximate', 'period_only', 'unknown', 'legacy_import' ) then return false; end if; if p_value ? 'reportedTime' and ( pg_catalog.jsonb_typeof(p_value -> 'reportedTime') is distinct from 'string' or not public.conversational_rectification_valid_time_text(p_value ->> 'reportedTime') ) then return false; end if; if p_value ? 'reportedPeriod' and ( pg_catalog.jsonb_typeof(p_value -> 'reportedPeriod') is distinct from 'string' or p_value ->> 'reportedPeriod' not in ( 'early_morning', 'morning', 'afternoon', 'evening', 'late_night' ) ) then return false; end if; if p_value ? 'uncertaintyBeforeMinutes' then if pg_catalog.jsonb_typeof(p_value -> 'uncertaintyBeforeMinutes') is distinct from 'number' or pg_catalog.jsonb_typeof(p_value -> 'uncertaintyAfterMinutes') is distinct from 'number' or p_value ->> 'uncertaintyBeforeMinutes' !~ '^[0-9]+$' or p_value ->> 'uncertaintyAfterMinutes' !~ '^[0-9]+$' then return false; end if; v_before := (p_value ->> 'uncertaintyBeforeMinutes')::integer; v_after := (p_value ->> 'uncertaintyAfterMinutes')::integer; elsif p_value ? 'uncertaintyAfterMinutes' then return false; end if; if v_source = 'hospital_record' then return p_value ? 'reportedTime' and not (p_value ? 'reportedPeriod') and v_before = 2 and v_after = 2; elsif v_source = 'family_exact' then return p_value ? 'reportedTime' and not (p_value ? 'reportedPeriod') and v_before = v_after and v_before in (5, 10, 15); elsif v_source = 'approximate' then return p_value ? 'reportedTime' and not (p_value ? 'reportedPeriod') and v_before = v_after and v_before in (15, 30, 60); elsif v_source = 'period_only' then return p_value ? 'reportedPeriod' and not (p_value ? 'reportedTime') and not (p_value ? 'uncertaintyBeforeMinutes') and not (p_value ? 'uncertaintyAfterMinutes'); elsif v_source = 'unknown' then return not (p_value ? 'reportedPeriod') and not (p_value ? 'reportedTime') and not (p_value ? 'uncertaintyBeforeMinutes') and not (p_value ? 'uncertaintyAfterMinutes'); end if; return not ((p_value ? 'reportedTime') and (p_value ? 'reportedPeriod')) and ((p_value ? 'reportedTime') or v_before is null) and (v_before is null or (v_before between 0 and 720 and v_after between 0 and 720)); exception when others then return false; end; $$; create or replace function public.conversational_rectification_valid_public_turn(p_value jsonb) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 65536 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array[ 'caseId', 'journeyProtocol', 'status', 'turnVersion', 'narrative', 'candidate', 'technicalReceipt', 'evidenceRequest', 'evidenceRecap', 'actions', 'pendingConsultationQuestion' ]::text[] ) and p_value ?& array[ 'caseId', 'journeyProtocol', 'status', 'turnVersion', 'narrative', 'candidate', 'technicalReceipt', 'evidenceRequest', 'evidenceRecap', 'actions', 'pendingConsultationQuestion' ]::text[] and pg_catalog.jsonb_typeof(p_value -> 'caseId') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'caseId') and pg_catalog.jsonb_typeof(p_value -> 'journeyProtocol') = 'string' and p_value ->> 'journeyProtocol' = 'conversational-evidence-v3' and pg_catalog.jsonb_typeof(p_value -> 'status') = 'string' and p_value ->> 'status' in ('active', 'paused', 'confirming', 'completed', 'abandoned') and pg_catalog.jsonb_typeof(p_value -> 'turnVersion') = 'number' and p_value ->> 'turnVersion' ~ '^[0-9]+$' and pg_catalog.jsonb_typeof(p_value -> 'narrative') = 'string' and public.conversational_rectification_text_utf16_length( p_value ->> 'narrative' ) between 1 and 12000 and public.conversational_rectification_text_is_nonblank( p_value ->> 'narrative' ) and public.conversational_rectification_valid_candidate(p_value -> 'candidate') and public.conversational_rectification_valid_technical_receipt(p_value -> 'technicalReceipt') and ( p_value -> 'evidenceRequest' = 'null'::jsonb or public.conversational_rectification_valid_evidence_request(p_value -> 'evidenceRequest') ) and public.conversational_rectification_valid_evidence_recap(p_value -> 'evidenceRecap') and public.conversational_rectification_valid_actions(p_value -> 'actions') and ( p_value -> 'pendingConsultationQuestion' = 'null'::jsonb or ( pg_catalog.jsonb_typeof(p_value -> 'pendingConsultationQuestion') = 'string' and public.conversational_rectification_text_utf16_length( p_value ->> 'pendingConsultationQuestion' ) between 1 and 500 and public.conversational_rectification_text_is_nonblank( p_value ->> 'pendingConsultationQuestion' ) ) ); $$; create or replace function public.conversational_rectification_valid_action_request(p_value jsonb) returns boolean language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 2048 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array[ 'kind', 'userId', 'caseId', 'expectedVersion', 'actionId', 'requestFingerprint', 'commandFingerprint' ]::text[] ) and p_value ?& array[ 'kind', 'userId', 'caseId', 'expectedVersion', 'actionId', 'requestFingerprint' ]::text[] and pg_catalog.jsonb_typeof(p_value -> 'kind') = 'string' and p_value ->> 'kind' in ( 'create', 'save_turn', 'pause', 'abandon', 'confirm', 'import_legacy', 'reserve_fee', 'complete_fee', 'release_fee', 'recover_fee' ) and pg_catalog.jsonb_typeof(p_value -> 'userId') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'userId') and pg_catalog.jsonb_typeof(p_value -> 'caseId') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'caseId') and pg_catalog.jsonb_typeof(p_value -> 'actionId') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'actionId') and pg_catalog.jsonb_typeof(p_value -> 'expectedVersion') = 'number' and p_value ->> 'expectedVersion' ~ '^[0-9]+$' and pg_catalog.jsonb_typeof(p_value -> 'requestFingerprint') = 'string' and p_value ->> 'requestFingerprint' ~ '^[0-9a-f]{64}$' and ( not (p_value ? 'commandFingerprint') or ( pg_catalog.jsonb_typeof(p_value -> 'commandFingerprint') = 'string' and p_value ->> 'commandFingerprint' ~ '^[0-9a-f]{64}$' ) ); $$; create or replace function public.conversational_rectification_action_request( p_kind text, p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_request_fingerprint text ) returns jsonb language sql immutable strict set search_path = '' as $$ select pg_catalog.jsonb_build_object( 'kind', p_kind, 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'requestFingerprint', p_request_fingerprint ); $$; create or replace function public.conversational_rectification_valid_action_response( p_value jsonb, p_action_kind text ) returns boolean language plpgsql immutable strict set search_path = '' as $$ begin if p_action_kind in ('reserve_fee', 'complete_fee', 'release_fee', 'recover_fee') then return coalesce(pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 2048 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array['success', 'credits', 'billing_state', 'error_code']::text[] ) and p_value ?& array['success', 'credits', 'billing_state', 'error_code']::text[] and pg_catalog.jsonb_typeof(p_value -> 'success') = 'boolean' and ( p_value -> 'credits' = 'null'::jsonb or ( pg_catalog.jsonb_typeof(p_value -> 'credits') = 'number' and p_value ->> 'credits' ~ '^[0-9]+$' ) ) and ( p_value -> 'billing_state' = 'null'::jsonb or ( pg_catalog.jsonb_typeof(p_value -> 'billing_state') = 'string' and p_value ->> 'billing_state' in ( 'reserved', 'charged', 'released', 'migration_waived' ) ) ) and ( p_value -> 'error_code' = 'null'::jsonb or ( pg_catalog.jsonb_typeof(p_value -> 'error_code') = 'string' and public.conversational_rectification_text_utf16_length( p_value ->> 'error_code' ) between 1 and 80 and public.conversational_rectification_text_is_nonblank( p_value ->> 'error_code' ) ) ), false); end if; return coalesce(pg_catalog.jsonb_typeof(p_value) = 'object' and pg_catalog.octet_length(p_value::text) <= 69632 and public.conversational_rectification_numbers_are_stable(p_value) and public.conversational_rectification_has_only_keys( p_value, array[ 'case_id', 'user_id', 'status', 'turn_version', 'revision_of_case_id', 'imported_from_case_id', 'baseline_active_time', 'pending_consultation_question', 'billing_state', 'latest_turn' ]::text[] ) and p_value ?& array[ 'case_id', 'user_id', 'status', 'turn_version', 'revision_of_case_id', 'imported_from_case_id', 'baseline_active_time', 'pending_consultation_question', 'billing_state', 'latest_turn' ]::text[] and pg_catalog.jsonb_typeof(p_value -> 'case_id') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'case_id') and pg_catalog.jsonb_typeof(p_value -> 'user_id') = 'string' and public.conversational_rectification_valid_uuid_text(p_value ->> 'user_id') and pg_catalog.jsonb_typeof(p_value -> 'status') = 'string' and p_value ->> 'status' in ('starting', 'active', 'paused', 'confirming', 'completed', 'abandoned') and pg_catalog.jsonb_typeof(p_value -> 'turn_version') = 'number' and p_value ->> 'turn_version' ~ '^[0-9]+$' and (p_value -> 'revision_of_case_id' = 'null'::jsonb or (pg_catalog.jsonb_typeof(p_value -> 'revision_of_case_id') = 'string' and public.conversational_rectification_valid_uuid_text( p_value ->> 'revision_of_case_id' ))) and (p_value -> 'imported_from_case_id' = 'null'::jsonb or (pg_catalog.jsonb_typeof(p_value -> 'imported_from_case_id') = 'string' and public.conversational_rectification_valid_uuid_text( p_value ->> 'imported_from_case_id' ))) and (p_value -> 'baseline_active_time' = 'null'::jsonb or (pg_catalog.jsonb_typeof(p_value -> 'baseline_active_time') = 'string' and public.conversational_rectification_valid_time_text( p_value ->> 'baseline_active_time' ))) and (p_value -> 'pending_consultation_question' = 'null'::jsonb or (pg_catalog.jsonb_typeof(p_value -> 'pending_consultation_question') = 'string' and public.conversational_rectification_text_utf16_length( p_value ->> 'pending_consultation_question' ) between 1 and 500 and public.conversational_rectification_text_is_nonblank( p_value ->> 'pending_consultation_question' ))) and (p_value -> 'billing_state' = 'null'::jsonb or (pg_catalog.jsonb_typeof(p_value -> 'billing_state') = 'string' and p_value ->> 'billing_state' in ( 'reserved', 'charged', 'released', 'migration_waived' ))) and public.conversational_rectification_valid_public_turn(p_value -> 'latest_turn'), false); exception when others then return false; end; $$; alter table public.birth_time_rectification_cases add column if not exists revision_of_case_id uuid, add column if not exists imported_from_case_id uuid, add column if not exists baseline_active_time time without time zone, add column if not exists pending_consultation_question text, add column if not exists declared_birth_input jsonb not null default '{}'::jsonb; alter table public.birth_time_rectification_cases drop constraint if exists birth_time_rectification_cases_revision_of_case_id_fkey, drop constraint if exists birth_time_rectification_cases_imported_from_case_id_fkey, drop constraint if exists birth_time_rectification_cases_pending_question_check, drop constraint if exists birth_time_rectification_cases_declared_birth_input_check, drop constraint if exists birth_time_rectification_cases_private_candidate_v3_check, drop constraint if exists birth_time_rectification_cases_turn_state_v3_check, drop constraint if exists birth_time_rectification_cases_journey_snapshot_v3_check, drop constraint if exists birth_time_rectification_cases_journey_protocol_check, drop constraint if exists birth_time_rectification_cases_status_check; alter table public.birth_time_rectification_cases add constraint birth_time_rectification_cases_revision_of_case_id_fkey foreign key (revision_of_case_id) references public.birth_time_rectification_cases(id) on delete set null, add constraint birth_time_rectification_cases_imported_from_case_id_fkey foreign key (imported_from_case_id) references public.birth_time_rectification_cases(id) on delete set null, add constraint birth_time_rectification_cases_pending_question_check check ( pending_consultation_question is null or ( public.conversational_rectification_text_utf16_length( pending_consultation_question ) between 1 and 500 and public.conversational_rectification_text_is_nonblank( pending_consultation_question ) ) ), add constraint birth_time_rectification_cases_declared_birth_input_check check ( jsonb_typeof(declared_birth_input) = 'object' and octet_length(declared_birth_input::text) <= 12000 and ( journey_protocol <> 'conversational-evidence-v3' or public.conversational_rectification_valid_declared_birth_input(declared_birth_input) is true ) ), add constraint birth_time_rectification_cases_private_candidate_v3_check check ( journey_protocol <> 'conversational-evidence-v3' or public.conversational_rectification_valid_private_candidate(candidate_result) is true ), add constraint birth_time_rectification_cases_turn_state_v3_check check ( journey_protocol <> 'conversational-evidence-v3' or public.conversational_rectification_valid_public_turn(turn_state) is true ), add constraint birth_time_rectification_cases_journey_snapshot_v3_check check ( journey_protocol <> 'conversational-evidence-v3' or public.conversational_rectification_valid_public_turn(journey_snapshot) is true ), add constraint birth_time_rectification_cases_journey_protocol_check check (journey_protocol in ( 'legacy-guided-v1', 'dynamic-choice-v2', 'conversational-evidence-v3' )), add constraint birth_time_rectification_cases_status_check check (status in ( 'assessing', 'rectifying', 'candidate', 'confirming', 'confirmed', 'starting', 'active', 'paused', 'completed', 'abandoned' )); -- Billing and case creation are phases of one public start action. Billing -- phases use deterministic internal receipt ids so the public action can stay -- stable without colliding with the case-transition receipt key. create or replace function public.conversational_rectification_billing_receipt_action_id( p_action_id uuid, p_action_kind text ) returns uuid language sql immutable strict set search_path = '' as $$ select pg_catalog.md5(p_action_id::text || ':' || p_action_kind)::uuid; $$; create table if not exists public.birth_time_rectification_turns ( id uuid not null default gen_random_uuid(), case_id uuid not null references public.birth_time_rectification_cases(id) on delete cascade, turn_version bigint not null check (turn_version >= 0), narrative text not null check ( public.conversational_rectification_text_utf16_length(narrative) between 1 and 12000 and public.conversational_rectification_text_is_nonblank(narrative) ), candidate jsonb not null check ( public.conversational_rectification_valid_candidate(candidate) is true ), technical_receipt jsonb not null check ( public.conversational_rectification_valid_technical_receipt(technical_receipt) is true ), evidence_request jsonb check ( evidence_request is null or public.conversational_rectification_valid_evidence_request(evidence_request) is true ), evidence_recap jsonb not null default '[]'::jsonb check ( public.conversational_rectification_valid_evidence_recap(evidence_recap) is true ), actions jsonb not null default '[]'::jsonb check ( public.conversational_rectification_valid_actions(actions) is true ), output_validation_receipt jsonb not null check ( public.conversational_rectification_valid_validation_receipt(output_validation_receipt) is true ), created_at timestamptz not null default now(), primary key (case_id, turn_version), unique (case_id, id), check (not jsonb_path_exists( jsonb_build_object( 'candidate', candidate, 'technicalReceipt', technical_receipt, 'evidenceRequest', evidence_request, 'evidenceRecap', evidence_recap, 'outputValidationReceipt', output_validation_receipt ), '$.**.candidateWeights' )), check (not jsonb_path_exists(candidate, '$.**.candidateScores')), check (not jsonb_path_exists(candidate, '$.**.partitionId')), check (not jsonb_path_exists(technical_receipt, '$.**.rawModelOutput')), check (not jsonb_path_exists(output_validation_receipt, '$.**.systemPrompt')) ); create table if not exists public.birth_time_rectification_event_evidence ( id uuid primary key default gen_random_uuid(), case_id uuid not null, source_turn_id uuid not null, raw_text text not null check ( public.conversational_rectification_text_utf16_length(raw_text) between 1 and 4000 and public.conversational_rectification_text_is_nonblank(raw_text) ), domain text not null check ( domain in ('career', 'education', 'relocation', 'relationship', 'family', 'other') ), event_summary text not null check ( public.conversational_rectification_text_utf16_length(event_summary) between 1 and 1000 and public.conversational_rectification_text_is_nonblank(event_summary) ), date_value text check ( date_value is null or ( public.conversational_rectification_text_utf16_length(date_value) between 1 and 80 and public.conversational_rectification_text_is_nonblank(date_value) ) ), date_precision text not null check ( date_precision in ('day', 'month', 'year', 'range', 'unknown') ), extraction_status text not null check ( extraction_status in ('clear', 'needs_clarification', 'corrected') ), corrects_evidence_ids uuid[] not null default '{}'::uuid[] check ( pg_catalog.cardinality(corrects_evidence_ids) <= 1 and ( (extraction_status = 'corrected' and pg_catalog.cardinality(corrects_evidence_ids) = 1) or (extraction_status = 'clear' and pg_catalog.cardinality(corrects_evidence_ids) = 0) or extraction_status = 'needs_clarification' ) ), scoreable boolean not null default false, created_at timestamptz not null default now(), foreign key (case_id, source_turn_id) references public.birth_time_rectification_turns(case_id, id) on delete cascade ); create table if not exists public.birth_time_rectification_action_receipts ( -- Billing reservation happens before the case exists, so receipts cannot -- depend on the case row. The service-only RPCs preserve ownership. case_id uuid not null, action_id uuid not null, user_id uuid not null references auth.users(id) on delete cascade, action_kind text not null check (action_kind in ( 'create', 'save_turn', 'pause', 'abandon', 'confirm', 'import_legacy', 'reserve_fee', 'complete_fee', 'release_fee', 'recover_fee' )), expected_turn_version bigint not null check (expected_turn_version >= 0), result_turn_version bigint not null check (result_turn_version >= 0), request_fingerprint text not null check (request_fingerprint ~ '^[0-9a-f]{64}$'), request jsonb not null check ( public.conversational_rectification_valid_action_request(request) is true ), response jsonb not null check ( public.conversational_rectification_valid_action_response(response, action_kind) is true ), created_at timestamptz not null default now(), primary key (case_id, action_id), check (not jsonb_path_exists(response, '$.**.candidateWeights')), check (not jsonb_path_exists(response, '$.**.candidateScores')), check (not jsonb_path_exists(response, '$.**.partitionId')), check (not jsonb_path_exists(response, '$.**.rawModelOutput')), check (not jsonb_path_exists(response, '$.**.systemPrompt')) ); create table if not exists public.birth_time_rectification_billing ( -- A reservation is the first durable start record and therefore precedes -- case creation. The case transition RPC claims this row atomically. case_id uuid primary key, user_id uuid not null references auth.users(id) on delete cascade, price integer not null check (price between 1 and 1000000), state text not null check ( state in ('reserved', 'charged', 'released', 'migration_waived') ), reservation_id uuid unique, billing_receipt_id uuid unique, reserve_action_id uuid, complete_action_id uuid, release_action_id uuid, balance_after integer check (balance_after is null or balance_after >= 0), reserved_at timestamptz, charged_at timestamptz, released_at timestamptz, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), check ( state not in ('reserved', 'charged') or (reservation_id is not null and reserve_action_id is not null and reserved_at is not null) ), check ( state <> 'charged' or (billing_receipt_id is not null and complete_action_id is not null and charged_at is not null) ), check (state <> 'released' or (release_action_id is not null and released_at is not null)), unique (user_id, reserve_action_id) ); create index if not exists birth_time_rectification_cases_v3_account_idx on public.birth_time_rectification_cases (user_id, updated_at desc) where journey_protocol = 'conversational-evidence-v3'; create index if not exists birth_time_rectification_evidence_case_idx on public.birth_time_rectification_event_evidence (case_id, created_at); create unique index if not exists birth_time_rectification_reserve_action_idx on public.birth_time_rectification_action_receipts (user_id, action_id) where action_kind = 'reserve_fee'; alter table public.birth_time_rectification_turns enable row level security; alter table public.birth_time_rectification_event_evidence enable row level security; alter table public.birth_time_rectification_action_receipts enable row level security; alter table public.birth_time_rectification_billing enable row level security; revoke all on table public.birth_time_rectification_cases from public, anon, authenticated; revoke all on table public.birth_time_rectification_turns from public, anon, authenticated; revoke all on table public.birth_time_rectification_event_evidence from public, anon, authenticated; revoke all on table public.birth_time_rectification_action_receipts from public, anon, authenticated; revoke all on table public.birth_time_rectification_billing from public, anon, authenticated; revoke all on table public.birth_time_rectification_cases from service_role; revoke all on table public.birth_time_rectification_turns from service_role; revoke all on table public.birth_time_rectification_event_evidence from service_role; revoke all on table public.birth_time_rectification_action_receipts from service_role; revoke all on table public.birth_time_rectification_billing from service_role; grant all on table public.birth_time_rectification_cases to service_role; grant all on table public.birth_time_rectification_turns to service_role; grant all on table public.birth_time_rectification_event_evidence to service_role; grant all on table public.birth_time_rectification_action_receipts to service_role; grant all on table public.birth_time_rectification_billing to service_role; revoke all on function public.conversational_rectification_billing_receipt_action_id( uuid, text ) from public, anon, authenticated, service_role; revoke all on function public.conversational_rectification_numbers_are_stable(jsonb) from public, anon, authenticated; revoke all on function public.conversational_rectification_text_utf16_length(text) from public, anon, authenticated; revoke all on function public.conversational_rectification_text_is_nonblank(text) from public, anon, authenticated; revoke all on function public.conversational_rectification_valid_life_event_evidence(jsonb) from public, anon, authenticated; revoke all on function public.conversational_rectification_valid_life_event_evidence_array(jsonb) from public, anon, authenticated; grant execute on function public.conversational_rectification_numbers_are_stable(jsonb) to service_role; grant execute on function public.conversational_rectification_text_utf16_length(text) to service_role; grant execute on function public.conversational_rectification_text_is_nonblank(text) to service_role; grant execute on function public.conversational_rectification_valid_life_event_evidence(jsonb) to service_role; grant execute on function public.conversational_rectification_valid_life_event_evidence_array(jsonb) to service_role; commit;