-- V15 — Discriminate open-alert_instance uniqueness by exchange id for PER_EXCHANGE rules.
--
-- V13 enforced "one open alert_instance per rule" via a partial unique on
-- (rule_id). That is correct for every scalar condition kind (ROUTE_METRIC,
-- AGENT_STATE, DEPLOYMENT_STATE, LOG_PATTERN, JVM_METRIC, and EXCHANGE_MATCH
-- in COUNT_IN_WINDOW mode) but wrong for EXCHANGE_MATCH / PER_EXCHANGE, which
-- by design emits one alert_instance per matching exchange. Under V13 every
-- PER_EXCHANGE tick with >1 match logged "Skipped duplicate open alert_instance
-- for rule …" at evaluator cadence and silently lost alert fidelity — only the
-- first matching exchange per tick got an AlertInstance + webhook dispatch.
--
-- New discriminator: (rule_id, COALESCE(context->'exchange'->>'id', '')).
-- Scalar evaluators emit Map.of() (no exchange subtree), the expression
-- resolves to '' for all of them, so they continue to get "one open per rule".
-- ExchangeMatchEvaluator.evaluatePerExchange emits {exchange.id = <execId>}
-- per firing, so PER_EXCHANGE instances for distinct exchanges coexist.
-- Two attempts to open an instance for the same (rule, exchange) still collapse
-- to one — the repo's DuplicateKeyException fallback preserves defense-in-depth.
DROP INDEX IF EXISTS alert_instances_open_rule_uq;

CREATE UNIQUE INDEX alert_instances_open_rule_uq
  ON alert_instances (rule_id, (COALESCE(context->'exchange'->>'id', '')))
  WHERE rule_id IS NOT NULL
    AND state IN ('PENDING','FIRING','ACKNOWLEDGED');
