begin; create or replace function public.conversational_rectification_fingerprint(value jsonb) returns text language sql immutable strict set search_path = '' as $$ select pg_catalog.encode( pg_catalog.sha256(pg_catalog.convert_to(value::text, 'UTF8')), 'hex' ); $$; create or replace function public.guard_imported_rectification_history() returns trigger language plpgsql security definer set search_path = '' as $$ begin if exists ( select 1 from public.birth_time_rectification_cases imported where imported.imported_from_case_id = old.id and imported.journey_protocol = 'conversational-evidence-v3' ) then raise exception 'conversational_imported_case_read_only' using errcode = 'P0001'; end if; if tg_op = 'DELETE' then return old; end if; return new; end; $$; drop trigger if exists birth_time_rectification_imported_history_update on public.birth_time_rectification_cases; create trigger birth_time_rectification_imported_history_update before update on public.birth_time_rectification_cases for each row execute function public.guard_imported_rectification_history(); drop trigger if exists birth_time_rectification_imported_history_delete on public.birth_time_rectification_cases; create trigger birth_time_rectification_imported_history_delete before delete on public.birth_time_rectification_cases for each row execute function public.guard_imported_rectification_history(); create or replace function public.conversational_rectification_case_projection( p_user_id uuid, p_case_id uuid ) returns jsonb language sql stable security definer set search_path = '' as $$ select pg_catalog.jsonb_build_object( 'case_id', c.id, 'user_id', c.user_id, 'status', c.status, 'turn_version', c.turn_version, 'revision_of_case_id', c.revision_of_case_id, 'imported_from_case_id', c.imported_from_case_id, 'baseline_active_time', case when c.baseline_active_time is null then null else pg_catalog.to_char(c.baseline_active_time, 'HH24:MI') end, 'pending_consultation_question', c.pending_consultation_question, 'billing_state', billing.state, 'latest_turn', pg_catalog.jsonb_build_object( 'caseId', c.id, 'journeyProtocol', 'conversational-evidence-v3', 'status', c.status, 'turnVersion', turn.turn_version, 'narrative', turn.narrative, 'candidate', turn.candidate, 'technicalReceipt', turn.technical_receipt, 'evidenceRequest', turn.evidence_request, 'evidenceRecap', turn.evidence_recap, 'actions', turn.actions, 'pendingConsultationQuestion', c.pending_consultation_question ) ) from public.birth_time_rectification_cases c join public.birth_time_rectification_turns turn on turn.case_id = c.id and turn.turn_version = c.turn_version left join public.birth_time_rectification_billing billing on billing.case_id = c.id and billing.user_id = c.user_id where c.id = p_case_id and c.user_id = p_user_id and c.journey_protocol = 'conversational-evidence-v3'; $$; create or replace function public.load_conversational_rectification_case( p_user_id uuid, p_case_id uuid default null ) returns jsonb language plpgsql stable security definer set search_path = '' as $$ declare v_case_id uuid; v_projection jsonb; begin if p_user_id is null then return null; end if; if p_case_id is not null then v_case_id := p_case_id; else select c.id into v_case_id from public.birth_time_rectification_cases c where c.user_id = p_user_id and c.journey_protocol = 'conversational-evidence-v3' and c.status in ('starting', 'active', 'paused', 'confirming') order by c.updated_at desc, c.created_at desc limit 1; end if; v_projection := public.conversational_rectification_case_projection( p_user_id, v_case_id ); if v_projection is null then return null; end if; -- This RPC is service-role-only. Mutation receipts continue to use the -- public projection above, while account resume additionally receives the -- private working state needed to continue on another device. return v_projection || pg_catalog.jsonb_build_object( 'declared_birth_input', ( select c.declared_birth_input from public.birth_time_rectification_cases c where c.id = v_case_id and c.user_id = p_user_id ), 'private_candidate', ( select c.candidate_result from public.birth_time_rectification_cases c where c.id = v_case_id and c.user_id = p_user_id ), 'event_evidence', coalesce(( select pg_catalog.jsonb_agg( pg_catalog.jsonb_build_object( 'id', evidence.id, 'rawText', evidence.raw_text, 'domain', evidence.domain, 'eventSummary', evidence.event_summary, 'dateValue', evidence.date_value, 'datePrecision', evidence.date_precision, 'extractionStatus', evidence.extraction_status, 'scoreable', evidence.scoreable, 'correctsEvidenceIds', pg_catalog.to_jsonb(evidence.corrects_evidence_ids) ) order by evidence.created_at, evidence.id ) from public.birth_time_rectification_event_evidence evidence where evidence.case_id = v_case_id ), '[]'::jsonb), 'validation_receipts', coalesce(( select pg_catalog.jsonb_agg( turn.output_validation_receipt order by turn.turn_version ) from public.birth_time_rectification_turns turn where turn.case_id = v_case_id ), '[]'::jsonb) ); end; $$; create or replace function public.replay_conversational_rectification_action( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_action_kind text, p_command_fingerprint text ) returns jsonb language plpgsql stable security definer set search_path = '' as $$ declare v_receipt public.birth_time_rectification_action_receipts%rowtype; begin if p_user_id is null or p_case_id is null or p_action_id is null or p_expected_version is null or p_expected_version < 0 or p_action_kind not in ('save_turn', 'pause', 'abandon', 'confirm') or p_command_fingerprint !~ '^[0-9a-f]{64}$' then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.user_id = p_user_id and r.case_id = p_case_id and r.action_id = p_action_id; if not found then return null; end if; -- Receipts created before command identities were introduced still use the -- mutation RPC's complete request fingerprint for their one-step replay. if not (v_receipt.request ? 'commandFingerprint') then return null; end if; if v_receipt.action_kind is distinct from p_action_kind or v_receipt.expected_turn_version is distinct from p_expected_version or v_receipt.request ->> 'commandFingerprint' is distinct from p_command_fingerprint then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; return v_receipt.response; end; $$; create or replace function public.conversational_rectification_case_fits_load_limits( p_user_id uuid, p_case_id uuid, p_status text, p_turn_version bigint, p_latest_turn jsonb, p_private_candidate jsonb, p_new_evidence jsonb, p_validation_receipt jsonb ) returns boolean language plpgsql stable security definer set search_path = '' as $$ declare v_evidence_count bigint; v_receipt_count bigint; v_existing_evidence jsonb; v_new_evidence jsonb; v_validation_receipts jsonb; v_projection jsonb; begin if public.conversational_rectification_valid_public_turn(p_latest_turn) is not true or public.conversational_rectification_valid_private_candidate( p_private_candidate ) is not true or public.conversational_rectification_valid_life_event_evidence_array( p_new_evidence ) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true then return false; end if; select pg_catalog.count(*) into v_evidence_count from public.birth_time_rectification_event_evidence evidence where evidence.case_id = p_case_id; select pg_catalog.count(*) into v_receipt_count from public.birth_time_rectification_turns turn where turn.case_id = p_case_id; if v_evidence_count + pg_catalog.jsonb_array_length(p_new_evidence) > 2000 or v_receipt_count + 1 > 2000 then return false; end if; select coalesce(pg_catalog.jsonb_agg( pg_catalog.jsonb_build_object( 'id', evidence.id, 'rawText', evidence.raw_text, 'domain', evidence.domain, 'eventSummary', evidence.event_summary, 'dateValue', evidence.date_value, 'datePrecision', evidence.date_precision, 'extractionStatus', evidence.extraction_status, 'scoreable', evidence.scoreable, 'correctsEvidenceIds', pg_catalog.to_jsonb(evidence.corrects_evidence_ids) ) order by evidence.created_at, evidence.id ), '[]'::jsonb) into v_existing_evidence from public.birth_time_rectification_event_evidence evidence where evidence.case_id = p_case_id; select coalesce(pg_catalog.jsonb_agg( pg_catalog.jsonb_build_object( 'id', item.value ->> 'id', 'rawText', item.value ->> 'rawText', 'domain', item.value ->> 'domain', 'eventSummary', item.value ->> 'eventSummary', 'dateValue', case when item.value -> 'dateValue' = 'null'::jsonb then null else item.value ->> 'dateValue' end, 'datePrecision', item.value ->> 'datePrecision', 'extractionStatus', item.value ->> 'extractionStatus', 'scoreable', case when item.value ? 'scoreable' then (item.value ->> 'scoreable')::boolean else false end, 'correctsEvidenceIds', coalesce( item.value -> 'correctsEvidenceIds', '[]'::jsonb ) ) order by item.ordinality ), '[]'::jsonb) into v_new_evidence from pg_catalog.jsonb_array_elements(p_new_evidence) with ordinality item(value, ordinality); select coalesce(pg_catalog.jsonb_agg( turn.output_validation_receipt order by turn.turn_version ), '[]'::jsonb) || pg_catalog.jsonb_build_array(p_validation_receipt) into v_validation_receipts from public.birth_time_rectification_turns turn where turn.case_id = p_case_id; select pg_catalog.jsonb_build_object( 'case_id', c.id, 'user_id', c.user_id, 'status', p_status, 'turn_version', p_turn_version, 'revision_of_case_id', c.revision_of_case_id, 'imported_from_case_id', c.imported_from_case_id, 'baseline_active_time', case when c.baseline_active_time is null then null else pg_catalog.to_char(c.baseline_active_time, 'HH24:MI') end, 'pending_consultation_question', c.pending_consultation_question, 'billing_state', billing.state, 'latest_turn', p_latest_turn, 'declared_birth_input', c.declared_birth_input, 'private_candidate', p_private_candidate, 'event_evidence', v_existing_evidence || v_new_evidence, 'validation_receipts', v_validation_receipts ) into v_projection from public.birth_time_rectification_cases c left join public.birth_time_rectification_billing billing on billing.case_id = c.id and billing.user_id = c.user_id where c.id = p_case_id and c.user_id = p_user_id and c.journey_protocol = 'conversational-evidence-v3'; return v_evidence_count + pg_catalog.jsonb_array_length(p_new_evidence) <= 2000 and v_receipt_count + 1 <= 2000 and v_projection is not null and pg_catalog.octet_length(v_projection::text) <= 4194304; exception when others then return false; end; $$; create or replace function public.create_conversational_rectification_case( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_revision_of_case_id uuid, p_pending_consultation_question text, p_declared_birth_input jsonb, p_first_turn jsonb, p_validation_receipt jsonb, p_private_candidate jsonb ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_profile public.profiles%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_response jsonb; v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'create', 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'revisionOfCaseId', p_revision_of_case_id, 'pendingConsultationQuestion', p_pending_consultation_question, 'declaredBirthInput', p_declared_birth_input, 'firstTurn', p_first_turn, 'validationReceipt', p_validation_receipt, 'privateCandidate', p_private_candidate ) ); begin if p_user_id is null or p_case_id is null or p_action_id is null or p_case_id is distinct from p_action_id or p_expected_version is null or p_expected_version < 0 then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended( p_user_id::text || ':conversational-rectification-case', 0 ) ); perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id for update; if found then if v_case.user_id is distinct from p_user_id or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found and v_receipt.user_id is not distinct from p_user_id and v_receipt.action_kind is not distinct from 'create' and v_receipt.expected_turn_version is not distinct from p_expected_version and v_receipt.request_fingerprint is not distinct from v_fingerprint then return v_receipt.response; end if; raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if p_expected_version is distinct from 0 then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; perform 1 from public.birth_time_rectification_cases active_case where active_case.user_id = p_user_id and active_case.id <> p_case_id and active_case.journey_protocol = 'conversational-evidence-v3' and active_case.status in ('starting', 'active', 'paused', 'confirming') for update; if found then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; select profile.* into v_profile from public.profiles profile where profile.id = p_user_id for update; if not found then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.case_id = p_case_id and b.user_id = p_user_id for update; if not found or v_billing.reserve_action_id is distinct from p_action_id or v_billing.state not in ('reserved', 'charged') then raise exception 'conversational_billing_failed' using errcode = 'P0001'; end if; if p_revision_of_case_id is not null then perform 1 from public.birth_time_rectification_cases prior where prior.id = p_revision_of_case_id and prior.user_id = p_user_id and prior.journey_protocol = 'conversational-evidence-v3' and prior.status in ('completed', 'abandoned') for update; if not found then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; end if; if public.conversational_rectification_valid_declared_birth_input( p_declared_birth_input ) is not true or public.conversational_rectification_valid_public_turn(p_first_turn) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or public.conversational_rectification_valid_private_candidate( p_private_candidate ) is not true or p_first_turn ->> 'caseId' is distinct from p_case_id::text or p_first_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_first_turn ->> 'turnVersion')::bigint is distinct from 0 or p_first_turn ->> 'status' is distinct from 'active' or nullif(p_first_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(p_pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.systemPrompt') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_cases ( id, user_id, journey_protocol, status, reported_date, reported_time, reported_period, source, uncertainty_before_minutes, uncertainty_after_minutes, declared_birth_input, journey_snapshot, turn_version, turn_state, candidate_result, candidate_result_id, candidate_start, candidate_end, event_scoring_version, revision_of_case_id, baseline_active_time, pending_consultation_question, updated_at ) values ( p_case_id, p_user_id, 'conversational-evidence-v3', 'active', (p_declared_birth_input ->> 'birthDate')::date, nullif(p_declared_birth_input ->> 'reportedTime', '')::time, nullif(p_declared_birth_input ->> 'reportedPeriod', ''), p_declared_birth_input ->> 'source', nullif(p_declared_birth_input ->> 'uncertaintyBeforeMinutes', '')::integer, nullif(p_declared_birth_input ->> 'uncertaintyAfterMinutes', '')::integer, p_declared_birth_input, p_first_turn, 0, p_first_turn, p_private_candidate, nullif(p_private_candidate ->> 'resultId', '')::uuid, nullif(p_private_candidate ->> 'rangeStart', '')::time, nullif(p_private_candidate ->> 'rangeEnd', '')::time, nullif(p_private_candidate ->> 'calculationVersion', ''), p_revision_of_case_id, v_profile.active_birth_time, nullif(p_pending_consultation_question, ''), pg_catalog.now() ); insert into public.birth_time_rectification_turns ( case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( p_case_id, 0, p_first_turn ->> 'narrative', p_first_turn -> 'candidate', p_first_turn -> 'technicalReceipt', nullif(p_first_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_first_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_first_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'create', p_expected_version, 0, v_fingerprint, public.conversational_rectification_action_request( 'create', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ), v_response ); return v_response; end; $$; create or replace function public.save_conversational_rectification_turn( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_turn jsonb, p_evidence jsonb, p_validation_receipt jsonb, p_private_candidate jsonb, p_command_fingerprint text default null ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_turn_id uuid := pg_catalog.gen_random_uuid(); v_response jsonb; v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'save_turn', 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'turn', p_turn, 'evidence', p_evidence, 'validationReceipt', p_validation_receipt, 'privateCandidate', p_private_candidate ) ); begin if p_user_id is null or p_case_id is null or p_action_id is null or p_expected_version is null or p_expected_version < 0 or (p_command_fingerprint is not null and p_command_fingerprint !~ '^[0-9a-f]{64}$') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id and c.user_id = p_user_id for update; if not found or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found then if v_receipt.user_id is distinct from p_user_id or v_receipt.action_kind is distinct from 'save_turn' or v_receipt.expected_turn_version is distinct from p_expected_version or (case when v_receipt.request ? 'commandFingerprint' then v_receipt.request ->> 'commandFingerprint' is distinct from p_command_fingerprint else v_receipt.request_fingerprint is distinct from v_fingerprint end) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; return v_receipt.response; end if; if v_case.turn_version is distinct from p_expected_version then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.case_id = p_case_id and b.user_id = p_user_id for update; if not found or v_billing.state not in ('charged', 'migration_waived') then raise exception 'conversational_billing_failed' using errcode = 'P0001'; end if; if v_case.status not in ('active', 'paused', 'confirming') or public.conversational_rectification_valid_public_turn(p_turn) is not true or public.conversational_rectification_valid_life_event_evidence_array( p_evidence ) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or public.conversational_rectification_valid_private_candidate( p_private_candidate ) is not true or p_turn ->> 'caseId' is distinct from p_case_id::text or p_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_turn ->> 'turnVersion')::bigint is distinct from p_expected_version + 1 or p_turn ->> 'status' not in ('active', 'confirming') or nullif(p_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(v_case.pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_turn, '$.**.systemPrompt') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; -- Correction targets are account-case evidence tips. Under the locked case version, -- reject one-to-many replacement attempts, missing/cross-case targets, or -- already-retired targets before writing a turn. if exists ( select 1 from ( select correction.value::uuid as target_id from pg_catalog.jsonb_array_elements(p_evidence) item(value) cross join lateral pg_catalog.jsonb_array_elements_text( coalesce(item.value -> 'correctsEvidenceIds', '[]'::jsonb) ) correction(value) group by correction.value::uuid having pg_catalog.count(*) > 1 ) duplicate_correction_target ) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if exists ( select 1 from pg_catalog.jsonb_array_elements(p_evidence) item(value) cross join lateral pg_catalog.jsonb_array_elements_text( coalesce(item.value -> 'correctsEvidenceIds', '[]'::jsonb) ) correction(value) where not exists ( select 1 from public.birth_time_rectification_event_evidence target where target.case_id = p_case_id and target.id = correction.value::uuid ) or exists ( select 1 from public.birth_time_rectification_event_evidence evidence cross join lateral pg_catalog.unnest( evidence.corrects_evidence_ids ) retired(target_id) where evidence.case_id = p_case_id and retired.target_id = correction.value::uuid ) ) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if public.conversational_rectification_case_fits_load_limits( p_user_id, p_case_id, p_turn ->> 'status', p_expected_version + 1, p_turn, p_private_candidate, p_evidence, p_validation_receipt ) is not true then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_turns ( id, case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( v_turn_id, p_case_id, p_expected_version + 1, p_turn ->> 'narrative', p_turn -> 'candidate', p_turn -> 'technicalReceipt', nullif(p_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); insert into public.birth_time_rectification_event_evidence ( id, case_id, source_turn_id, raw_text, domain, event_summary, date_value, date_precision, extraction_status, corrects_evidence_ids, scoreable ) select (item ->> 'id')::uuid, p_case_id, v_turn_id, item ->> 'rawText', item ->> 'domain', item ->> 'eventSummary', case when item -> 'dateValue' = 'null'::jsonb then null else item ->> 'dateValue' end, item ->> 'datePrecision', item ->> 'extractionStatus', case when item ? 'correctsEvidenceIds' then array( select correction.value::uuid from pg_catalog.jsonb_array_elements_text( item -> 'correctsEvidenceIds' ) correction(value) ) else '{}'::uuid[] end, case when item ? 'scoreable' then (item ->> 'scoreable')::boolean else false end from pg_catalog.jsonb_array_elements(p_evidence) item; update public.birth_time_rectification_cases set status = p_turn ->> 'status', turn_version = p_expected_version + 1, turn_state = p_turn, journey_snapshot = p_turn, candidate_result = p_private_candidate, candidate_result_id = nullif(p_private_candidate ->> 'resultId', '')::uuid, candidate_start = nullif(p_private_candidate ->> 'rangeStart', '')::time, candidate_end = nullif(p_private_candidate ->> 'rangeEnd', '')::time, event_scoring_version = nullif(p_private_candidate ->> 'calculationVersion', ''), updated_at = pg_catalog.now() where id = p_case_id and user_id = p_user_id and turn_version = p_expected_version; if not found then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'save_turn', p_expected_version, p_expected_version + 1, v_fingerprint, public.conversational_rectification_action_request( 'save_turn', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ) || case when p_command_fingerprint is null then '{}'::jsonb else pg_catalog.jsonb_build_object( 'commandFingerprint', p_command_fingerprint ) end, v_response ); return v_response; end; $$; create or replace function public.pause_conversational_rectification_case( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_turn jsonb, p_validation_receipt jsonb, p_command_fingerprint text default null ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_response jsonb; v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'pause', 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'turn', p_turn, 'validationReceipt', p_validation_receipt ) ); begin if p_user_id is null or p_case_id is null or p_action_id is null or p_expected_version is null or p_expected_version < 0 or (p_command_fingerprint is not null and p_command_fingerprint !~ '^[0-9a-f]{64}$') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id and c.user_id = p_user_id for update; if not found or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found then if v_receipt.user_id is distinct from p_user_id or v_receipt.action_kind is distinct from 'pause' or v_receipt.expected_turn_version is distinct from p_expected_version or (case when v_receipt.request ? 'commandFingerprint' then v_receipt.request ->> 'commandFingerprint' is distinct from p_command_fingerprint else v_receipt.request_fingerprint is distinct from v_fingerprint end) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; return v_receipt.response; end if; if v_case.turn_version is distinct from p_expected_version then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.case_id = p_case_id and b.user_id = p_user_id for update; if not found or v_billing.state not in ('charged', 'migration_waived') then raise exception 'conversational_billing_failed' using errcode = 'P0001'; end if; if v_case.status not in ('active', 'confirming') or public.conversational_rectification_valid_public_turn(p_turn) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or p_turn ->> 'caseId' is distinct from p_case_id::text or p_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_turn ->> 'turnVersion')::bigint is distinct from p_expected_version + 1 or p_turn ->> 'status' is distinct from 'paused' or nullif(p_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(v_case.pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_turn, '$.**.systemPrompt') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if public.conversational_rectification_case_fits_load_limits( p_user_id, p_case_id, 'paused', p_expected_version + 1, p_turn, v_case.candidate_result, '[]'::jsonb, p_validation_receipt ) is not true then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_turns ( case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( p_case_id, p_expected_version + 1, p_turn ->> 'narrative', p_turn -> 'candidate', p_turn -> 'technicalReceipt', nullif(p_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); update public.birth_time_rectification_cases set status = 'paused', turn_version = p_expected_version + 1, turn_state = p_turn, journey_snapshot = p_turn, updated_at = pg_catalog.now() where id = p_case_id and user_id = p_user_id and turn_version = p_expected_version; if not found then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'pause', p_expected_version, p_expected_version + 1, v_fingerprint, public.conversational_rectification_action_request( 'pause', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ) || case when p_command_fingerprint is null then '{}'::jsonb else pg_catalog.jsonb_build_object( 'commandFingerprint', p_command_fingerprint ) end, v_response ); return v_response; end; $$; create or replace function public.abandon_conversational_rectification_case( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_turn jsonb, p_validation_receipt jsonb, p_command_fingerprint text default null ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_response jsonb; v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'abandon', 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'turn', p_turn, 'validationReceipt', p_validation_receipt ) ); begin if p_user_id is null or p_case_id is null or p_action_id is null or p_expected_version is null or p_expected_version < 0 or (p_command_fingerprint is not null and p_command_fingerprint !~ '^[0-9a-f]{64}$') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id and c.user_id = p_user_id for update; if not found or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found then if v_receipt.user_id is distinct from p_user_id or v_receipt.action_kind is distinct from 'abandon' or v_receipt.expected_turn_version is distinct from p_expected_version or (case when v_receipt.request ? 'commandFingerprint' then v_receipt.request ->> 'commandFingerprint' is distinct from p_command_fingerprint else v_receipt.request_fingerprint is distinct from v_fingerprint end) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; return v_receipt.response; end if; if v_case.turn_version is distinct from p_expected_version then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.case_id = p_case_id and b.user_id = p_user_id for update; if not found or v_billing.state not in ('charged', 'migration_waived') then raise exception 'conversational_billing_failed' using errcode = 'P0001'; end if; if v_case.status not in ('active', 'paused', 'confirming') or public.conversational_rectification_valid_public_turn(p_turn) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or p_turn ->> 'caseId' is distinct from p_case_id::text or p_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_turn ->> 'turnVersion')::bigint is distinct from p_expected_version + 1 or p_turn ->> 'status' is distinct from 'abandoned' or nullif(p_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(v_case.pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_turn, '$.**.systemPrompt') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if public.conversational_rectification_case_fits_load_limits( p_user_id, p_case_id, 'abandoned', p_expected_version + 1, p_turn, v_case.candidate_result, '[]'::jsonb, p_validation_receipt ) is not true then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_turns ( case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( p_case_id, p_expected_version + 1, p_turn ->> 'narrative', p_turn -> 'candidate', p_turn -> 'technicalReceipt', nullif(p_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); update public.birth_time_rectification_cases set status = 'abandoned', turn_version = p_expected_version + 1, turn_state = p_turn, journey_snapshot = p_turn, updated_at = pg_catalog.now() where id = p_case_id and user_id = p_user_id and turn_version = p_expected_version; if not found then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'abandon', p_expected_version, p_expected_version + 1, v_fingerprint, public.conversational_rectification_action_request( 'abandon', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ) || case when p_command_fingerprint is null then '{}'::jsonb else pg_catalog.jsonb_build_object( 'commandFingerprint', p_command_fingerprint ) end, v_response ); return v_response; end; $$; create or replace function public.confirm_conversational_rectification_candidate( p_user_id uuid, p_case_id uuid, p_expected_version bigint, p_action_id uuid, p_result_id uuid, p_time time without time zone, p_calculation_version text, p_turn jsonb, p_validation_receipt jsonb, p_command_fingerprint text default null ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_profile public.profiles%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_current_turn public.birth_time_rectification_turns%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_response jsonb; v_time text := pg_catalog.to_char(p_time, 'HH24:MI'); v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'confirm', 'userId', p_user_id, 'caseId', p_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'resultId', p_result_id, 'time', p_time, 'calculationVersion', p_calculation_version, 'turn', p_turn, 'validationReceipt', p_validation_receipt ) ); begin if p_command_fingerprint is not null and p_command_fingerprint !~ '^[0-9a-f]{64}$' then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if p_user_id is null or p_case_id is null or p_action_id is null or p_result_id is null or p_time is null or extract(second from p_time) is distinct from 0 or nullif(p_calculation_version, '') is null or p_expected_version is null or p_expected_version < 0 then raise exception 'conversational_candidate_changed' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id and c.user_id = p_user_id for update; if not found or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found then if v_receipt.user_id is distinct from p_user_id or v_receipt.action_kind is distinct from 'confirm' or v_receipt.expected_turn_version is distinct from p_expected_version or (case when v_receipt.request ? 'commandFingerprint' then v_receipt.request ->> 'commandFingerprint' is distinct from p_command_fingerprint else v_receipt.request_fingerprint is distinct from v_fingerprint end) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; return v_receipt.response; end if; if v_case.turn_version is distinct from p_expected_version then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.case_id = p_case_id and b.user_id = p_user_id for update; if not found or v_billing.state not in ('charged', 'migration_waived') then raise exception 'conversational_billing_failed' using errcode = 'P0001'; end if; select turn.* into v_current_turn from public.birth_time_rectification_turns turn where turn.case_id = p_case_id and turn.turn_version = p_expected_version for update; if not found then raise exception 'conversational_candidate_changed' using errcode = 'P0001'; end if; select profile.* into v_profile from public.profiles profile where profile.id = p_user_id for update; if not found or v_profile.active_birth_time is distinct from v_case.baseline_active_time then raise exception 'conversational_candidate_changed' using errcode = 'P0001'; end if; if v_case.status is distinct from 'confirming' or v_case.candidate_result_id is distinct from p_result_id or v_case.candidate_result ->> 'resultId' is distinct from p_result_id::text or v_case.candidate_result ->> 'representativeTime' is distinct from v_time or v_case.candidate_result ->> 'calculationVersion' is distinct from p_calculation_version or v_case.event_scoring_version is distinct from p_calculation_version or v_current_turn.candidate ->> 'status' is distinct from 'ready_for_confirmation' or v_current_turn.candidate ->> 'representativeTime' is distinct from v_time or v_current_turn.technical_receipt ->> 'calculationVersion' is distinct from p_calculation_version or public.conversational_rectification_valid_public_turn(p_turn) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or p_turn ->> 'caseId' is distinct from p_case_id::text or p_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_turn ->> 'turnVersion')::bigint is distinct from p_expected_version + 1 or p_turn ->> 'status' is distinct from 'completed' or p_turn #>> '{candidate,status}' is distinct from 'confirmed' or p_turn #>> '{candidate,representativeTime}' is distinct from v_time or p_turn #>> '{technicalReceipt,calculationVersion}' is distinct from p_calculation_version or nullif(p_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(v_case.pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_turn, '$.**.systemPrompt') then raise exception 'conversational_candidate_changed' using errcode = 'P0001'; end if; if public.conversational_rectification_case_fits_load_limits( p_user_id, p_case_id, 'completed', p_expected_version + 1, p_turn, v_case.candidate_result, '[]'::jsonb, p_validation_receipt ) is not true then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_turns ( case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( p_case_id, p_expected_version + 1, p_turn ->> 'narrative', p_turn -> 'candidate', p_turn -> 'technicalReceipt', nullif(p_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); update public.birth_time_rectification_cases set status = 'completed', turn_version = p_expected_version + 1, turn_state = p_turn, journey_snapshot = p_turn, confirmed_time = p_time, confirmed_at = pg_catalog.now(), updated_at = pg_catalog.now() where id = p_case_id and user_id = p_user_id and turn_version = p_expected_version; if not found then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; update public.profiles set active_birth_time = p_time, birth_time = p_time, birth_time_status = 'confirmed', rectification_case_id = p_case_id, updated_at = pg_catalog.now() where id = p_user_id and active_birth_time is not distinct from v_case.baseline_active_time; if not found then raise exception 'conversational_candidate_changed' using errcode = 'P0001'; end if; v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'confirm', p_expected_version, p_expected_version + 1, v_fingerprint, public.conversational_rectification_action_request( 'confirm', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ) || case when p_command_fingerprint is null then '{}'::jsonb else pg_catalog.jsonb_build_object( 'commandFingerprint', p_command_fingerprint ) end, v_response ); return v_response; end; $$; create or replace function public.import_legacy_conversational_rectification_case( p_user_id uuid, p_case_id uuid, p_legacy_case_id uuid, p_expected_version bigint, p_action_id uuid, p_price integer, p_pending_consultation_question text, p_first_turn jsonb, p_validation_receipt jsonb, p_private_candidate jsonb ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.birth_time_rectification_cases%rowtype; v_legacy public.birth_time_rectification_cases%rowtype; v_profile public.profiles%rowtype; v_billing public.birth_time_rectification_billing%rowtype; v_receipt public.birth_time_rectification_action_receipts%rowtype; v_declared_birth_input jsonb; v_response jsonb; v_fingerprint text := public.conversational_rectification_fingerprint( pg_catalog.jsonb_build_object( 'kind', 'import_legacy', 'userId', p_user_id, 'caseId', p_case_id, 'legacyCaseId', p_legacy_case_id, 'expectedVersion', p_expected_version, 'actionId', p_action_id, 'price', p_price, 'pendingConsultationQuestion', p_pending_consultation_question, 'firstTurn', p_first_turn, 'validationReceipt', p_validation_receipt, 'privateCandidate', p_private_candidate ) ); begin if p_user_id is null or p_case_id is null or p_legacy_case_id is null or p_action_id is null or p_case_id is distinct from p_action_id or p_expected_version is null or p_expected_version < 0 or p_price is null or not (p_price between 1 and 1000000) then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended( p_user_id::text || ':conversational-rectification-case', 0 ) ); perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended(p_user_id::text || ':' || p_action_id::text, 0) ); select c.* into v_case from public.birth_time_rectification_cases c where c.id = p_case_id for update; if found then if v_case.user_id is distinct from p_user_id or v_case.journey_protocol is distinct from 'conversational-evidence-v3' then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; select r.* into v_receipt from public.birth_time_rectification_action_receipts r where r.case_id = p_case_id and r.action_id = p_action_id for update; if found and v_receipt.user_id is not distinct from p_user_id and v_receipt.action_kind is not distinct from 'import_legacy' and v_receipt.expected_turn_version is not distinct from p_expected_version and v_receipt.request_fingerprint is not distinct from v_fingerprint then return v_receipt.response; end if; raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; select legacy.* into v_legacy from public.birth_time_rectification_cases legacy where legacy.id = p_legacy_case_id and legacy.user_id = p_user_id for update; if not found or v_legacy.journey_protocol not in ('legacy-guided-v1', 'dynamic-choice-v2') or v_legacy.status in ('confirmed', 'completed', 'abandoned') then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; if v_legacy.turn_version is distinct from p_expected_version then raise exception 'conversational_stale_turn' using errcode = 'P0001'; end if; perform 1 from public.birth_time_rectification_cases imported where imported.user_id = p_user_id and imported.imported_from_case_id = p_legacy_case_id and imported.journey_protocol = 'conversational-evidence-v3' for update; if found then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; perform 1 from public.birth_time_rectification_cases active_case where active_case.user_id = p_user_id and active_case.id <> p_case_id and active_case.id <> p_legacy_case_id and active_case.journey_protocol = 'conversational-evidence-v3' and active_case.status in ('starting', 'active', 'paused', 'confirming') for update; if found then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; select profile.* into v_profile from public.profiles profile where profile.id = p_user_id for update; if not found then raise exception 'conversational_case_not_found' using errcode = 'P0001'; end if; v_profile.credits := public.recover_conversational_rectification_orphan_reservations( p_user_id, null::uuid ); -- Preserve an explicit nullable clue while omitting inapplicable time-mode -- keys. This yields the same source-discriminated representation accepted -- by new starts and keeps legacy import round-trippable across devices. v_declared_birth_input := pg_catalog.jsonb_build_object( 'birthDate', pg_catalog.to_char(v_legacy.reported_date, 'YYYY-MM-DD'), 'source', v_legacy.source, 'birthTimeClue', v_profile.birth_time_clue, 'birthplace', pg_catalog.jsonb_strip_nulls(pg_catalog.jsonb_build_object( 'countryCode', v_profile.country_code, 'provinceCode', v_profile.province_code, 'cityCode', v_profile.city_code, 'districtCode', v_profile.district_code, 'latitude', v_profile.latitude, 'longitude', v_profile.longitude, 'timezoneOffset', v_profile.timezone_offset )) ); if v_legacy.reported_time is not null then v_declared_birth_input := v_declared_birth_input || pg_catalog.jsonb_build_object( 'reportedTime', pg_catalog.to_char(v_legacy.reported_time, 'HH24:MI') ); end if; if v_legacy.reported_period is not null then v_declared_birth_input := v_declared_birth_input || pg_catalog.jsonb_build_object( 'reportedPeriod', v_legacy.reported_period ); end if; if v_legacy.uncertainty_before_minutes is not null or v_legacy.uncertainty_after_minutes is not null then v_declared_birth_input := v_declared_birth_input || pg_catalog.jsonb_build_object( 'uncertaintyBeforeMinutes', v_legacy.uncertainty_before_minutes, 'uncertaintyAfterMinutes', v_legacy.uncertainty_after_minutes ); end if; select b.* into v_billing from public.birth_time_rectification_billing b where b.user_id = p_user_id and b.state = 'reserved' for update; if found then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; if public.conversational_rectification_valid_declared_birth_input( v_declared_birth_input ) is not true or public.conversational_rectification_valid_public_turn(p_first_turn) is not true or public.conversational_rectification_valid_validation_receipt( p_validation_receipt ) is not true or public.conversational_rectification_valid_private_candidate( p_private_candidate ) is not true or p_first_turn ->> 'caseId' is distinct from p_case_id::text or p_first_turn ->> 'journeyProtocol' is distinct from 'conversational-evidence-v3' or (p_first_turn ->> 'turnVersion')::bigint is distinct from 0 or p_first_turn ->> 'status' is distinct from 'active' or nullif(p_first_turn ->> 'pendingConsultationQuestion', '') is distinct from nullif(p_pending_consultation_question, '') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.candidateWeights') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.candidateScores') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.partitionId') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.rawModelOutput') or pg_catalog.jsonb_path_exists(p_first_turn, '$.**.systemPrompt') then raise exception 'conversational_action_conflict' using errcode = 'P0001'; end if; insert into public.birth_time_rectification_cases ( id, user_id, journey_protocol, status, reported_date, reported_time, reported_period, source, uncertainty_before_minutes, uncertainty_after_minutes, declared_birth_input, questionnaire, answers, life_events, candidate_scan, journey_snapshot, turn_version, turn_state, candidate_result, candidate_result_id, candidate_start, candidate_end, event_scoring_version, imported_from_case_id, baseline_active_time, pending_consultation_question, updated_at ) values ( p_case_id, p_user_id, 'conversational-evidence-v3', 'active', v_legacy.reported_date, v_legacy.reported_time, v_legacy.reported_period, v_legacy.source, v_legacy.uncertainty_before_minutes, v_legacy.uncertainty_after_minutes, v_declared_birth_input, v_legacy.questionnaire, v_legacy.answers, v_legacy.life_events, v_legacy.candidate_scan, p_first_turn, 0, p_first_turn, p_private_candidate, nullif(p_private_candidate ->> 'resultId', '')::uuid, nullif(p_private_candidate ->> 'rangeStart', '')::time, nullif(p_private_candidate ->> 'rangeEnd', '')::time, nullif(p_private_candidate ->> 'calculationVersion', ''), p_legacy_case_id, v_profile.active_birth_time, nullif(p_pending_consultation_question, ''), pg_catalog.now() ); insert into public.birth_time_rectification_turns ( case_id, turn_version, narrative, candidate, technical_receipt, evidence_request, evidence_recap, actions, output_validation_receipt ) values ( p_case_id, 0, p_first_turn ->> 'narrative', p_first_turn -> 'candidate', p_first_turn -> 'technicalReceipt', nullif(p_first_turn -> 'evidenceRequest', 'null'::jsonb), coalesce(p_first_turn -> 'evidenceRecap', '[]'::jsonb), coalesce(p_first_turn -> 'actions', '[]'::jsonb), p_validation_receipt ); insert into public.birth_time_rectification_billing ( case_id, user_id, price, state, billing_receipt_id, complete_action_id, balance_after ) values ( p_case_id, p_user_id, p_price, 'migration_waived', pg_catalog.gen_random_uuid(), p_action_id, v_profile.credits ); v_response := public.conversational_rectification_case_projection(p_user_id, p_case_id); insert into public.birth_time_rectification_action_receipts ( case_id, action_id, user_id, action_kind, expected_turn_version, result_turn_version, request_fingerprint, request, response ) values ( p_case_id, p_action_id, p_user_id, 'import_legacy', p_expected_version, 0, v_fingerprint, public.conversational_rectification_action_request( 'import_legacy', p_user_id, p_case_id, p_expected_version, p_action_id, v_fingerprint ), v_response ); return v_response; end; $$; revoke all on function public.conversational_rectification_fingerprint(jsonb) from public, anon, authenticated, service_role; revoke all on function public.guard_imported_rectification_history() from public, anon, authenticated, service_role; revoke all on function public.conversational_rectification_case_projection(uuid, uuid) from public, anon, authenticated, service_role; revoke all on function public.conversational_rectification_case_fits_load_limits( uuid, uuid, text, bigint, jsonb, jsonb, jsonb, jsonb ) from public, anon, authenticated, service_role; revoke all on function public.load_conversational_rectification_case(uuid, uuid) from public, anon, authenticated; revoke all on function public.replay_conversational_rectification_action( uuid, uuid, bigint, uuid, text, text ) from public, anon, authenticated; revoke all on function public.create_conversational_rectification_case( uuid, uuid, bigint, uuid, uuid, text, jsonb, jsonb, jsonb, jsonb ) from public, anon, authenticated; revoke all on function public.save_conversational_rectification_turn( uuid, uuid, bigint, uuid, jsonb, jsonb, jsonb, jsonb, text ) from public, anon, authenticated; revoke all on function public.pause_conversational_rectification_case( uuid, uuid, bigint, uuid, jsonb, jsonb, text ) from public, anon, authenticated; revoke all on function public.abandon_conversational_rectification_case( uuid, uuid, bigint, uuid, jsonb, jsonb, text ) from public, anon, authenticated; revoke all on function public.confirm_conversational_rectification_candidate( uuid, uuid, bigint, uuid, uuid, time without time zone, text, jsonb, jsonb, text ) from public, anon, authenticated; revoke all on function public.import_legacy_conversational_rectification_case( uuid, uuid, uuid, bigint, uuid, integer, text, jsonb, jsonb, jsonb ) from public, anon, authenticated; grant execute on function public.load_conversational_rectification_case(uuid, uuid) to service_role; grant execute on function public.replay_conversational_rectification_action( uuid, uuid, bigint, uuid, text, text ) to service_role; grant execute on function public.create_conversational_rectification_case( uuid, uuid, bigint, uuid, uuid, text, jsonb, jsonb, jsonb, jsonb ) to service_role; grant execute on function public.save_conversational_rectification_turn( uuid, uuid, bigint, uuid, jsonb, jsonb, jsonb, jsonb, text ) to service_role; grant execute on function public.pause_conversational_rectification_case( uuid, uuid, bigint, uuid, jsonb, jsonb, text ) to service_role; grant execute on function public.abandon_conversational_rectification_case( uuid, uuid, bigint, uuid, jsonb, jsonb, text ) to service_role; grant execute on function public.confirm_conversational_rectification_candidate( uuid, uuid, bigint, uuid, uuid, time without time zone, text, jsonb, jsonb, text ) to service_role; grant execute on function public.import_legacy_conversational_rectification_case( uuid, uuid, uuid, bigint, uuid, integer, text, jsonb, jsonb, jsonb ) to service_role; commit;