-- Migration 003: Operations (signals, checks, approvals, status) -- Signals are dual entities (entities row + signals table for indexed querying). CREATE TABLE check_defs ( entity_id UUID PRIMARY KEY REFERENCES entities(id), target_id UUID REFERENCES entities(id), target_type TEXT REFERENCES entity_types(name), kind TEXT NOT NULL, config JSONB NOT NULL DEFAULT '{}', interval_s INTEGER NOT NULL DEFAULT 600, timeout_s INTEGER NOT NULL DEFAULT 10, zone TEXT, enabled BOOLEAN NOT NULL DEFAULT true, updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE signals ( entity_id UUID PRIMARY KEY REFERENCES entities(id), kind TEXT NOT NULL, severity TEXT NOT NULL CHECK (severity IN ('info','warning','critical')), target_entity_id UUID REFERENCES entities(id), check_id UUID REFERENCES check_defs(entity_id), evidence TEXT, likely_cause TEXT, state TEXT NOT NULL DEFAULT 'raised', occurrence_count INTEGER NOT NULL DEFAULT 1, first_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(), last_seen_at TIMESTAMPTZ NOT NULL DEFAULT now(), flap_count INTEGER NOT NULL DEFAULT 0, hold_down_until TIMESTAMPTZ, mute_until TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- At most ONE open signal per (target, kind) — repeats update the open row CREATE UNIQUE INDEX uq_signals_open ON signals(target_entity_id, kind) WHERE state NOT IN ('resolved','failed'); CREATE INDEX idx_signals_state ON signals(state); CREATE TABLE approvals ( entity_id UUID PRIMARY KEY REFERENCES entities(id), subject_entity_id UUID REFERENCES entities(id), action TEXT NOT NULL, risk_class TEXT NOT NULL, kind TEXT NOT NULL DEFAULT 'execution', payload JSONB, status TEXT NOT NULL DEFAULT 'pending', token_hash TEXT, expires_at TIMESTAMPTZ NOT NULL, decided_at TIMESTAMPTZ, decided_by UUID REFERENCES entities(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE entity_status ( entity_id UUID PRIMARY KEY REFERENCES entities(id), health TEXT NOT NULL DEFAULT 'unknown', last_check_at TIMESTAMPTZ, details JSONB NOT NULL DEFAULT '{}', updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE idempotency_keys ( key TEXT NOT NULL, actor TEXT NOT NULL, request_hash TEXT NOT NULL, response_code INTEGER, response_body JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (actor, key) );