-- A segment minute is an adoption output, not a new engine candidate or confirmation. begin; create function public.rectification_segment_adoption_minute( p_case public.agentic_rectification_cases, p_result public.agentic_rectification_results, p_state jsonb ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_summary jsonb := p_state -> 'segment_summary'; v_minutes jsonb := p_state -> 'segment_minutes'; v_base jsonb := p_result.decision_receipt -> 'inference_state'; v_targets jsonb; v_chart jsonb; v_segments jsonb; v_shares jsonb; v_weights jsonb; v_candidate jsonb; v_payload jsonb; v_minute jsonb; v_top integer; v_alive integer; v_share double precision; v_tied boolean; v_sign integer; v_tier text; v_unique boolean; v_all_unique boolean := true; v_width integer; v_origin timestamp; v_end timestamp; v_reported integer; v_start_clock integer; v_delta integer; v_start integer; v_finish integer; v_intersection_start integer := 0; v_intersection_end integer; v_fallback_start integer; v_fallback_end integer; v_priority integer := 4; v_current_priority integer; v_midpoint integer; v_adoption jsonb; v_peak double precision; v_index integer; begin if jsonb_typeof(v_summary) is distinct from 'object' or v_summary -> 'scan_complete' is distinct from 'true'::jsonb or p_state -> 'segment_scan_complete' is distinct from 'true'::jsonb or jsonb_typeof(v_minutes) is distinct from 'array' or jsonb_typeof(v_summary -> 'targets') is distinct from 'array' or jsonb_typeof(v_summary -> 'charts') is distinct from 'array' or coalesce(v_summary ->> 'window_minutes', '') !~ '^[0-9]+$' then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; v_width := (v_summary ->> 'window_minutes')::integer; v_targets := v_summary -> 'targets'; if v_width < 1 or v_width > 1440 or jsonb_array_length(v_minutes) <> v_width or jsonb_array_length(v_targets) not between 1 and 3 or jsonb_array_length(v_summary -> 'charts') <> jsonb_array_length(v_targets) or p_state -> 'segment_targets' is distinct from v_targets or v_minutes is distinct from v_base -> 'segment_minutes' or v_targets is distinct from v_base -> 'segment_targets' or (select count(distinct value) from jsonb_array_elements(v_targets)) <> jsonb_array_length(v_targets) or exists (select 1 from jsonb_array_elements_text(v_targets) x where x not in ('D1','D9','D10')) then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; -- A complete proof covers one contiguous, dated window; disjoint/24h identities fail closed. if p_result.decision_receipt ->> 'candidate_window_contract' = 'dated-v1' then if jsonb_array_length(p_result.decision_receipt -> 'candidate_intervals') <> 1 or p_result.decision_receipt -> 'candidate_intervals' is distinct from p_case.candidate_range -> 'candidate_intervals' then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; v_origin := (p_result.decision_receipt -> 'candidate_intervals' -> 0 ->> 'start_at')::timestamp; v_end := (p_result.decision_receipt -> 'candidate_intervals' -> 0 ->> 'end_at')::timestamp; else v_start_clock := extract(hour from (p_state ->> 'range_start')::time)::integer * 60 + extract(minute from (p_state ->> 'range_start')::time)::integer; v_origin := (p_case.baseline_birth_snapshot ->> 'birth_date')::date + make_interval(mins => v_start_clock); if p_case.baseline_birth_snapshot ->> 'reported_birth_time' is not null then v_reported := extract(hour from (p_case.baseline_birth_snapshot ->> 'reported_birth_time')::time)::integer * 60 + extract(minute from (p_case.baseline_birth_snapshot ->> 'reported_birth_time')::time)::integer; v_delta := v_start_clock - v_reported; if v_delta > 720 then v_origin := v_origin - interval '1 day'; elsif v_delta < -720 then v_origin := v_origin + interval '1 day'; end if; elsif p_case.baseline_birth_snapshot ->> 'declared_window_end' < p_case.baseline_birth_snapshot ->> 'declared_window_start' and p_state ->> 'range_start' < p_case.baseline_birth_snapshot ->> 'declared_window_start' then v_origin := v_origin + interval '1 day'; end if; v_end := v_origin + make_interval(mins => (extract(hour from (p_state ->> 'range_end')::time)::integer * 60 + extract(minute from (p_state ->> 'range_end')::time)::integer - v_start_clock + 1440) % 1440); end if; if v_origin is null or v_end is null or extract(epoch from (v_end - v_origin))/60 + 1 <> v_width then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; v_index := 0; for v_minute in select value from jsonb_array_elements(v_minutes) loop if v_minute -> 'offset' is distinct from to_jsonb(v_index) or v_minute ->> 'time' is distinct from to_char(v_origin + make_interval(mins => v_index), 'HH24:MI') or v_minute ->> 'date' is distinct from to_char(v_origin + make_interval(mins => v_index), 'YYYY-MM-DD') or jsonb_typeof(v_minute -> 'signs') is distinct from 'object' or exists (select 1 from jsonb_array_elements_text(v_targets) target where coalesce(v_minute -> 'signs' ->> target, '') !~ '^([0-9]|1[01])$') then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; v_index := v_index + 1; end loop; -- Raw posterior is server-owned ledger output. Prior and real cluster members -- must still identify the persisted engine candidate, never client-supplied mass. for v_candidate in select value from jsonb_array_elements(p_state -> 'candidates') loop select candidate_payload into v_payload from public.agentic_rectification_candidates where result_id = p_result.id and user_id = p_case.user_id and case_id = p_case.id and to_char(candidate_time, 'HH24:MI') = v_candidate ->> 'time'; if not found or jsonb_typeof(v_candidate -> 'raw_posterior_score') is distinct from 'number' or jsonb_typeof(v_candidate -> 'raw_eliminated') is distinct from 'boolean' or v_candidate -> 'raw_prior_score' is distinct from v_payload -> 'raw_score' or jsonb_typeof(v_payload -> 'raw_score') is distinct from 'number' or v_candidate -> 'cluster_times' is distinct from v_payload -> 'cluster_times' or jsonb_typeof(v_candidate -> 'cluster_times') is distinct from 'array' or jsonb_array_length(v_candidate -> 'cluster_times') = 0 or (select count(distinct value) from jsonb_array_elements(v_candidate -> 'cluster_times')) <> jsonb_array_length(v_candidate -> 'cluster_times') or exists (select 1 from jsonb_array_elements_text(v_candidate -> 'cluster_times') member where not exists (select 1 from jsonb_array_elements(v_minutes) m where m ->> 'time' = member)) then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; end loop; select max((value ->> 'raw_posterior_score')::double precision) into v_peak from jsonb_array_elements(p_state -> 'candidates') where value -> 'raw_eliminated' = 'false'::jsonb; select coalesce(jsonb_object_agg(clock, mass), '{}'::jsonb) into v_weights from ( select member as clock, sum(greatest((candidate ->> 'raw_posterior_score')::double precision, 0) / jsonb_array_length(candidate -> 'cluster_times')) as mass from jsonb_array_elements(p_state -> 'candidates') candidate, lateral jsonb_array_elements_text(candidate -> 'cluster_times') member where candidate -> 'raw_eliminated' = 'false'::jsonb and v_peak - (candidate ->> 'raw_posterior_score')::double precision < 8 group by member ) weights; v_intersection_end := v_width - 1; v_index := 0; for v_chart in select value from jsonb_array_elements(v_summary -> 'charts') loop if v_chart -> 'chart' is distinct from v_targets -> v_index then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; with numbered as ( select (m ->> 'offset')::integer as minute_offset, (m -> 'signs' ->> (v_chart ->> 'chart'))::integer as sign, lag((m -> 'signs' ->> (v_chart ->> 'chart'))::integer) over (order by (m ->> 'offset')::integer) as previous from jsonb_array_elements(v_minutes) m ), runs as ( select *, sum(case when sign is distinct from previous then 1 else 0 end) over (order by minute_offset) - 1 as segment from numbered ) select jsonb_agg(jsonb_build_object('index', segment, 'start', start, 'end', finish, 'key', jsonb_build_array(sign)) order by segment) into v_segments from (select segment, min(minute_offset) as start, max(minute_offset) as finish, min(sign) as sign from runs group by segment) x; if v_chart -> 'segments' is distinct from v_segments then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; with mass as ( select (s ->> 'index')::integer as index, coalesce(sum((v_weights ->> (m ->> 'time'))::double precision), 0) as mass from jsonb_array_elements(v_segments) s join jsonb_array_elements(v_minutes) m on (m ->> 'offset')::integer between (s ->> 'start')::integer and (s ->> 'end')::integer group by s ), shares as ( select *, case when sum(mass) over () > 0 then mass / sum(mass) over () else 0 end as share from mass ) select jsonb_agg(round(share::numeric, 6) order by index), count(*) filter (where mass > 1e-9), (array_agg(index order by share desc, index) filter (where mass > 1e-9))[1], coalesce(max(share) filter (where mass > 1e-9), 0), coalesce((array_agg(share order by share desc, index) filter (where mass > 1e-9))[1] - (array_agg(share order by share desc, index) filter (where mass > 1e-9))[2] < 1e-9, false) into v_shares, v_alive, v_top, v_share, v_tied from shares; v_unique := jsonb_array_length(v_segments) = 1; v_all_unique := v_all_unique and v_unique; v_sign := case when v_top is not null then (v_segments -> v_top -> 'key' ->> 0)::integer when v_unique then (v_segments -> 0 -> 'key' ->> 0)::integer else null end; v_share := round(v_share::numeric, 6)::double precision; v_tier := case when v_chart ->> 'chart' = 'D1' and v_unique then 'certain' when v_chart ->> 'chart' <> 'D1' and v_width > 61 then 'blocked' when v_share >= 0.6 then 'credible' when v_width <= 21 and v_share >= 0.5 then 'tentative' else 'indistinct' end; if v_chart -> 'shares' is distinct from v_shares or v_chart -> 'alive_segments' is distinct from to_jsonb(v_alive) or v_chart -> 'top_segment' is distinct from coalesce(to_jsonb(v_top), 'null'::jsonb) or v_chart -> 'top_share' is distinct from to_jsonb(v_share) or v_chart -> 'tied' is distinct from to_jsonb(v_tied) or v_chart -> 'full_window_unique' is distinct from to_jsonb(v_unique) or v_chart -> 'sign' is distinct from coalesce(to_jsonb(v_sign), 'null'::jsonb) or v_chart ->> 'tier' is distinct from v_tier then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; if v_tier <> 'blocked' and v_top is not null then v_start := (v_segments -> v_top ->> 'start')::integer; v_finish := (v_segments -> v_top ->> 'end')::integer; v_intersection_start := greatest(v_intersection_start, v_start); v_intersection_end := least(v_intersection_end, v_finish); v_current_priority := case v_chart ->> 'chart' when 'D1' then 0 when 'D9' then 1 else 2 end; if v_current_priority < v_priority then v_priority := v_current_priority; v_fallback_start := v_start; v_fallback_end := v_finish; end if; end if; v_index := v_index + 1; end loop; if v_summary -> 'no_rectification_needed' is distinct from to_jsonb(v_all_unique) or v_fallback_start is null then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; v_midpoint := case when v_intersection_start <= v_intersection_end then (v_intersection_start + v_intersection_end) / 2 else (v_fallback_start + v_fallback_end) / 2 end; select m into v_adoption from jsonb_array_elements(v_minutes) m where case when jsonb_typeof(p_state -> 'credible_intervals') = 'array' and jsonb_array_length(p_state -> 'credible_intervals') > 0 then exists (select 1 from jsonb_array_elements(p_state -> 'credible_intervals') r where (m ->> 'date') || 'T' || (m ->> 'time') between r ->> 'start_at' and r ->> 'end_at') else case when p_state -> 'credible_range' ->> 0 <= p_state -> 'credible_range' ->> 1 then m ->> 'time' between p_state -> 'credible_range' ->> 0 and p_state -> 'credible_range' ->> 1 else m ->> 'time' >= p_state -> 'credible_range' ->> 0 or m ->> 'time' <= p_state -> 'credible_range' ->> 1 end end order by abs((m ->> 'offset')::integer - v_midpoint), (m ->> 'offset')::integer limit 1; if v_adoption is null or v_summary -> 'adoption_minute' is distinct from v_adoption then raise exception 'agentic_rectification_segment_state_inconsistent'; end if; return v_adoption; end; $$; revoke all on function public.rectification_segment_adoption_minute(public.agentic_rectification_cases, public.agentic_rectification_results, jsonb) from public, anon, authenticated, service_role; create function public.accept_agentic_rectification_segment_for_case_v1( p_user_id uuid, p_case_id uuid, p_result_id uuid, p_candidate_id uuid, p_request_id uuid ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.agentic_rectification_cases%rowtype; v_result public.agentic_rectification_results%rowtype; v_profile public.profiles%rowtype; v_existing public.agentic_rectification_candidate_decisions%rowtype; v_previous_response jsonb; v_state jsonb; v_minute jsonb; v_response jsonb; v_provenance jsonb; v_date date; v_time time; v_offset double precision; v_candidate public.agentic_rectification_candidates%rowtype; v_candidate_count integer; v_candidate_set text; v_top_time text; v_range_start text; v_range_end text; begin if p_user_id is null or p_case_id is null or p_result_id is null or p_candidate_id is null or p_request_id is null then raise exception 'agentic_rectification_candidate_invalid_input'; end if; perform pg_advisory_xact_lock(hashtextextended(p_user_id::text || ':' || p_request_id::text, 0)); select * into v_case from public.agentic_rectification_cases where id = p_case_id and user_id = p_user_id for update; if not found then raise exception 'agentic_rectification_case_not_found'; end if; select * into v_result from public.agentic_rectification_results where id = p_result_id and case_id = p_case_id and user_id = p_user_id for update; if not found or not exists (select 1 from public.agentic_rectification_candidates where id = p_candidate_id and result_id = p_result_id and case_id = p_case_id and user_id = p_user_id) then raise exception 'agentic_rectification_candidate_not_found'; end if; select * into v_existing from public.agentic_rectification_candidate_decisions where user_id = p_user_id and request_id = p_request_id for update; if found then if v_existing.decision_kind <> 'accept' or v_existing.case_id <> p_case_id or v_existing.result_id <> p_result_id or v_existing.candidate_id <> p_candidate_id then raise exception 'agentic_rectification_candidate_request_conflict'; end if; -- Old requests remain old receipts; a retry never changes the minute already adopted. return jsonb_set(v_existing.response, '{idempotent}', 'true'::jsonb, true); end if; select inference_state into v_state from public.agentic_rectification_inference_transitions where case_id = p_case_id and result_id = p_result_id order by revision desc limit 1; v_state := coalesce(v_state, v_result.decision_receipt -> 'inference_state'); if not (v_state ? 'segment_summary') then return public.accept_agentic_rectification_candidate_for_case_v2(p_user_id,p_case_id,p_result_id,p_candidate_id,p_request_id,null); end if; v_minute := public.rectification_segment_adoption_minute(v_case, v_result, v_state); select * into v_profile from public.profiles where id = p_user_id for update; if not found then raise exception 'agentic_rectification_candidate_profile_changed'; end if; if v_result.selected_candidate_id is not null and v_case.adopted_credible_range ->> 'source' = 'server_segment_midpoint' and (v_profile.active_birth_time is distinct from v_result.selected_time or v_profile.active_birth_date is distinct from (v_case.adopted_credible_range ->> 'representative_date')::date or v_profile.birth_time_status is distinct from 'accepted' or v_profile.active_birth_timezone_offset is null or v_profile.active_birth_timezone_offset not between -14 and 14 or v_profile.active_birth_timezone_offset is distinct from (v_profile.active_birth_provenance ->> 'timezone_offset')::double precision or v_profile.timezone_id is distinct from v_profile.active_birth_provenance ->> 'timezone_id' or v_profile.active_birth_provenance ->> 'contract' is distinct from 'segment-v1' or v_profile.active_birth_provenance ->> 'case_id' is distinct from p_case_id::text or v_profile.active_birth_provenance ->> 'candidate_id' is distinct from v_result.selected_candidate_id::text or v_profile.active_birth_provenance ->> 'candidate_date' is distinct from v_profile.active_birth_date::text or v_profile.active_birth_provenance ->> 'candidate_time' is distinct from to_char(v_profile.active_birth_time,'HH24:MI') or v_profile.active_birth_provenance ->> 'result_id' is distinct from p_result_id::text) then raise exception 'agentic_rectification_candidate_selection_blocked'; end if; -- A new request for the same segment selection must not bounce through the -- candidate minute. Revalidate freshness and the frozen dated gate read-only. if v_result.selected_candidate_id = p_candidate_id and v_case.adopted_credible_range ->> 'source' = 'server_segment_midpoint' and v_result.selected_time = (v_minute ->> 'time')::time and v_profile.active_birth_date = (v_minute ->> 'date')::date and v_case.accepted_time = v_result.selected_time and v_result.selection_kind = 'user_accepted' and v_case.status = 'candidate_accepted' then if v_result.invalidated_at is not null or v_result.expires_at <= now() then raise exception 'agentic_rectification_candidate_expired'; end if; if not v_result.display_allowed or not v_result.selection_allowed then raise exception 'agentic_rectification_candidate_selection_blocked'; end if; if exists (select 1 from public.agentic_rectification_results newer where newer.user_id=p_user_id and newer.case_id=p_case_id and newer.invalidated_at is null and newer.created_at>v_result.created_at) then raise exception 'agentic_rectification_candidate_superseded'; end if; if v_profile.birth_date is distinct from (v_case.baseline_birth_snapshot ->> 'birth_date')::date or v_profile.reported_birth_time is distinct from (v_case.baseline_birth_snapshot ->> 'reported_birth_time')::time or v_profile.birth_time_source is distinct from v_case.baseline_birth_snapshot ->> 'birth_time_source' or v_profile.birth_time_period is distinct from v_case.baseline_birth_snapshot ->> 'birth_time_period' or v_profile.declared_window_start is distinct from v_case.baseline_birth_snapshot ->> 'declared_window_start' or v_profile.declared_window_end is distinct from v_case.baseline_birth_snapshot ->> 'declared_window_end' or v_profile.uncertainty_before_minutes is distinct from (v_case.baseline_birth_snapshot ->> 'uncertainty_before_minutes')::integer or v_profile.uncertainty_after_minutes is distinct from (v_case.baseline_birth_snapshot ->> 'uncertainty_after_minutes')::integer or v_profile.latitude is distinct from (v_case.baseline_birth_snapshot ->> 'latitude')::double precision or v_profile.longitude is distinct from (v_case.baseline_birth_snapshot ->> 'longitude')::double precision or v_profile.timezone_id is distinct from v_case.baseline_birth_snapshot ->> 'timezone_id' or v_profile.timezone_offset is distinct from (v_case.baseline_birth_snapshot ->> 'timezone_offset')::double precision then raise exception 'agentic_rectification_candidate_profile_changed'; end if; if jsonb_typeof(v_state -> 'revision') is distinct from 'number' or coalesce(v_state ->> 'revision','') !~ '^[0-9]+$' or length(btrim(coalesce(v_state ->> 'candidate_set_id',''))) = 0 or v_state ->> 'range_start' is distinct from v_case.candidate_range ->> 'start_time' or v_state ->> 'range_end' is distinct from v_case.candidate_range ->> 'end_time' or exists (select 1 from public.agentic_rectification_inference_transitions t where t.case_id=p_case_id and t.result_id=p_result_id and t.revision=(select max(revision) from public.agentic_rectification_inference_transitions where case_id=p_case_id and result_id=p_result_id) and (t.revision is distinct from (v_state ->> 'revision')::integer or t.candidate_set_id is distinct from v_state ->> 'candidate_set_id')) then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; select * into v_candidate from public.agentic_rectification_candidates where id=p_candidate_id; if public.validate_dated_rectification_candidate(v_case,v_result,v_candidate,v_state) is null then -- Frozen legacy identity/active/credible gate, evaluated without any writes. select count(*), (v_case.candidate_range ->> 'start_time') || '-' || (v_case.candidate_range ->> 'end_time') || ':' || string_agg(to_char(candidate_time,'HH24:MI'),',' order by candidate_time) into v_candidate_count,v_candidate_set from public.agentic_rectification_candidates where result_id=p_result_id and case_id=p_case_id and user_id=p_user_id; if v_candidate_count=0 or jsonb_array_length(v_state -> 'candidates')<>v_candidate_count or v_state ->> 'candidate_set_id' is distinct from v_candidate_set or exists (select 1 from jsonb_array_elements(v_state -> 'candidates') a where jsonb_typeof(a) is distinct from 'object' or coalesce(a ->> 'time','') !~ '^([01][0-9]|2[0-3]):[0-5][0-9]$' or coalesce(a ->> 'status','') not in ('active','equivalent','winner','eliminated') or jsonb_typeof(a -> 'probability') is distinct from 'number' or jsonb_typeof(a -> 'posterior_score') is distinct from 'number' or jsonb_typeof(a -> 'cluster_range') is distinct from 'array') then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; if exists (select 1 from jsonb_array_elements(v_state -> 'candidates') a where jsonb_array_length(a -> 'cluster_range') is distinct from 2 or coalesce(a -> 'cluster_range' ->> 0,'') !~ '^([01][0-9]|2[0-3]):[0-5][0-9]$' or coalesce(a -> 'cluster_range' ->> 1,'') !~ '^([01][0-9]|2[0-3]):[0-5][0-9]$' or a -> 'cluster_range' ->> 0 > a ->> 'time' or a -> 'cluster_range' ->> 1 < a ->> 'time' or not exists (select 1 from public.agentic_rectification_candidates c where c.result_id=p_result_id and to_char(c.candidate_time,'HH24:MI')=a ->> 'time')) or (select count(distinct a ->> 'time') from jsonb_array_elements(v_state -> 'candidates') a)<>v_candidate_count then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; select a ->> 'time' into v_top_time from jsonb_array_elements(v_state -> 'candidates') a where a ->> 'status'<>'eliminated' order by (a ->> 'probability')::numeric desc,(a ->> 'posterior_score')::numeric desc,a ->> 'time' limit 1; select min(a -> 'cluster_range' ->> 0),max(a -> 'cluster_range' ->> 1) into v_range_start,v_range_end from jsonb_array_elements(v_state -> 'candidates') a where a ->> 'status'<>'eliminated'; if v_top_time is null or v_state ->> 'representative_time' is distinct from v_top_time or jsonb_typeof(v_state -> 'credible_range') is distinct from 'array' or jsonb_array_length(v_state -> 'credible_range')<>2 or v_state -> 'credible_range' ->> 0 is distinct from v_range_start or v_state -> 'credible_range' ->> 1 is distinct from v_range_end or not exists (select 1 from jsonb_array_elements(v_state -> 'candidates') a where a ->> 'time'=to_char(v_candidate.candidate_time,'HH24:MI') and a ->> 'status'<>'eliminated') then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; end if; select response into v_previous_response from public.agentic_rectification_candidate_decisions where user_id=p_user_id and case_id=p_case_id and result_id=p_result_id and candidate_id=p_candidate_id and decision_kind='accept' and response ->> 'adoption_contract'='segment-v1' and response ->> 'saved_time'=v_minute ->> 'time' and response ->> 'saved_date'=v_minute ->> 'date' order by created_at desc limit 1; if v_previous_response is not null then v_response := v_previous_response || jsonb_build_object('idempotent',true); insert into public.agentic_rectification_candidate_decisions(user_id,case_id,result_id,candidate_id,request_id,decision_kind,response) values(p_user_id,p_case_id,p_result_id,p_candidate_id,p_request_id,'accept',v_response); return v_response; end if; end if; -- Reuse every frozen accept gate in the same transaction for a new selection. -- Any later exception rolls back its writes as well as ours. v_response := public.accept_agentic_rectification_candidate_for_case_v2(p_user_id,p_case_id,p_result_id,p_candidate_id,p_request_id,null); v_date := (v_minute ->> 'date')::date; v_time := (v_minute ->> 'time')::time; v_offset := coalesce((v_result.decision_receipt ->> 'candidate_timezone_offset')::double precision, v_profile.timezone_offset); v_provenance := jsonb_build_object('contract','segment-v1','case_id',p_case_id,'result_id',p_result_id,'candidate_id',p_candidate_id, 'declared_birth_date',v_profile.birth_date,'candidate_date',v_date,'candidate_time',v_minute ->> 'time', 'timezone_offset',v_offset,'timezone_id',v_profile.timezone_id,'source','server_segment_midpoint', 'window_offset_minutes',v_minute -> 'offset','adopted_at',now()); update public.profiles set birth_time=v_time, active_birth_time=v_time, active_birth_date=v_date, active_birth_timezone_offset=v_offset, active_birth_provenance=v_provenance, updated_at=now() where id=p_user_id; update public.agentic_rectification_results set selected_time=v_time, updated_at=now() where id=p_result_id; update public.agentic_rectification_cases set accepted_time=v_time, adopted_credible_range=coalesce(adopted_credible_range,'{}'::jsonb) || jsonb_build_object('representative_time',v_minute ->> 'time', 'representative_date',v_date,'source','server_segment_midpoint','segment_summary',v_state -> 'segment_summary'), updated_at=now() where id=p_case_id; v_response := v_response || jsonb_build_object('saved_time',v_minute ->> 'time','saved_date',v_date, 'day_offset',v_date-v_profile.birth_date,'date_changed',v_date<>v_profile.birth_date, 'saved_timezone_offset',v_offset,'adoption_contract','segment-v1'); update public.agentic_rectification_candidate_decisions set response=v_response where user_id=p_user_id and request_id=p_request_id; return v_response; end; $$; revoke all on function public.accept_agentic_rectification_segment_for_case_v1(uuid,uuid,uuid,uuid,uuid) from public, anon, authenticated; grant execute on function public.accept_agentic_rectification_segment_for_case_v1(uuid,uuid,uuid,uuid,uuid) to service_role; -- Expose only the adopted civil identity, never the profile or its full provenance. create function public.get_agentic_rectification_segment_selection_v1(p_user_id uuid, p_case_id uuid, p_result_id uuid) returns jsonb language sql security definer set search_path = '' as $$ select jsonb_build_object('result_id', r.id, 'candidate_id', r.selected_candidate_id, 'date', p.active_birth_date, 'time', to_char(p.active_birth_time, 'HH24:MI'), 'contract', 'segment-v1') from public.agentic_rectification_results r join public.agentic_rectification_cases c on c.id=r.case_id and c.user_id=r.user_id join public.profiles p on p.id=r.user_id where r.id=p_result_id and r.case_id=p_case_id and r.user_id=p_user_id and r.selection_kind='user_accepted' and p.birth_time_status='accepted' and c.status='candidate_accepted' and r.invalidated_at is null and r.expires_at>now() and not exists (select 1 from public.agentic_rectification_results newer where newer.case_id=c.id and newer.user_id=p_user_id and newer.invalidated_at is null and newer.created_at>r.created_at) and p.active_birth_timezone_offset is not null and p.active_birth_timezone_offset between -14 and 14 and p.active_birth_timezone_offset=(p.active_birth_provenance ->> 'timezone_offset')::double precision and p.timezone_id is not distinct from p.active_birth_provenance ->> 'timezone_id' and c.accepted_time=r.selected_time and p.active_birth_time=r.selected_time and c.adopted_credible_range ->> 'source'='server_segment_midpoint' and c.adopted_credible_range ->> 'representative_date'=p.active_birth_date::text and p.active_birth_provenance ->> 'contract'='segment-v1' and p.active_birth_provenance ->> 'case_id'=c.id::text and p.active_birth_provenance ->> 'result_id'=r.id::text and p.active_birth_provenance ->> 'candidate_id'=r.selected_candidate_id::text and p.active_birth_provenance ->> 'candidate_date'=p.active_birth_date::text and p.active_birth_provenance ->> 'candidate_time'=to_char(p.active_birth_time,'HH24:MI'); $$; revoke all on function public.get_agentic_rectification_segment_selection_v1(uuid,uuid,uuid) from public, anon, authenticated; grant execute on function public.get_agentic_rectification_segment_selection_v1(uuid,uuid,uuid) to service_role; commit;