-- v12.1 execution-readiness delta. Prior migrations remain immutable. alter table building_blocks.outbox_message add column if not exists next_attempt_at timestamptz null, add column if not exists lease_owner text null, add column if not exists lease_until timestamptz null, add column if not exists dead_lettered_at timestamptz null, add column if not exists last_error_code text null; create index if not exists ix_outbox_dispatchable on building_blocks.outbox_message (coalesce(next_attempt_at, occurred_at), occurred_at, message_id) where published_at is null and dead_lettered_at is null; alter table building_blocks.inbox_message add column if not exists processed_at timestamptz null, add column if not exists result_hash text null; create table if not exists building_blocks.projection_checkpoint ( projection_name text not null, projection_version integer not null check (projection_version > 0), source_watermark text not null, last_event_id uuid null, content_hash text not null, built_at timestamptz not null, primary key (projection_name, projection_version) ); create table if not exists building_blocks.projection_rebuild_run ( rebuild_id uuid primary key, projection_name text not null, target_version integer not null check (target_version > 0), through_watermark text not null, dry_run boolean not null, requested_by text not null, requested_at timestamptz not null, status text not null, source_count bigint null, projected_count bigint null, result_hash text null, finished_at timestamptz null, error_code text null ); create table if not exists building_blocks.audit_event ( audit_event_id uuid primary key, entity_type text not null, entity_id text not null, event_type text not null, actor_id text not null, correlation_id text not null, payload_json jsonb not null, payload_hash text not null, corrects_audit_event_id uuid null references building_blocks.audit_event(audit_event_id), occurred_at timestamptz not null, unique (entity_type, entity_id, event_type, payload_hash) ); create or replace function building_blocks.prevent_append_only_change() returns trigger language plpgsql as $$ begin raise exception 'append-only row cannot be updated or deleted'; end; $$; drop trigger if exists audit_event_append_only on building_blocks.audit_event; create trigger audit_event_append_only before update or delete on building_blocks.audit_event for each row execute function building_blocks.prevent_append_only_change();