{
  "filename": "orders.py",
  "rule": "- name: tenant-scoped-update\n  where: \"orders.py\"\n  ensure: >-\n    The cancellation UPDATE must constrain tenant_id to actor.tenant_id before changing the order. An id-only or owner-only update breaks this requirement.",
  "corrected": "from fastapi import HTTPException\nfrom sqlalchemy import update\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom models import Order\n\nasync def cancel_order(session: AsyncSession, actor, order_id: int):\n    async with session.begin():\n        statement = (\n            update(Order)\n            .where(Order.id == order_id,\n                   Order.tenant_id == actor.tenant_id,\n                   Order.owner_id == actor.id,\n                   Order.status == \"pending\")\n            .values(status=\"cancelled\")\n            .returning(Order.id)\n        )\n        result = await session.execute(statement)\n        cancelled_id = result.scalar_one_or_none()\n        if cancelled_id is None:\n            raise HTTPException(status_code=404, detail=\"Pending order not found\")\n    return {\"id\": cancelled_id, \"status\": \"cancelled\"}\n",
  "broken": "from fastapi import HTTPException\nfrom sqlalchemy import update\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom models import Order\n\nasync def cancel_order(session: AsyncSession, actor, order_id: int):\n    async with session.begin():\n        statement = (\n            update(Order)\n            .where(Order.id == order_id,\n                   Order.owner_id == actor.id,\n                   Order.status == \"pending\")\n            .values(status=\"cancelled\")\n            .returning(Order.id)\n        )\n        result = await session.execute(statement)\n        cancelled_id = result.scalar_one_or_none()\n        if cancelled_id is None:\n            raise HTTPException(status_code=404, detail=\"Pending order not found\")\n    return {\"id\": cancelled_id, \"status\": \"cancelled\"}\n",
  "regression": [
    "                   Order.tenant_id == actor.tenant_id,\n",
    ""
  ],
  "test_scenarios": [
    [
      "Matching tenant, owner, pending row",
      "One cancellation and commit"
    ],
    [
      "Another tenant with the same owner id",
      "404 and no update"
    ],
    [
      "Already cancelled or missing row",
      "404 and no update"
    ],
    [
      "Two simultaneous cancellation requests",
      "At most one changes the pending row"
    ]
  ],
  "test_source": null,
  "test_command": "python -m pytest tests/test_order_cancellation.py"
}