-- Regenerate the latest completed Assistant reply in place. -- This is a free, read-only Agent pass followed by one narrowly scoped turn -- text update. It never appends a turn or changes evidence, results, profiles, -- cases, candidate state, or billing records. begin; create table if not exists public.agentic_rectification_turn_regenerations ( request_id uuid primary key, user_id uuid not null, case_id uuid not null references public.agentic_rectification_cases(id) on delete cascade, turn_id uuid not null references public.agentic_rectification_turns(id) on delete cascade, assistant_message text not null check (length(btrim(assistant_message)) > 0), created_at timestamptz not null default pg_catalog.now() ); create index if not exists agentic_rectification_turn_regenerations_case_idx on public.agentic_rectification_turn_regenerations (case_id, created_at desc); alter table public.agentic_rectification_turn_regenerations enable row level security; revoke all on table public.agentic_rectification_turn_regenerations from public, anon, authenticated; grant all on table public.agentic_rectification_turn_regenerations to service_role; create or replace function public.get_agentic_rectification_turn_regeneration( p_user_id uuid, p_case_id uuid, p_turn_id uuid, p_request_id uuid ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_row public.agentic_rectification_turn_regenerations%rowtype; begin if p_user_id is null or p_case_id is null or p_turn_id is null or p_request_id is null then raise exception 'agentic_rectification_regeneration_invalid_input' using errcode = 'P0001'; end if; select * into v_row from public.agentic_rectification_turn_regenerations where request_id = p_request_id and user_id = p_user_id and case_id = p_case_id and turn_id = p_turn_id; if not found then return null; end if; return jsonb_build_object( 'ok', true, 'turn_id', v_row.turn_id, 'assistant_message', v_row.assistant_message, 'idempotent', true ); end; $$; revoke all on function public.get_agentic_rectification_turn_regeneration(uuid, uuid, uuid, uuid) from public, anon, authenticated; grant execute on function public.get_agentic_rectification_turn_regeneration(uuid, uuid, uuid, uuid) to service_role; create or replace function public.regenerate_agentic_rectification_turn( p_user_id uuid, p_case_id uuid, p_session_id uuid, p_turn_id uuid, p_request_id uuid, p_assistant_message text ) returns jsonb language plpgsql security definer set search_path = '' as $$ declare v_case public.agentic_rectification_cases%rowtype; v_turn public.agentic_rectification_turns%rowtype; v_latest_turn_id uuid; v_existing public.agentic_rectification_turn_regenerations%rowtype; v_message text; begin if p_user_id is null or p_case_id is null or p_session_id is null or p_turn_id is null or p_request_id is null or p_assistant_message is null or length(btrim(p_assistant_message)) = 0 then raise exception 'agentic_rectification_regeneration_invalid_input' using errcode = 'P0001'; end if; perform pg_catalog.pg_advisory_xact_lock( pg_catalog.hashtextextended('agentic-rectification-regenerate:' || p_case_id::text, 0) ); 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; if v_case.session_id <> p_session_id then raise exception 'agentic_rectification_case_session_mismatch' using errcode = 'P0001'; end if; select * into v_existing from public.agentic_rectification_turn_regenerations where request_id = p_request_id; if found then if v_existing.user_id <> p_user_id or v_existing.case_id <> p_case_id or v_existing.turn_id <> p_turn_id then raise exception 'agentic_rectification_regeneration_request_conflict' using errcode = 'P0001'; end if; return jsonb_build_object( 'ok', true, 'turn_id', v_existing.turn_id, 'assistant_message', v_existing.assistant_message, 'idempotent', true ); end if; if v_case.status in ('confirmed', 'closed', 'abandoned', 'superseded') then raise exception 'agentic_rectification_case_terminal' 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; if v_turn.status <> 'completed' or v_turn.assistant_message is null or length(btrim(v_turn.assistant_message)) = 0 then raise exception 'agentic_rectification_turn_not_completed' using errcode = 'P0001'; end if; select id into v_latest_turn_id from public.agentic_rectification_turns where case_id = p_case_id and status = 'completed' and assistant_message is not null and length(btrim(assistant_message)) > 0 order by created_at desc, id desc limit 1; if v_latest_turn_id is distinct from p_turn_id then raise exception 'agentic_rectification_turn_not_latest' using errcode = 'P0001'; end if; v_message := btrim(p_assistant_message); update public.agentic_rectification_turns set assistant_message = v_message, updated_at = pg_catalog.now() where id = p_turn_id and case_id = p_case_id; insert into public.agentic_rectification_turn_regenerations ( request_id, user_id, case_id, turn_id, assistant_message ) values ( p_request_id, p_user_id, p_case_id, p_turn_id, v_message ); return jsonb_build_object( 'ok', true, 'turn_id', p_turn_id, 'assistant_message', v_message, 'idempotent', false ); end; $$; revoke all on function public.regenerate_agentic_rectification_turn(uuid, uuid, uuid, uuid, uuid, text) from public, anon, authenticated; grant execute on function public.regenerate_agentic_rectification_turn(uuid, uuid, uuid, uuid, uuid, text) to service_role; commit;