create table if not exists signal_engine.evidence_snapshot ( evidence_id text primary key, as_of timestamptz not null, published_at_cutoff timestamptz not null, dataset_id text not null, data_hash text not null, model_version text not null, config_version text not null, payload jsonb not null, content_hash text not null unique, created_at timestamptz not null default now() ); create table if not exists signal_engine.signal_decision ( decision_id uuid primary key, lot_id uuid not null, cycle_id uuid not null, evidence_id text not null references signal_engine.evidence_snapshot(evidence_id), action text not null, sell_fraction numeric(9,6) not null check (sell_fraction between 0 and 1), policy_id text not null, priority integer not null, reason_code text not null, reentry_eligible boolean not null, created_at timestamptz not null, decision_hash text not null unique ); create table if not exists signal_engine.reentry_watch ( watch_id uuid primary key, sell_decision_id uuid not null unique references signal_engine.signal_decision(decision_id), prior_cycle_id uuid not null, state text not null, minimum_wait_sessions integer not null check (minimum_wait_sessions >= 0), minimum_spacing_sessions integer not null check (minimum_spacing_sessions >= 0), expires_after_sessions integer not null check (expires_after_sessions > 0), created_at timestamptz not null ); create table if not exists signal_engine.reentry_stage ( watch_id uuid not null references signal_engine.reentry_watch(watch_id), stage_no integer not null, target_fraction numeric(9,6) not null check (target_fraction > 0 and target_fraction <= 1), status text not null, eligible_at timestamptz null, executed_cycle_id uuid null, primary key (watch_id, stage_no) ); create or replace function signal_engine.prevent_immutable_change() returns trigger language plpgsql as $$ begin raise exception 'immutable evidence/decision rows cannot be updated or deleted'; end; $$; drop trigger if exists evidence_snapshot_immutable on signal_engine.evidence_snapshot; create trigger evidence_snapshot_immutable before update or delete on signal_engine.evidence_snapshot for each row execute function signal_engine.prevent_immutable_change();