lib/openagents/shadow_programs/run.ex

main at 58e6347eeb72 · 1 KB

defmodule OpenAgents.ShadowPrograms.Run do
  @moduledoc "Immutable terminal comparison receipt for one shadow decision."

  use Ecto.Schema
  import Ecto.Changeset

  @primary_key {:id, :binary_id, autogenerate: true}
  @foreign_key_type :binary_id
  @timestamps_opts [type: :utc_datetime_usec, updated_at: false]

  @type t :: %__MODULE__{}

  schema "shadow_program_runs" do
    belongs_to :turn_receipt, OpenAgents.Conversations.TurnReceipt
    field :signature_id, :string
    field :signature_version, :integer
    field :artifact_id, :string
    field :artifact_digest, :string
    field :input_digest, :string
    field :baseline_output, :map
    field :candidate_output, :map
    field :candidate_output_digest, :string
    field :status, :string
    field :comparison, :map
    field :provider_id, :string
    field :provider_response_id, :string
    field :usage, :map
    field :latency_ms, :integer
    field :failure_code, :string
    field :completed_at, :utc_datetime_usec
    timestamps()
  end

  def changeset(run, attributes) do
    run
    |> cast(attributes, [
      :signature_id,
      :signature_version,
      :artifact_id,
      :artifact_digest,
      :input_digest,
      :baseline_output,
      :candidate_output,
      :candidate_output_digest,
      :status,
      :comparison,
      :provider_id,
      :provider_response_id,
      :usage,
      :latency_ms,
      :failure_code,
      :completed_at
    ])
    |> validate_required([
      :signature_id,
      :signature_version,
      :input_digest,
      :baseline_output,
      :status,
      :comparison,
      :provider_id,
      :latency_ms,
      :completed_at
    ])
    |> validate_inclusion(:status, ~w(completed degraded malformed failed timed_out))
    |> validate_number(:latency_ms, greater_than_or_equal_to: 0)
    |> validate_format(:input_digest, ~r/\A[0-9a-f]{64}\z/)
    |> foreign_key_constraint(:turn_receipt_id)
  end
end