-- IHF Phase 12 — Platform Memory and Continuous Learning -- Workplan: IHUB-WP-0013 -- outcome_correlations CREATE TABLE outcome_correlations ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, hub_id UUID NOT NULL REFERENCES hubs(id), annotation_category TEXT NOT NULL REFERENCES annotation_category_registry(name), correlation_type TEXT NOT NULL DEFAULT 'annotation_predictor', correlation_score DOUBLE PRECISION NOT NULL, sample_count INTEGER NOT NULL DEFAULT 0, computed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL, CHECK (correlation_type IN ('annotation_predictor', 'routing_quality', 'pattern_quality')) ); CREATE INDEX outcome_correlations_hub_idx ON outcome_correlations (hub_id); CREATE INDEX outcome_correlations_score_idx ON outcome_correlations (correlation_score DESC); -- pattern_performance_records CREATE TABLE pattern_performance_records ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, widget_pattern_id UUID NOT NULL REFERENCES widget_patterns(id), hub_id UUID NOT NULL REFERENCES hubs(id), adoption_count INTEGER NOT NULL DEFAULT 0, positive_outcome_count INTEGER NOT NULL DEFAULT 0, total_outcome_count INTEGER NOT NULL DEFAULT 0, mean_outcome_value DOUBLE PRECISION, outcome_rank INTEGER, calibrated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL, UNIQUE (widget_pattern_id, hub_id) ); CREATE INDEX pattern_performance_pattern_idx ON pattern_performance_records (widget_pattern_id); CREATE INDEX pattern_performance_rank_idx ON pattern_performance_records (hub_id, outcome_rank); -- adaptive_threshold_configs CREATE TABLE adaptive_threshold_configs ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, hub_id UUID NOT NULL REFERENCES hubs(id) UNIQUE, weight_overrides JSONB NOT NULL DEFAULT '{}', bottleneck_threshold_override DOUBLE PRECISION, calibration_date TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL, notes TEXT ); CREATE INDEX adaptive_threshold_hub_idx ON adaptive_threshold_configs (hub_id); -- institutional_knowledge_entries CREATE TABLE institutional_knowledge_entries ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, hub_id UUID NOT NULL REFERENCES hubs(id), decision_record_id UUID REFERENCES decision_records(id), summary TEXT NOT NULL, summary_tsv TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', summary)) STORED, tags JSONB NOT NULL DEFAULT '[]', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL, updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL ); CREATE INDEX institutional_knowledge_hub_idx ON institutional_knowledge_entries (hub_id); CREATE INDEX institutional_knowledge_fts_idx ON institutional_knowledge_entries USING GIN (summary_tsv); -- learning_insights CREATE TABLE learning_insights ( id UUID DEFAULT uuid_generate_v4() PRIMARY KEY NOT NULL, hub_id UUID NOT NULL REFERENCES hubs(id), insight_type TEXT NOT NULL, title TEXT NOT NULL, body TEXT NOT NULL, evidence_links JSONB NOT NULL DEFAULT '[]', is_actioned BOOLEAN NOT NULL DEFAULT FALSE, computed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL, CHECK (insight_type IN ( 'annotation_predictor', 'threshold_calibration', 'pattern_ranking', 'routing_improvement' )) ); CREATE INDEX learning_insights_hub_idx ON learning_insights (hub_id); CREATE INDEX learning_insights_type_idx ON learning_insights (insight_type); -- Extend core tables — outcome_summary for retroactive lineage enrichment -- GAAF rule 3: contracts/core/ updated separately ALTER TABLE decision_records ADD COLUMN outcome_summary JSONB NULL; ALTER TABLE requirement_candidates ADD COLUMN outcome_summary JSONB NULL; -- Retroactive lineage enrichment trigger (T06) CREATE OR REPLACE FUNCTION enrich_lineage_on_outcome() RETURNS TRIGGER LANGUAGE plpgsql AS $$ DECLARE v_dec_id UUID; v_req_id UUID; v_cand_id UUID; v_summary JSONB; BEGIN -- Walk chain upward from the new outcome_signal SELECT decision_id INTO v_dec_id FROM deployment_records WHERE id = NEW.deployment_id; IF v_dec_id IS NOT NULL THEN v_summary := jsonb_build_object( 'signal_type', NEW.signal_type, 'value', NEW.value, 'observed_at', NEW.observed_at ); -- Append to decision_records.outcome_summary (non-append-only column) UPDATE decision_records SET outcome_summary = COALESCE(outcome_summary, '[]'::jsonb) || v_summary WHERE id = v_dec_id; SELECT requirement_id INTO v_req_id FROM decision_records WHERE id = v_dec_id; IF v_req_id IS NOT NULL THEN SELECT candidate_id INTO v_cand_id FROM requirements WHERE id = v_req_id; IF v_cand_id IS NOT NULL THEN UPDATE requirement_candidates SET outcome_summary = COALESCE(outcome_summary, '[]'::jsonb) || v_summary WHERE id = v_cand_id; END IF; END IF; END IF; RETURN NEW; END; $$; CREATE TRIGGER trg_enrich_lineage AFTER INSERT ON outcome_signals FOR EACH ROW EXECUTE FUNCTION enrich_lineage_on_outcome(); -- Batch enrichment helper: called on-demand by EnrichLineageAction -- Applies the same logic as enrich_lineage_on_outcome() for a given signal id CREATE OR REPLACE FUNCTION enrich_lineage_on_outcome_batch(p_signal_id UUID) RETURNS VOID LANGUAGE plpgsql AS $$ DECLARE v_sig RECORD; v_dec_id UUID; v_req_id UUID; v_cand_id UUID; v_summary JSONB; BEGIN SELECT * INTO v_sig FROM outcome_signals WHERE id = p_signal_id; SELECT decision_id INTO v_dec_id FROM deployment_records WHERE id = v_sig.deployment_id; IF v_dec_id IS NOT NULL THEN v_summary := jsonb_build_object( 'signal_type', v_sig.signal_type, 'value', v_sig.value, 'observed_at', v_sig.observed_at ); UPDATE decision_records SET outcome_summary = COALESCE(outcome_summary, '[]'::jsonb) || v_summary WHERE id = v_dec_id; SELECT requirement_id INTO v_req_id FROM decision_records WHERE id = v_dec_id; IF v_req_id IS NOT NULL THEN SELECT candidate_id INTO v_cand_id FROM requirements WHERE id = v_req_id; IF v_cand_id IS NOT NULL THEN UPDATE requirement_candidates SET outcome_summary = COALESCE(outcome_summary, '[]'::jsonb) || v_summary WHERE id = v_cand_id; END IF; END IF; END IF; END; $$;