75dbab08b9
Dateless occupation_note stayed draft, so classic coverage never finished and offer-candidates stayed blocked. Confirm those notes, stop crowding dasha probes with encoded exam quality, and adopt once blocking methods are covered. Co-authored-by: Cursor <cursoragent@cursor.com>
321 lines
11 KiB
PL/PgSQL
321 lines
11 KiB
PL/PgSQL
begin;
|
|
|
|
-- Dateless occupation_note (and other background notes) can be confirmed
|
|
-- without a calendar date. Career dated events still do not cover occupation.
|
|
|
|
create or replace function public.agentic_rectification_allows_dateless_confirm(p_kind text)
|
|
returns boolean
|
|
language sql
|
|
immutable
|
|
as $$
|
|
select p_kind in ('occupation_note', 'appearance_note', 'birthmark_or_scar')
|
|
$$;
|
|
|
|
revoke all on function public.agentic_rectification_allows_dateless_confirm(text)
|
|
from public, anon, authenticated;
|
|
grant execute on function public.agentic_rectification_allows_dateless_confirm(text)
|
|
to service_role;
|
|
|
|
create or replace function public.record_agentic_rectification_evidence_batch(
|
|
p_user_id uuid,
|
|
p_case_id uuid,
|
|
p_source_turn_id uuid,
|
|
p_focus_id uuid,
|
|
p_items jsonb
|
|
)
|
|
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_focus public.agentic_rectification_conversation_focuses%rowtype;
|
|
v_focus_replay boolean := false;
|
|
v_focus_match_id uuid;
|
|
v_focus_match_count integer := 0;
|
|
v_focus_resolution text := 'not_requested';
|
|
v_item jsonb;
|
|
v_index bigint;
|
|
v_item_key text;
|
|
v_quote text;
|
|
v_subject text;
|
|
v_kind text;
|
|
v_domain text;
|
|
v_precision text;
|
|
v_summary text;
|
|
v_from date;
|
|
v_to date;
|
|
v_status text;
|
|
v_outcome text;
|
|
v_error text;
|
|
v_clarification jsonb;
|
|
v_existing public.agentic_rectification_evidence%rowtype;
|
|
v_evidence_id uuid;
|
|
v_idempotent boolean;
|
|
v_results jsonb := '[]'::jsonb;
|
|
v_accepted integer := 0;
|
|
v_needs integer := 0;
|
|
v_rejected integer := 0;
|
|
begin
|
|
if p_user_id is null or p_case_id is null or p_source_turn_id is null
|
|
or p_items is null or jsonb_typeof(p_items) <> 'array'
|
|
or jsonb_array_length(p_items) = 0 or jsonb_array_length(p_items) > 12 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
|
|
for update;
|
|
if not found then
|
|
raise exception 'agentic_rectification_case_not_found' using errcode = 'P0001';
|
|
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_source_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.user_message is null then
|
|
raise exception 'agentic_rectification_quote_not_grounded' using errcode = 'P0001';
|
|
end if;
|
|
|
|
if p_focus_id is not null then
|
|
select * into v_focus
|
|
from public.agentic_rectification_conversation_focuses
|
|
where id = p_focus_id and case_id = p_case_id
|
|
for update;
|
|
if not found or v_focus.status not in ('active', 'resolved') then
|
|
raise exception 'agentic_rectification_focus_not_active' using errcode = 'P0001';
|
|
end if;
|
|
v_focus_replay := v_focus.status = 'resolved';
|
|
if not v_focus_replay and v_focus.target_evidence_id is not null then
|
|
raise exception 'agentic_rectification_focus_target_mismatch' using errcode = 'P0001';
|
|
end if;
|
|
v_focus_resolution := case when v_focus_replay then 'idempotent' else 'active' end;
|
|
if v_focus_replay and exists (
|
|
select 1
|
|
from jsonb_array_elements(p_items) item
|
|
where length(btrim(coalesce(item->>'idempotency_key', ''))) = 0
|
|
or not exists (
|
|
select 1 from public.agentic_rectification_evidence e
|
|
where e.case_id = p_case_id
|
|
and e.idempotency_key = btrim(item->>'idempotency_key')
|
|
)
|
|
) then
|
|
raise exception 'agentic_rectification_focus_not_active' using errcode = 'P0001';
|
|
end if;
|
|
end if;
|
|
|
|
for v_item, v_index in
|
|
select value, ordinality
|
|
from jsonb_array_elements(p_items) with ordinality
|
|
loop
|
|
v_item_key := btrim(coalesce(v_item->>'idempotency_key', ''));
|
|
v_quote := btrim(coalesce(v_item->>'quote', ''));
|
|
v_subject := coalesce(v_item->>'subject', '');
|
|
v_kind := coalesce(v_item->>'event_kind', '');
|
|
v_domain := coalesce(v_item->>'domain', '');
|
|
v_precision := coalesce(v_item->>'date_precision', '');
|
|
v_summary := btrim(coalesce(v_item->>'summary', ''));
|
|
v_from := null;
|
|
v_to := null;
|
|
v_error := null;
|
|
v_clarification := '[]'::jsonb;
|
|
v_evidence_id := null;
|
|
v_idempotent := false;
|
|
|
|
begin
|
|
if nullif(v_item->>'occurred_from', '') is not null then
|
|
v_from := (v_item->>'occurred_from')::date;
|
|
end if;
|
|
if nullif(v_item->>'occurred_to', '') is not null then
|
|
v_to := (v_item->>'occurred_to')::date;
|
|
end if;
|
|
exception when others then
|
|
v_error := 'invalid_date';
|
|
end;
|
|
|
|
if v_error is null and (
|
|
length(v_item_key) = 0 or length(v_item_key) > 160
|
|
or length(v_quote) = 0 or length(v_summary) = 0
|
|
or v_subject not in ('self', 'family', 'other')
|
|
or not (v_kind = any (public.agentic_rectification_evidence_kinds()))
|
|
or not (v_domain = any (public.agentic_rectification_evidence_domains()))
|
|
or not (v_precision = any (public.agentic_rectification_date_precisions()))
|
|
) then
|
|
v_error := 'invalid_item';
|
|
end if;
|
|
|
|
if v_error is null and position(
|
|
public.agentic_rectification_normalize_quote(v_quote)
|
|
in public.agentic_rectification_normalize_quote(v_turn.user_message)
|
|
) = 0 then
|
|
v_error := 'quote_not_grounded';
|
|
end if;
|
|
|
|
if v_error is null and v_precision = 'range'
|
|
and (v_from is null or v_to is null or v_from > v_to) then
|
|
v_error := 'invalid_range';
|
|
end if;
|
|
|
|
if v_error is not null then
|
|
v_outcome := 'rejected';
|
|
v_status := 'rejected';
|
|
v_rejected := v_rejected + 1;
|
|
else
|
|
select * into v_existing
|
|
from public.agentic_rectification_evidence
|
|
where case_id = p_case_id and idempotency_key = v_item_key;
|
|
|
|
if found then
|
|
if v_existing.source_turn_id is distinct from p_source_turn_id
|
|
or v_existing.user_quote is distinct from v_quote
|
|
or v_existing.subject is distinct from v_subject
|
|
or v_existing.event_kind is distinct from v_kind
|
|
or v_existing.domain is distinct from v_domain
|
|
or v_existing.occurred_from is distinct from v_from
|
|
or v_existing.occurred_to is distinct from v_to
|
|
or v_existing.date_precision is distinct from v_precision
|
|
or v_existing.summary is distinct from v_summary then
|
|
v_outcome := 'rejected';
|
|
v_status := 'rejected';
|
|
v_error := 'idempotency_conflict';
|
|
v_rejected := v_rejected + 1;
|
|
else
|
|
v_evidence_id := v_existing.id;
|
|
v_idempotent := true;
|
|
if v_existing.status = 'confirmed' then
|
|
v_outcome := 'accepted';
|
|
v_status := 'confirmed';
|
|
v_accepted := v_accepted + 1;
|
|
elsif public.agentic_rectification_allows_dateless_confirm(v_kind)
|
|
and (v_precision = 'unknown' or v_from is null) then
|
|
update public.agentic_rectification_evidence
|
|
set status = 'confirmed',
|
|
confirmed_at = coalesce(confirmed_at, pg_catalog.now()),
|
|
updated_at = pg_catalog.now()
|
|
where id = v_existing.id
|
|
and status is distinct from 'confirmed';
|
|
v_outcome := 'accepted';
|
|
v_status := 'confirmed';
|
|
v_accepted := v_accepted + 1;
|
|
else
|
|
v_outcome := 'needs_clarification';
|
|
v_status := v_existing.status;
|
|
v_clarification := case
|
|
when v_existing.date_precision = 'unknown' then '["date"]'::jsonb
|
|
else '[]'::jsonb
|
|
end;
|
|
v_needs := v_needs + 1;
|
|
end if;
|
|
end if;
|
|
else
|
|
if (v_precision = 'unknown' or v_from is null)
|
|
and not public.agentic_rectification_allows_dateless_confirm(v_kind) then
|
|
v_outcome := 'needs_clarification';
|
|
v_status := 'draft';
|
|
v_clarification := '["date"]'::jsonb;
|
|
v_needs := v_needs + 1;
|
|
else
|
|
v_outcome := 'accepted';
|
|
v_status := 'confirmed';
|
|
v_accepted := v_accepted + 1;
|
|
end if;
|
|
|
|
insert into public.agentic_rectification_evidence (
|
|
case_id, source_turn_id, user_quote, subject, event_kind, domain,
|
|
occurred_from, occurred_to, date_precision, summary, status,
|
|
confirmed_at, idempotency_key
|
|
) values (
|
|
p_case_id, p_source_turn_id, v_quote, v_subject, v_kind, v_domain,
|
|
v_from, v_to, v_precision, v_summary, v_status,
|
|
case when v_status = 'confirmed' then pg_catalog.now() else null end,
|
|
v_item_key
|
|
) returning id into v_evidence_id;
|
|
end if;
|
|
end if;
|
|
|
|
if p_focus_id is not null
|
|
and v_outcome = 'accepted'
|
|
and v_evidence_id is not null
|
|
and (v_focus.target_domain is null or v_domain = v_focus.target_domain)
|
|
and (v_focus.target_kind is null or v_kind = v_focus.target_kind) then
|
|
v_focus_match_count := v_focus_match_count + 1;
|
|
if v_focus_match_id is null then
|
|
v_focus_match_id := v_evidence_id;
|
|
end if;
|
|
end if;
|
|
|
|
v_results := v_results || jsonb_build_array(jsonb_build_object(
|
|
'index', v_index - 1,
|
|
'idempotency_key', nullif(v_item_key, ''),
|
|
'outcome', v_outcome,
|
|
'evidence_id', v_evidence_id,
|
|
'status', v_status,
|
|
'idempotent', v_idempotent,
|
|
'clarification_fields', v_clarification,
|
|
'error_code', v_error
|
|
));
|
|
end loop;
|
|
|
|
update public.agentic_rectification_cases
|
|
set status = case when status = 'draft' and v_accepted > 0 then 'collecting_evidence' else status end,
|
|
last_activity_at = pg_catalog.now(),
|
|
updated_at = pg_catalog.now()
|
|
where id = p_case_id;
|
|
|
|
if p_focus_id is not null then
|
|
if v_focus_replay then
|
|
if v_focus_match_count <> 1
|
|
or v_focus_match_id is distinct from v_focus.target_evidence_id then
|
|
raise exception 'agentic_rectification_focus_idempotency_conflict' using errcode = 'P0001';
|
|
end if;
|
|
v_focus_resolution := 'idempotent';
|
|
elsif v_focus_match_count = 1 then
|
|
perform public.resolve_agentic_rectification_conversation_focus(
|
|
p_user_id, p_case_id, p_focus_id, 'resolved', v_focus_match_id
|
|
);
|
|
v_focus_resolution := 'resolved';
|
|
elsif v_focus_match_count > 1 then
|
|
v_focus_resolution := 'ambiguous';
|
|
else
|
|
v_focus_resolution := 'unmatched';
|
|
end if;
|
|
end if;
|
|
|
|
return jsonb_build_object(
|
|
'items', v_results,
|
|
'accepted_count', v_accepted,
|
|
'needs_clarification_count', v_needs,
|
|
'rejected_count', v_rejected,
|
|
'focus_id', p_focus_id,
|
|
'focus_evidence_id', v_focus_match_id,
|
|
'focus_resolution', v_focus_resolution
|
|
);
|
|
end;
|
|
$$;
|
|
|
|
revoke all on function public.record_agentic_rectification_evidence_batch(
|
|
uuid, uuid, uuid, uuid, jsonb
|
|
) from public, anon, authenticated;
|
|
grant execute on function public.record_agentic_rectification_evidence_batch(
|
|
uuid, uuid, uuid, uuid, jsonb
|
|
) to service_role;
|
|
|
|
update public.agentic_rectification_evidence
|
|
set status = 'confirmed',
|
|
confirmed_at = coalesce(confirmed_at, pg_catalog.now()),
|
|
updated_at = pg_catalog.now()
|
|
where event_kind in ('occupation_note', 'appearance_note', 'birthmark_or_scar')
|
|
and status in ('draft', 'pending_confirmation')
|
|
and (date_precision = 'unknown' or occurred_from is null);
|
|
|
|
commit;
|