-- BUG-1181: the scoring algorithm identity moves to rectification-v5-matrix-scoring-10 -- (functional benefic / malefic profile v2 changes candidate scores for identical -- input). scoring-9 introduced the dated candidate-window contract and every later -- generation keeps it, so this guard compares the generation (>= 9) instead of one -- literal: a bump must not let a partial new contract pass as a legacy same-day -- result. Function body otherwise byte-identical to 20260920020000_adopted_birth_date.sql; -- no table, column, grant or historical row changes (create or replace keeps the -- existing owner and the revoke from 20260920020000). create or replace function public.validate_dated_rectification_candidate( p_case public.agentic_rectification_cases, p_result public.agentic_rectification_results, p_candidate public.agentic_rectification_candidates, p_inference jsonb ) returns date language plpgsql security definer set search_path = '' as $$ declare v_receipt jsonb := p_result.decision_receipt; v_windows jsonb; v_payload jsonb; v_item jsonb; v_interval jsonb; v_window jsonb; v_first timestamp; v_at timestamp; v_start timestamp; v_end timestamp; v_previous timestamp; v_date date; v_expected text; v_count integer; v_union pg_catalog.tsmultirange; v_credible pg_catalog.tsmultirange; v_expected_credible jsonb; v_credible_ranges pg_catalog.tsrange[] := array[]::pg_catalog.tsrange[]; v_top text; begin if v_receipt ->> 'candidate_window_contract' is distinct from 'dated-v1' then -- A partial new contract must never silently become a legacy same-day result. if coalesce(p_result.algorithm_version, '') ~ '^rectification-v5-matrix-scoring-(9|[1-9][0-9]+)$' or v_receipt ? 'candidate_window_contract' or v_receipt ? 'candidate_intervals' or p_candidate.candidate_payload ? 'candidate_date' or coalesce(p_inference ->> 'candidate_set_id', '') like 'dated-v1:%' then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; return null; end if; v_windows := v_receipt -> 'candidate_intervals'; if jsonb_typeof(v_windows) is distinct from 'array' or jsonb_array_length(v_windows) = 0 or v_windows is distinct from p_case.candidate_range -> 'candidate_intervals' or jsonb_typeof(v_receipt -> 'candidate_timezone_offset') is distinct from 'number' or (v_receipt ->> 'candidate_timezone_offset')::double precision is distinct from (p_case.baseline_birth_snapshot ->> 'timezone_offset')::double precision or coalesce(v_receipt ->> 'candidate_timezone_id', '') is distinct from coalesce(p_case.baseline_birth_snapshot ->> 'timezone_id', '') or jsonb_typeof(p_inference -> 'candidates') is distinct from 'array' or jsonb_typeof(p_inference -> 'credible_intervals') is distinct from 'array' or p_inference ->> 'range_start' is distinct from p_case.candidate_range ->> 'start_time' or p_inference ->> 'range_end' is distinct from p_case.candidate_range ->> 'end_time' then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; for v_window in select value from jsonb_array_elements(v_windows) loop if coalesce(v_window ->> 'start_at', '') !~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$' or coalesce(v_window ->> 'end_at', '') !~ '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$' then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; v_start := (v_window ->> 'start_at')::timestamp; v_end := (v_window ->> 'end_at')::timestamp; if v_end < v_start or v_start <= v_previous then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; v_first := coalesce(v_first, v_start); v_previous := v_end; end loop; select count(*), 'dated-v1:' || (p_case.candidate_range ->> 'start_time') || '-' || (p_case.candidate_range ->> 'end_time') || ':' || string_agg( (candidate_payload ->> 'candidate_date') || 'T' || to_char(candidate_time, 'HH24:MI') || '@' || (candidate_payload ->> 'window_index') || '@' || (candidate_payload ->> 'window_offset_minutes') || '@' || (candidate_payload ->> 'segment_index'), ',' order by (candidate_payload ->> 'window_index')::integer) into v_count, v_expected from public.agentic_rectification_candidates where result_id = p_result.id; if v_count = 0 or jsonb_array_length(p_inference -> 'candidates') <> v_count or p_inference ->> 'candidate_set_id' is distinct from v_expected or (select count(distinct value ->> 'window_index') from jsonb_array_elements(p_inference -> 'candidates')) <> v_count then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; for v_item in select value from jsonb_array_elements(p_inference -> 'candidates') loop select candidate_payload into v_payload from public.agentic_rectification_candidates where result_id = p_result.id and to_char(candidate_time, 'HH24:MI') = v_item ->> 'time'; if not found or coalesce(v_item ->> 'candidate_date', '') !~ '^\d{4}-\d{2}-\d{2}$' or coalesce(v_item ->> 'window_index', '') !~ '^\d+$' or coalesce(v_item ->> 'window_offset_minutes', '') !~ '^\d+$' or coalesce(v_item ->> 'segment_index', '') !~ '^\d+$' or v_item -> 'candidate_date' is distinct from v_payload -> 'candidate_date' or v_item -> 'window_index' is distinct from v_payload -> 'window_index' or v_item -> 'window_offset_minutes' is distinct from v_payload -> 'window_offset_minutes' or v_item -> 'segment_index' is distinct from v_payload -> 'segment_index' or v_item -> 'cluster_intervals' is distinct from v_payload -> 'cluster_intervals' or coalesce(v_item ->> 'status', '') not in ('active', 'equivalent', 'winner', 'eliminated') or jsonb_typeof(v_item -> 'probability') is distinct from 'number' or jsonb_typeof(v_item -> 'posterior_score') is distinct from 'number' or jsonb_typeof(v_item -> 'cluster_intervals') is distinct from 'array' or jsonb_array_length(v_item -> 'cluster_intervals') = 0 then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; v_at := ((v_item ->> 'candidate_date') || 'T' || (v_item ->> 'time'))::timestamp; v_window := v_windows -> (v_item ->> 'segment_index')::integer; if v_window is null or v_at < (v_window ->> 'start_at')::timestamp or v_at > (v_window ->> 'end_at')::timestamp or extract(epoch from (v_at - v_first)) / 60 <> (v_item ->> 'window_offset_minutes')::integer then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; if not exists (select 1 from jsonb_array_elements(v_item -> 'cluster_intervals') x where x -> 'segment_index' = v_item -> 'segment_index' and (v_item ->> 'window_index')::integer between (x ->> 'start_index')::integer and (x ->> 'end_index')::integer and v_at between (x ->> 'start_at')::timestamp and (x ->> 'end_at')::timestamp) then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; for v_interval in select value from jsonb_array_elements(v_item -> 'cluster_intervals') loop v_start := (v_interval ->> 'start_at')::timestamp; v_end := (v_interval ->> 'end_at')::timestamp; v_window := v_windows -> (v_interval ->> 'segment_index')::integer; if v_start is null or v_end is null or v_window is null or v_end < v_start or v_start < (v_window ->> 'start_at')::timestamp or v_end > (v_window ->> 'end_at')::timestamp or (v_interval ->> 'start_offset_minutes')::integer is distinct from (extract(epoch from (v_start - v_first)) / 60)::integer or (v_interval ->> 'end_offset_minutes')::integer is distinct from (extract(epoch from (v_end - v_first)) / 60)::integer or coalesce(v_interval ->> 'start_index', '') !~ '^\d+$' or coalesce(v_interval ->> 'end_index', '') !~ '^\d+$' or (v_interval ->> 'end_index')::integer < (v_interval ->> 'start_index')::integer then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; end loop; end loop; -- Historical credible semantics envelope active clusters within each declared segment, -- but never bridge separate declared segments. select range_agg(tsrange(first_at, last_at + interval '1 minute', '[)')), jsonb_agg(jsonb_build_object('segment_index', segment_index, 'start_index', first_index, 'end_index', last_index, 'start_offset_minutes', (extract(epoch from (first_at - v_first)) / 60)::integer, 'end_offset_minutes', (extract(epoch from (last_at - v_first)) / 60)::integer, 'start_at', to_char(first_at, 'YYYY-MM-DD"T"HH24:MI'), 'end_at', to_char(last_at, 'YYYY-MM-DD"T"HH24:MI')) order by first_index) into v_union, v_expected_credible from ( select (part ->> 'segment_index')::integer as segment_index, min((part ->> 'start_index')::integer) as first_index, max((part ->> 'end_index')::integer) as last_index, min((part ->> 'start_at')::timestamp) as first_at, max((part ->> 'end_at')::timestamp) as last_at from jsonb_array_elements(p_inference -> 'candidates') candidate, lateral jsonb_array_elements(candidate -> 'cluster_intervals') part where candidate ->> 'status' <> 'eliminated' and (select (value ->> 'posterior_score')::numeric from jsonb_array_elements(p_inference -> 'candidates') where value ->> 'status' <> 'eliminated' order by (value ->> 'probability')::numeric desc, (value ->> 'posterior_score')::numeric desc, (value ->> 'window_index')::integer limit 1) - (candidate ->> 'posterior_score')::numeric < 8 group by (part ->> 'segment_index')::integer ) segments; for v_interval in select value from jsonb_array_elements(p_inference -> 'credible_intervals') loop v_start := (v_interval ->> 'start_at')::timestamp; v_end := (v_interval ->> 'end_at')::timestamp; if v_start is null or v_end is null or v_end < v_start then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; v_credible_ranges := array_append(v_credible_ranges, tsrange(v_start, v_end + interval '1 minute', '[)')); end loop; select range_agg(x) into v_credible from unnest(v_credible_ranges) x; select value ->> 'time' into v_top from jsonb_array_elements(p_inference -> 'candidates') where value ->> 'status' <> 'eliminated' order by (value ->> 'probability')::numeric desc, (value ->> 'posterior_score')::numeric desc, (value ->> 'window_index')::integer limit 1; if v_union is null or v_credible is distinct from v_union or p_inference -> 'credible_intervals' is distinct from v_expected_credible or p_inference ->> 'representative_time' is distinct from v_top or not exists (select 1 from jsonb_array_elements(p_inference -> 'candidates') where value ->> 'time' = to_char(p_candidate.candidate_time, 'HH24:MI') and value ->> 'status' <> 'eliminated') then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; v_date := (p_candidate.candidate_payload ->> 'candidate_date')::date; if v_date is null then raise exception 'agentic_rectification_candidate_state_inconsistent'; end if; return v_date; end; $$;