-- Created 2026-08-12. Filename intentionally follows the existing -- 20260813010000 migration; the identifier is an ordering key. begin; alter table public.agentic_rectification_tool_receipts add column if not exists executed_methods jsonb not null default '[]'::jsonb check (jsonb_typeof(executed_methods) = 'array'); drop function if exists public.insert_agentic_rectification_tool_receipt( uuid, uuid, uuid, text, text, text, text, text, text, text ); create or replace function public.insert_agentic_rectification_tool_receipt( p_user_id uuid, p_case_id uuid, p_turn_id uuid, p_tool_name text, p_public_phase text, p_status text, p_input_fingerprint text, p_result_fingerprint text, p_engine_version text, p_safe_error_code text, p_executed_methods jsonb ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_receipt_id uuid; v_turn_count bigint; begin if p_user_id is null or p_case_id is null or p_turn_id is null or p_executed_methods is null or jsonb_typeof(p_executed_methods) <> 'array' or exists ( select 1 from jsonb_array_elements_text(p_executed_methods) as method(value) where method.value not in ( 'd1-rashi', 'd2-hora', 'd4-chaturthamsha', 'd9-navamsa', 'd10-dashamsa', 'd11-labhamsha', 'd24-chaturvimshamsha', 'd30-trimshamsha', 'vimshottari-dasha', 'narayana-dasha', 'gochara', 'ashtakavarga', 'shadbala', 'arudha-pada', 'functional-benefic-malefic' ) ) then raise exception 'agentic_rectification_invalid_input' using errcode = 'P0001'; end if; if not exists ( select 1 from public.agentic_rectification_cases where id = p_case_id and user_id = p_user_id ) then raise exception 'agentic_rectification_case_not_found' using errcode = 'P0001'; end if; select count(*) into v_turn_count from public.agentic_rectification_turns where id = p_turn_id and case_id = p_case_id; if v_turn_count = 0 then raise exception 'agentic_rectification_turn_not_found' using errcode = 'P0001'; end if; insert into public.agentic_rectification_tool_receipts ( case_id, turn_id, tool_name, public_phase, status, input_fingerprint, result_fingerprint, engine_version, safe_error_code, executed_methods, completed_at ) values ( p_case_id, p_turn_id, p_tool_name, p_public_phase, p_status, p_input_fingerprint, p_result_fingerprint, p_engine_version, p_safe_error_code, p_executed_methods, case when p_status = 'completed' then pg_catalog.now() else null end ) returning id into v_receipt_id; return jsonb_build_object('receipt_id', v_receipt_id); end; $$; revoke all on function public.insert_agentic_rectification_tool_receipt( uuid, uuid, uuid, text, text, text, text, text, text, text, jsonb ) from public, anon, authenticated; grant execute on function public.insert_agentic_rectification_tool_receipt( uuid, uuid, uuid, text, text, text, text, text, text, text, jsonb ) to service_role; create or replace function public.get_agentic_rectification_turn_receipt( p_user_id uuid, p_case_id uuid, p_turn_id uuid ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_turn public.agentic_rectification_turns%rowtype; v_case public.agentic_rectification_cases%rowtype; v_phases jsonb; v_tools jsonb; v_methods jsonb; v_engine_version text; begin if p_user_id is null or p_case_id is null or p_turn_id is null then raise exception 'agentic_rectification_invalid_input' using errcode = 'P0001'; end if; select * into v_case from public.agentic_rectification_cases where id = p_case_id and user_id = p_user_id; if not found then raise exception 'agentic_rectification_case_not_found' using errcode = 'P0001'; end if; select * into v_turn from public.agentic_rectification_turns where id = p_turn_id and case_id = p_case_id; if not found then raise exception 'agentic_rectification_turn_not_found' using errcode = 'P0001'; end if; select coalesce(jsonb_agg( jsonb_build_object('phase', rp.phase, 'tool', rp.tool_name) order by rp.sequence ), '[]'::jsonb) into v_phases from public.agentic_rectification_run_phases rp where rp.turn_id = p_turn_id; select coalesce(jsonb_agg(tool_name order by tool_name), '[]'::jsonb) into v_tools from ( select distinct tr.tool_name from public.agentic_rectification_tool_receipts tr where tr.turn_id = p_turn_id and tr.status = 'completed' ) tools; select coalesce(jsonb_agg(method order by method), '[]'::jsonb) into v_methods from ( select distinct jsonb_array_elements_text(tr.executed_methods) as method from public.agentic_rectification_tool_receipts tr where tr.turn_id = p_turn_id and tr.status = 'completed' ) methods; select max(tr.engine_version) into v_engine_version from public.agentic_rectification_tool_receipts tr where tr.turn_id = p_turn_id and tr.engine_version is not null; return jsonb_build_object( 'turn_id', v_turn.id, 'status', v_turn.status, 'skill_name', v_case.skill_name, 'skill_version', v_case.skill_version, 'engine_version', v_engine_version, 'phases', v_phases, 'tools', v_tools, 'methods', v_methods, 'started_at', v_turn.created_at, 'completed_at', v_turn.completed_at ); end; $$; revoke all on function public.get_agentic_rectification_turn_receipt(uuid, uuid, uuid) from public, anon, authenticated; grant execute on function public.get_agentic_rectification_turn_receipt(uuid, uuid, uuid) to service_role; create temporary table rectification_birth_context_affected_cases on commit drop as select c.id from public.agentic_rectification_cases c join public.profiles p on p.id = c.user_id where c.status = any (public.agentic_rectification_resumable_statuses()) and ( nullif(btrim(c.baseline_birth_snapshot ->> 'birth_place_label'), '') is null or nullif(btrim(c.baseline_birth_snapshot ->> 'timezone_id'), '') is null or c.baseline_birth_snapshot ->> 'birth_place_label' is distinct from p.birth_place_label or c.baseline_birth_snapshot ->> 'timezone_id' is distinct from p.timezone_id ); update public.agentic_rectification_cases c set baseline_birth_snapshot = c.baseline_birth_snapshot || jsonb_build_object( 'birth_place_label', p.birth_place_label, 'timezone_id', p.timezone_id ) from public.profiles p, rectification_birth_context_affected_cases affected where c.id = affected.id and p.id = c.user_id and nullif(btrim(coalesce(p.birth_place_label, '')), '') is not null and nullif(btrim(coalesce(p.timezone_id, '')), '') is not null; update public.agentic_rectification_cases c set status = 'needs_rebaseline', updated_at = pg_catalog.now(), last_activity_at = pg_catalog.now() from rectification_birth_context_affected_cases affected where c.id = affected.id; update public.agentic_rectification_results r set invalidated_at = pg_catalog.now(), updated_at = pg_catalog.now() from rectification_birth_context_affected_cases affected where r.case_id = affected.id and r.invalidated_at is null; create or replace function public.agentic_rectification_profiles_rebaseline_guard() returns trigger language plpgsql security definer set search_path = '' as $$ begin if old.birth_date is distinct from new.birth_date or old.birth_place_label is distinct from new.birth_place_label or old.reported_birth_time is distinct from new.reported_birth_time or old.active_birth_time is distinct from new.active_birth_time or old.birth_time_source is distinct from new.birth_time_source or old.birth_time_period is distinct from new.birth_time_period or old.uncertainty_before_minutes is distinct from new.uncertainty_before_minutes or old.uncertainty_after_minutes is distinct from new.uncertainty_after_minutes or old.latitude is distinct from new.latitude or old.longitude is distinct from new.longitude or old.timezone_id is distinct from new.timezone_id or old.timezone_offset is distinct from new.timezone_offset then update public.agentic_rectification_cases set status = 'needs_rebaseline', updated_at = pg_catalog.now(), last_activity_at = pg_catalog.now() where user_id = new.id and status = any (public.agentic_rectification_resumable_statuses()); end if; return new; end; $$; revoke all on function public.agentic_rectification_profiles_rebaseline_guard() from public, anon, authenticated; drop trigger if exists agentic_rectification_profiles_rebaseline_guard_trigger on public.profiles; create trigger agentic_rectification_profiles_rebaseline_guard_trigger after update of birth_date, birth_place_label, reported_birth_time, active_birth_time, birth_time_source, birth_time_period, uncertainty_before_minutes, uncertainty_after_minutes, latitude, longitude, timezone_id, timezone_offset on public.profiles for each row execute function public.agentic_rectification_profiles_rebaseline_guard(); create or replace function public.invalidate_agentic_rectification_results_on_profile_change() returns trigger language plpgsql security definer set search_path = '' as $$ begin if old.birth_date is distinct from new.birth_date or old.birth_place_label is distinct from new.birth_place_label or old.reported_birth_time is distinct from new.reported_birth_time or old.birth_time_source is distinct from new.birth_time_source or old.birth_time_period is distinct from new.birth_time_period or old.uncertainty_before_minutes is distinct from new.uncertainty_before_minutes or old.uncertainty_after_minutes is distinct from new.uncertainty_after_minutes or old.latitude is distinct from new.latitude or old.longitude is distinct from new.longitude or old.timezone_id is distinct from new.timezone_id or old.timezone_offset is distinct from new.timezone_offset or ( (old.active_birth_time is distinct from new.active_birth_time or old.birth_time_status is distinct from new.birth_time_status) and coalesce(new.birth_time_status, '') not in ('accepted', 'confirmed') ) then update public.agentic_rectification_results set invalidated_at = pg_catalog.now(), updated_at = pg_catalog.now() where user_id = new.id and invalidated_at is null; end if; return new; end; $$; revoke all on function public.invalidate_agentic_rectification_results_on_profile_change() from public, anon, authenticated; drop trigger if exists profiles_invalidate_agentic_rectification_results on public.profiles; create trigger profiles_invalidate_agentic_rectification_results after update of birth_date, birth_place_label, reported_birth_time, active_birth_time, birth_time_status, birth_time_source, birth_time_period, uncertainty_before_minutes, uncertainty_after_minutes, latitude, longitude, timezone_id, timezone_offset on public.profiles for each row execute function public.invalidate_agentic_rectification_results_on_profile_change(); commit;