-- 053_clan_event_store.sql -- Minimal CLAN event store: consent_events (immutable), artifacts (versioned), -- outbox (pending/done), state_transitions (immutable) begin; create schema if not exists clan; -- 1) Immutable ConsentEvents create table if not exists clan.clan_consent_events ( consent_event_id text primary key, payload jsonb not null, decision_type text not null, target_artifact_ids text[] not null, request_id text not null, created_ts timestamptz not null default now(), constraint chk_consent_decision_type check (decision_type in ('approve', 'reject', 'revoke')) ); create index if not exists idx_clan_consent_events_created_ts on clan.clan_consent_events(created_ts desc); -- 2) Versioned Artifacts (CAS by version) create table if not exists clan.clan_artifacts ( artifact_id text primary key, artifact_type text not null, status text not null, visibility_level text not null, payload jsonb not null default '{}'::jsonb, provenance jsonb not null default '[]'::jsonb, version bigint not null default 1, created_ts timestamptz not null default now(), updated_ts timestamptz not null default now(), constraint chk_artifact_visibility check (visibility_level in ('public', 'interclan', 'incircle', 'soulsafe', 'sacred')), constraint chk_artifact_status check ( status in ( 'draft', 'waiting_for_consent', 'needs_confirmation', 'approved_for_execution', 'confirmed', 'rejected', 'revoked' ) ) ); create index if not exists idx_clan_artifacts_status on clan.clan_artifacts(status); create index if not exists idx_clan_artifacts_updated_ts on clan.clan_artifacts(updated_ts desc); -- 3) Outbox apply_consent (idempotent by outbox_id) create table if not exists clan.clan_outbox ( outbox_id text primary key, -- outbox_{consent_event_id} event_type text not null, -- apply_consent consent_event_id text not null references clan.clan_consent_events(consent_event_id) on delete restrict, target_artifact_ids text[] not null, request_id text not null, status text not null default 'pending', -- pending|done attempts int not null default 0, last_error text, created_ts timestamptz not null default now(), updated_ts timestamptz not null default now(), constraint chk_outbox_status check (status in ('pending', 'done')), constraint chk_outbox_event_type check (event_type in ('apply_consent')) ); create index if not exists idx_clan_outbox_pending on clan.clan_outbox(status, created_ts); -- 4) Immutable Transition Log create table if not exists clan.clan_state_transitions ( transition_id text primary key, ts timestamptz not null default now(), artifact_id text not null references clan.clan_artifacts(artifact_id) on delete restrict, artifact_type text not null, from_status text not null, to_status text not null, op text not null, consent_event_id text not null references clan.clan_consent_events(consent_event_id) on delete restrict, decision_type text not null, request_id text not null, visibility_level text not null, versions jsonb not null default '{}'::jsonb, constraint chk_transition_decision_type check (decision_type in ('approve', 'reject', 'revoke')), constraint chk_transition_visibility check (visibility_level in ('public', 'interclan', 'incircle', 'soulsafe', 'sacred')) ); create index if not exists idx_clan_transitions_artifact on clan.clan_state_transitions(artifact_id, ts desc); create index if not exists idx_clan_transitions_consent on clan.clan_state_transitions(consent_event_id, ts desc); commit;