{
  "filename": "confirmation.py",
  "rule": "- name: outbox-no-early-commit\n  where: \"confirmation.py\"\n  ensure: >-\n    The order update and outbox insertion stay in the same transaction. The function must not explicitly commit inside the session.begin block before it finishes successfully.",
  "corrected": "import json\nfrom sqlalchemy import update\nfrom models import Order, OutboxEvent\n\nasync def confirm_order(session, order_id, tenant_id):\n    async with session.begin():\n        result = await session.execute(\n            update(Order)\n            .where(Order.id == order_id, Order.tenant_id == tenant_id,\n                   Order.status == \"pending\")\n            .values(status=\"confirmed\")\n            .returning(Order.id)\n        )\n        if result.scalar_one_or_none() is None:\n            raise ValueError(\"No pending order in tenant\")\n        session.add(OutboxEvent(\n            event_key=f\"order-confirmed:{order_id}\",\n            payload=json.dumps({\"order_id\": order_id, \"tenant_id\": tenant_id}),\n        ))\n        await session.flush()\n    return order_id\n",
  "broken": "import json\nfrom sqlalchemy import update\nfrom models import Order, OutboxEvent\n\nasync def confirm_order(session, order_id, tenant_id):\n    async with session.begin():\n        result = await session.execute(\n            update(Order)\n            .where(Order.id == order_id, Order.tenant_id == tenant_id,\n                   Order.status == \"pending\")\n            .values(status=\"confirmed\")\n            .returning(Order.id)\n        )\n        if result.scalar_one_or_none() is None:\n            raise ValueError(\"No pending order in tenant\")\n        session.add(OutboxEvent(\n            event_key=f\"order-confirmed:{order_id}\",\n            payload=json.dumps({\"order_id\": order_id, \"tenant_id\": tenant_id}),\n        ))\n        await session.flush()\n        await session.commit()\n        raise ValueError(\"abort after early commit\")\n    return order_id\n",
  "regression": [
    "        await session.flush()\n    return order_id",
    "        await session.flush()\n        await session.commit()\n        raise ValueError(\"abort after early commit\")\n    return order_id"
  ],
  "test_scenarios": [
    [
      "Normal confirmation",
      "Order and outbox row both commit"
    ],
    [
      "Failure before transaction exit",
      "Neither change commits"
    ],
    [
      "Duplicate event_key",
      "Transaction rolls back"
    ],
    [
      "Worker repeats event",
      "Consumer treats the event key as already processed"
    ]
  ],
  "test_source": null,
  "test_command": "python -m pytest tests/test_outbox_transaction.py"
}