Files
Jyotisha/frontend/supabase/migrations/20260920010000_rectification_receipt_result_identity.sql
T
jesse-uxandClaude Code 8d0359fc62
Independent Staging Quality Gate / validate (push) Successful in 10m4s
Independent Staging Quality Gate / publish (push) Successful in 10m26s
fix(rectification): enforce trusted result identity and preserve receipt provenance
Unify minute and block cache identity, keep unverifiable historical results read-only across server tools and write entrypoints, and aggregate completed receipt sources chronologically through a compatible function migration.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-20 18:03:38 +08:00

175 lines
5.7 KiB
PL/PgSQL

-- BUG-984: result provenance comes from the last completed result in the
-- selected attempt. Preserve owner/turn/attempt checks and the public shape.
-- Compatible with old writers; no table changes or historical row updates.
begin;
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_attempt_id uuid;
v_phases jsonb;
v_tool_activities 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;
v_attempt_id := v_turn.successful_attempt_id;
if v_attempt_id is null then
select id into v_attempt_id
from public.agentic_rectification_run_attempts
where turn_id = p_turn_id
order by attempt_number desc
limit 1;
end if;
select coalesce(jsonb_agg(
jsonb_build_object('phase', rp.phase, 'tool', rp.tool_name)
order by rp.sequence, rp.created_at, rp.id
), '[]'::jsonb) into v_phases
from public.agentic_rectification_run_phases rp
where rp.turn_id = p_turn_id
and (
(v_attempt_id is not null and rp.attempt_id = v_attempt_id)
or (v_attempt_id is null and rp.attempt_id is null)
);
with latest_terminal as materialized (
select distinct on (tr.tool_name)
tr.id,
tr.tool_name,
tr.status,
tr.executed_methods,
tr.started_at,
tr.safe_error_code,
tr.result_fingerprint
from public.agentic_rectification_tool_receipts tr
where tr.turn_id = p_turn_id
and tr.status in ('completed', 'failed')
and (
(v_attempt_id is not null and tr.attempt_id = v_attempt_id)
or (v_attempt_id is null and tr.attempt_id is null)
)
order by tr.tool_name, tr.started_at desc, tr.id desc
),
latest_started as materialized (
select distinct on (tr.tool_name)
tr.tool_name,
tr.started_at
from public.agentic_rectification_tool_receipts tr
where tr.turn_id = p_turn_id
and tr.status = 'started'
and (
(v_attempt_id is not null and tr.attempt_id = v_attempt_id)
or (v_attempt_id is null and tr.attempt_id is null)
)
order by tr.tool_name, tr.started_at desc, tr.id desc
)
select
coalesce(jsonb_agg(
jsonb_strip_nulls(jsonb_build_object(
'tool', terminal.tool_name,
'status', terminal.status,
'methods', case
when terminal.status = 'completed' then terminal.executed_methods
else '[]'::jsonb
end,
'started_at', coalesce(started.started_at, terminal.started_at),
'elapsed_ms', greatest(
0,
(extract(epoch from (
terminal.started_at - coalesce(started.started_at, terminal.started_at)
)) * 1000)::int
),
'error', terminal.safe_error_code,
'result_fingerprint', terminal.result_fingerprint
)) order by coalesce(started.started_at, terminal.started_at), terminal.id
), '[]'::jsonb),
coalesce(jsonb_agg(terminal.tool_name order by coalesce(started.started_at, terminal.started_at), terminal.id)
filter (where terminal.status = 'completed'), '[]'::jsonb)
into v_tool_activities, v_tools
from latest_terminal terminal
left join latest_started started on started.tool_name = terminal.tool_name;
with latest_terminal as materialized (
select distinct on (tr.tool_name)
tr.tool_name,
tr.status,
tr.executed_methods
from public.agentic_rectification_tool_receipts tr
where tr.turn_id = p_turn_id
and tr.status in ('completed', 'failed')
and (
(v_attempt_id is not null and tr.attempt_id = v_attempt_id)
or (v_attempt_id is null and tr.attempt_id is null)
)
order by tr.tool_name, tr.started_at desc, tr.id desc
)
select coalesce(jsonb_agg(method order by method), '[]'::jsonb) into v_methods
from (
select distinct jsonb_array_elements_text(terminal.executed_methods) as method
from latest_terminal terminal
where terminal.status = 'completed'
) methods;
select 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
and tr.status = 'completed'
and (
(v_attempt_id is not null and tr.attempt_id = v_attempt_id)
or (v_attempt_id is null and tr.attempt_id is null)
)
order by tr.started_at desc, tr.id desc
limit 1;
return jsonb_build_object(
'turn_id', v_turn.id,
'attempt_id', v_attempt_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,
'tool_activities', v_tool_activities,
'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;
commit;