fix: integrate people archive, report reader and western chart corrections
Validate on Linux Node 22 and PostgreSQL 17: 3894 frontend tests and 64 database tests pass, with no removed test names or new failures. Preserve static Home, bounded gzip, assertion-change records and manual acceptance gaps. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
-- Legacy JSON is not evidence of an accepted/confirmed rectification.
|
||||
-- This is a corrective migration, not a rewrite of the deployed backfill.
|
||||
begin;
|
||||
lock table public.chart_profiles in access exclusive mode;
|
||||
alter table public.chart_profiles disable trigger chart_profiles_guard_birth;
|
||||
update public.chart_profiles
|
||||
set birth_time_status = 'reported',
|
||||
active_birth_time = null,
|
||||
active_birth_date = null,
|
||||
active_birth_timezone_offset = null
|
||||
where role = 'other'
|
||||
and (birth_time_status in ('accepted', 'confirmed')
|
||||
or active_birth_time is not null
|
||||
or active_birth_date is not null
|
||||
or active_birth_timezone_offset is not null);
|
||||
alter table public.chart_profiles enable trigger chart_profiles_guard_birth;
|
||||
|
||||
create or replace function public.chart_profiles_guard_limit()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
set search_path = ''
|
||||
as $$
|
||||
declare
|
||||
others integer;
|
||||
begin
|
||||
if new.role is distinct from 'other' then
|
||||
raise exception 'chart_subject_self_mirror_retired';
|
||||
end if;
|
||||
if tg_op = 'INSERT' or new.user_id is distinct from old.user_id then
|
||||
-- The lock and count are separate statements: after a competing insert
|
||||
-- commits, READ COMMITTED sees its row before deciding the limit.
|
||||
perform pg_catalog.pg_advisory_xact_lock(pg_catalog.hashtextextended(new.user_id::text, 73031));
|
||||
select count(*) into others from public.chart_profiles
|
||||
where user_id = new.user_id and role = 'other';
|
||||
if others >= 4 then
|
||||
raise exception 'subject_limit_reached';
|
||||
end if;
|
||||
end if;
|
||||
return new;
|
||||
end;
|
||||
$$;
|
||||
commit;
|
||||
@@ -0,0 +1,64 @@
|
||||
begin;
|
||||
|
||||
-- A subject deletion cascades through reports/jobs before a worker can observe
|
||||
-- the missing subject. Release the reservation in that same transaction.
|
||||
create or replace function public.release_deleted_report_reservation()
|
||||
returns trigger
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = ''
|
||||
as $$
|
||||
declare
|
||||
released record;
|
||||
begin
|
||||
-- A ready document has already been delivered, even if the worker is between
|
||||
-- its durable ready write and billing completion. Do not refund that window.
|
||||
if old.status = 'ready' then return old; end if;
|
||||
-- Account removal may already have cascaded away the wallet itself.
|
||||
if not exists (select 1 from public.profiles where id = old.user_id) then return old; end if;
|
||||
select * into released from public.release_usage(old.user_id, old.request_id::text, 'report_deleted');
|
||||
-- Completed usage remains charged. Any other failure must roll back deletion
|
||||
-- rather than orphan a paid reservation with no durable job left to refund it.
|
||||
if not released.success and released.error_code is distinct from 'request_completed' then
|
||||
raise exception 'report_reservation_release_failed';
|
||||
end if;
|
||||
return old;
|
||||
end;
|
||||
$$;
|
||||
|
||||
revoke all on function public.release_deleted_report_reservation() from public, anon, authenticated;
|
||||
create trigger personal_reports_release_before_delete
|
||||
before delete on public.personal_reports
|
||||
for each row execute function public.release_deleted_report_reservation();
|
||||
|
||||
-- Match the worker's job -> report lock order before the cascading delete.
|
||||
-- Definer is needed only for job locks; the caller identity/ownership check is
|
||||
-- explicit and no arbitrary refund operation is exposed to authenticated.
|
||||
create or replace function public.delete_chart_subject(p_id uuid)
|
||||
returns void
|
||||
language plpgsql
|
||||
security definer
|
||||
set search_path = ''
|
||||
as $$
|
||||
declare
|
||||
owner uuid := (select auth.uid());
|
||||
begin
|
||||
if owner is null then raise exception 'chart_subject_unauthenticated'; end if;
|
||||
perform 1 from public.chart_profiles
|
||||
where id = p_id and user_id = owner and role = 'other' for update;
|
||||
if not found then raise exception 'chart_subject_not_found'; end if;
|
||||
perform 1 from public.personal_report_jobs j
|
||||
join public.personal_reports r on r.user_id = j.user_id and r.request_id = j.request_id
|
||||
where r.user_id = owner and r.chart_profile_id = p_id
|
||||
order by j.id for update of j;
|
||||
perform 1 from public.personal_reports
|
||||
where user_id = owner and chart_profile_id = p_id order by id for update;
|
||||
delete from public.chat_sessions where user_id = owner and chart_profile_id = p_id::text;
|
||||
delete from public.personal_reports where user_id = owner and chart_profile_id = p_id;
|
||||
delete from public.chart_profiles where id = p_id and user_id = owner and role = 'other';
|
||||
end;
|
||||
$$;
|
||||
revoke all on function public.delete_chart_subject(uuid) from public, anon;
|
||||
grant execute on function public.delete_chart_subject(uuid) to authenticated;
|
||||
|
||||
commit;
|
||||
Reference in New Issue
Block a user