-- A freeze/unfreeze is a viewer-visible user projection change. Version the -- durable fact so a claimed old nudge can never acknowledge a newer state. ALTER TABLE public.account_restrictions ADD COLUMN version bigint DEFAULT 1 NOT NULL, ADD CONSTRAINT account_restrictions_version_check CHECK (version > 0); -- updateUser has no pts. This coalesced queue is only a crash-safe online -- nudge; offline clients reconstruct the current restriction from the -- authoritative account_restrictions row during normal user hydration. CREATE TABLE public.account_freeze_notifications ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, target_user_id bigint NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, frozen_user_id bigint NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, version bigint NOT NULL, frozen boolean NOT NULL, status text DEFAULT 'pending' NOT NULL, attempts integer DEFAULT 0 NOT NULL, next_attempt_at timestamp with time zone DEFAULT now() NOT NULL, lease_until timestamp with time zone, last_error text DEFAULT '' NOT NULL, created_at timestamp with time zone DEFAULT now() NOT NULL, updated_at timestamp with time zone DEFAULT now() NOT NULL, CONSTRAINT account_freeze_notifications_status_check CHECK (status IN ('pending', 'dispatching', 'delivered')), CONSTRAINT account_freeze_notifications_attempts_check CHECK (attempts >= 0), CONSTRAINT account_freeze_notifications_version_check CHECK (version > 0), CONSTRAINT account_freeze_notifications_not_self_check CHECK (target_user_id <> frozen_user_id), UNIQUE (target_user_id, frozen_user_id) ); CREATE INDEX account_freeze_notifications_ready_idx ON public.account_freeze_notifications(next_attempt_at, id) WHERE status = 'pending'; CREATE INDEX account_freeze_notifications_lease_idx ON public.account_freeze_notifications(lease_until, id) WHERE status = 'dispatching';