Skip to main content

priv/repo/migrations/20260526110000_add_guardrail_decisions.exs

defmodule Rulestead.Repo.Migrations.AddGuardrailDecisions do
  use Rulestead.Migration, prefix: "rulestead", create_schema: true

  def change do
    create rulestead_table(:guardrail_decisions, primary_key: false) do
      add(:id, :uuid, primary_key: true, default: fragment("gen_random_uuid()"))
      add(:flag_key, :text, null: false)
      add(:environment_key, :text, null: false)
      add(:rule_key, :text, null: false)
      add(:stage, :text, null: false)
      add(:tenant_key, :text)
      add(:decision_state, :text, null: false)
      add(:action_type, :text, null: false)
      add(:decision_reason, :text)
      add(:effective_percentage, :integer)
      add(:rollout_salt, :text)
      add(:variant_fingerprint, :text)
      add(:monitoring_window_started_at, :utc_datetime_usec)
      add(:monitoring_window_ends_at, :utc_datetime_usec)
      add(:occurred_at, :utc_datetime_usec, null: false)
      add(:signal_facts, {:array, :map}, null: false, default: [])
      add(:guardrail_evidence, :map, null: false, default: fragment("'{}'::jsonb"))
      add(:authored_snapshot, :map)
      add(:rollback_target_snapshot, :map)
      add(:correlation_id, :text)
      add(:metadata, :map, null: false, default: fragment("'{}'::jsonb"))

      timestamps(type: :utc_datetime_usec, updated_at: false)
    end

    create(rulestead_index(:guardrail_decisions, [:flag_key, :environment_key, :occurred_at]))

    create(
      rulestead_index(:guardrail_decisions, [
        :flag_key,
        :environment_key,
        :rule_key,
        :stage,
        :occurred_at
      ])
    )

    create(rulestead_index(:guardrail_decisions, [:correlation_id]))

    create(
      rulestead_constraint(
        :guardrail_decisions,
        :guardrail_decisions_decision_state_must_be_valid,
        check: "decision_state IN ('healthy', 'pending_data', 'held', 'rollback_triggered')"
      )
    )

    create(
      rulestead_constraint(:guardrail_decisions, :guardrail_decisions_action_type_must_be_valid,
        check: "action_type IN ('advance', 'evaluate', 'hold', 'rollback')"
      )
    )

    create(
      rulestead_constraint(:guardrail_decisions, :guardrail_decisions_effective_percentage_bounds,
        check:
          "effective_percentage IS NULL OR (effective_percentage >= 0 AND effective_percentage <= 100)"
      )
    )
  end
end