"""Phase 12: Risk Intelligence — technique_risk_profiles table Revision ID: b038risk Revises: b037know Create Date: 2026-05-20 Uses raw SQL to bypass SQLAlchemy DDL hooks. """ from typing import Union from alembic import op import sqlalchemy as sa revision: str = "b038risk" down_revision: Union[str, None] = "b037know" branch_labels = None depends_on = None def upgrade() -> None: conn = op.get_bind() conn.execute(sa.text(""" CREATE TABLE IF NOT EXISTS technique_risk_profiles ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), technique_id UUID NOT NULL REFERENCES techniques(id) ON DELETE CASCADE, risk_score FLOAT NOT NULL DEFAULT 0.0, likelihood FLOAT NOT NULL DEFAULT 0.0, impact FLOAT NOT NULL DEFAULT 0.0, risk_level VARCHAR(16) NOT NULL DEFAULT 'info', detection_gap FLOAT NOT NULL DEFAULT 1.0, threat_actor_count INTEGER NOT NULL DEFAULT 0, osint_signal_count INTEGER NOT NULL DEFAULT 0, test_fail_count INTEGER NOT NULL DEFAULT 0, test_total_count INTEGER NOT NULL DEFAULT 0, test_failure_rate FLOAT NOT NULL DEFAULT 0.0, confidence_level FLOAT NOT NULL DEFAULT 0.0, scoring_breakdown JSONB, recommendations JSONB, computed_at TIMESTAMP DEFAULT now(), is_stale BOOLEAN DEFAULT TRUE, CONSTRAINT uq_risk_profile_technique UNIQUE (technique_id) ) """)) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_risk_profiles_risk_score " "ON technique_risk_profiles (risk_score)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_risk_profiles_risk_level " "ON technique_risk_profiles (risk_level)" )) conn.execute(sa.text( "CREATE INDEX IF NOT EXISTS ix_risk_profiles_stale " "ON technique_risk_profiles (is_stale)" )) def downgrade() -> None: conn = op.get_bind() conn.execute(sa.text("DROP TABLE IF EXISTS technique_risk_profiles"))