-- IHF Phase 4: Outcome Observation and Antifragility Loop -- Adds: deployment_records, outcome_signals (append-only), change_evaluations CREATE TABLE deployment_records ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, impl_ref_id UUID REFERENCES implementation_change_references(id) ON DELETE SET NULL, decision_id UUID NOT NULL REFERENCES decision_records(id) ON DELETE RESTRICT, version_ref TEXT NOT NULL, deployed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, deployed_by UUID REFERENCES users(id), notes TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL ); CREATE INDEX deployment_records_decision_id_idx ON deployment_records (decision_id); CREATE INDEX deployment_records_deployed_at_idx ON deployment_records (deployed_at DESC); CREATE TABLE outcome_signals ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, widget_id UUID NOT NULL REFERENCES widgets(id) ON DELETE CASCADE, deployment_id UUID NOT NULL REFERENCES deployment_records(id) ON DELETE CASCADE, signal_type TEXT NOT NULL, value NUMERIC, observed_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL ); CREATE INDEX outcome_signals_widget_id_idx ON outcome_signals (widget_id); CREATE INDEX outcome_signals_deployment_id_idx ON outcome_signals (deployment_id); CREATE INDEX outcome_signals_observed_at_idx ON outcome_signals (observed_at DESC); CREATE OR REPLACE FUNCTION prevent_outcome_signal_mutation() RETURNS TRIGGER AS $$ BEGIN RAISE EXCEPTION 'outcome_signals is append-only: UPDATE and DELETE are not permitted'; END; $$ LANGUAGE plpgsql; CREATE TRIGGER outcome_signals_no_update BEFORE UPDATE ON outcome_signals FOR EACH ROW EXECUTE FUNCTION prevent_outcome_signal_mutation(); CREATE TRIGGER outcome_signals_no_delete BEFORE DELETE ON outcome_signals FOR EACH ROW EXECUTE FUNCTION prevent_outcome_signal_mutation(); CREATE TABLE change_evaluations ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, deployment_id UUID NOT NULL REFERENCES deployment_records(id) ON DELETE CASCADE, decision_id UUID REFERENCES decision_records(id) ON DELETE SET NULL, score SMALLINT NOT NULL CHECK (score BETWEEN 1 AND 5), rationale TEXT NOT NULL, evaluated_by UUID REFERENCES users(id), evaluated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, UNIQUE (deployment_id) ); CREATE INDEX change_evaluations_deployment_id_idx ON change_evaluations (deployment_id);