defmodule Tarakan.Repo.Migrations.AddCalibrationRefutationEmbeddingToCanonicalFindings do
  use Ecto.Migration

  def change do
    alter table(:canonical_findings) do
      # Severity calibration. The submitter's claim stays in `severity` and is
      # never overwritten; this is a second opinion scored against a rubric, so
      # the two can be compared and the rubric can be re-run on old findings.
      add :calibrated_severity, :string
      add :calibrated_at, :utc_datetime_usec
      add :severity_rubric, :string

      # Adversarial verification. `refutations_count` is attempts made,
      # `survived_refutations_count` is attempts that failed to break it.
      # Surviving attacks is a stronger claim than collecting agreements.
      add :refutations_count, :integer, null: false, default: 0
      add :survived_refutations_count, :integer, null: false, default: 0

      # Client-submitted embedding of the finding's code context, and the
      # semantic cluster derived from it. Kept separate from `pattern_key`
      # (a hash of the normalized title) so the two can disagree and the
      # title-based grouping keeps working while clusters fill in.
      add :embedding, {:array, :float}
      add :embedding_model, :string
      add :code_pattern_key, :string
    end

    create constraint(:canonical_findings, :canonical_findings_calibrated_severity_valid,
             check:
               "calibrated_severity IS NULL OR " <>
                 "calibrated_severity IN ('critical','high','medium','low','info')"
           )

    create constraint(:canonical_findings, :canonical_findings_refutations_non_negative,
             check: "refutations_count >= 0 AND survived_refutations_count >= 0"
           )

    create constraint(:canonical_findings, :canonical_findings_survived_within_attempts,
             check: "survived_refutations_count <= refutations_count"
           )

    create index(:canonical_findings, [:code_pattern_key])
    create index(:canonical_findings, [:calibrated_severity])
  end
end
