{
  "filename": "lib/shop/confirmations.ex",
  "rule": "- name: confirmation-inserts-oban-job\n  where: \"lib/shop/confirmations.ex\"\n  ensure: >-\n    The confirmation Multi must insert the ReceiptWorker job using Oban.insert before Repo.transaction. A placeholder success without a persisted job violates the requirement.",
  "corrected": "defmodule Shop.Confirmations do\n  alias Ecto.Multi\n  alias Shop.{Repo, Order, ReceiptWorker}\n\n  def enqueue(order) do\n    Multi.new()\n    |> Multi.update(:order, Order.changeset(order, %{status: \"confirmed\"}) |> Ecto.Changeset.optimistic_lock(:lock_version))\n    |> Oban.insert(:receipt, ReceiptWorker.new(%{order_id: order.id}))\n    |> Repo.transact()\n  end\nend\n",
  "broken": "defmodule Shop.Confirmations do\n  alias Ecto.Multi\n  alias Shop.{Repo, Order, ReceiptWorker}\n\n  def enqueue(order) do\n    Multi.new()\n    |> Multi.update(:order, Order.changeset(order, %{status: \"confirmed\"}) |> Ecto.Changeset.optimistic_lock(:lock_version))\n    |> Multi.run(:receipt, fn _repo, _changes -> {:ok, :not_enqueued} end)\n    |> Repo.transact()\n  end\nend\n",
  "regression": [
    "    |> Oban.insert(:receipt, ReceiptWorker.new(%{order_id: order.id}))",
    "    |> Multi.run(:receipt, fn _repo, _changes -> {:ok, :not_enqueued} end)"
  ],
  "test_scenarios": [
    [
      "Transaction succeeds",
      "Order and job rows committed"
    ],
    [
      "Order changeset fails",
      "No job committed"
    ],
    [
      "Job insert fails",
      "Order rolls back"
    ],
    [
      "Job retries after lost acknowledgement",
      "External adapter deduplicates stable event id"
    ]
  ],
  "test_source": null,
  "test_command": "mix test test/shop/confirmations_test.exs"
}