{
  "pr": 917,
  "title": "Duplicate prevention failed for simultaneous requests",
  "run": {
    "pr": 917,
    "title": "Spec: mcp-server-core",
    "run_id": "mcp-server-core-20260623T174318286059",
    "mode": "spec",
    "created": "2026-06-23T17:43:18.340714+00:00",
    "merged": "2026-06-23T19:55:11+00:00",
    "agent": "codex",
    "chain_runs": 1,
    "has_implement_event": true,
    "elapsed_hours": 2.197960912777778,
    "implementation_to_merge_hours": 2.192662828611111,
    "recorded_phase_hours": 2.1979127069444444,
    "churn": 2052,
    "files": 15,
    "direct_signals": 0,
    "broad_signals": 0,
    "review_rejected": true,
    "review_rejections": 4,
    "gates_rejected": false
  },
  "merge_sha": "06b8f934bb0b12f9fc8551c794ebe291a1845033",
  "selected_review": {
    "attempt_number": 1,
    "findings": [
      {
        "body": "The idempotency lookup is performed and closed before the REST enqueue, and the idempotency row is only inserted after `/api/runs` or `/api/runs/sweep` has already accepted a run. Two concurrent requests with the same `(service_key_id, tool_name, idempotency_key)` can both miss the lookup, both enqueue, and only then race on `create_mcp_idempotency_record`. That violates the spec requirement that same-payload idempotent repeats do not enqueue a duplicate run. Reserve or lock the idempotency tuple before calling the REST enqueue, or otherwise make the lookup/enqueue/record sequence atomic.",
        "confidence": 0.93,
        "end_line": 283,
        "file": "api/mcp/router.py",
        "id": "F1",
        "severity": "P1",
        "start_line": 245,
        "title": "Concurrent idempotent enqueues can create duplicate runs"
      }
    ],
    "reviewed_at": "2026-06-23T11:21:00Z",
    "reviewed_base_sha": "6f4e96b72108ed11ec0980227c71a2e2a4989e7f",
    "reviewed_head_sha": "b992090dcc2ab5d4931c0eb4cc47c9a74e2441d2",
    "reviewer_agent": "codex",
    "reviewer_role": "independent_reviewer",
    "source_check_name": "review-decision-gate",
    "source_check_url": "private repository PR #917",
    "status": "request_changes",
    "summary": "Found blocking idempotency correctness issues. The MCP surface is broadly present, but the enqueue idempotency contract is not safe under concurrency or TTL expiry, and a telemetry acceptance criterion is also not implemented."
  },
  "final_approval": {
    "attempt_number": 5,
    "findings": [],
    "reviewed_at": "2026-06-23T19:43:00Z",
    "reviewed_base_sha": "6f4e96b72108ed11ec0980227c71a2e2a4989e7f",
    "reviewed_head_sha": "7b8d9711dbf16fc2928d1da9657987c429985209",
    "reviewer_agent": "codex",
    "reviewer_role": "independent_reviewer",
    "source_check_name": "review-decision-gate",
    "source_check_url": "private repository PR #917",
    "status": "approved",
    "summary": "No blocking correctness, security, acceptance-criteria, or test issues found in the PR diff."
  },
  "merged_excerpts": [
    {
      "file": "api/mcp/router.py",
      "start_line": 240,
      "end_line": 306,
      "language": "python",
      "text": "async def _enqueue_with_idempotency(\n    request: Request,\n    actor: McpActor,\n    *,\n    tool_name: str,\n    rest_path: str,\n    payload: dict[str, Any],\n    idempotency_key: str | None,\n) -> dict[str, Any]:\n    if not idempotency_key:\n        response = await _rest_call(request, actor, \"POST\", rest_path, json_body=payload)\n        _raise_for_rest_error(response)\n        return _enqueue_envelope(response.json(), idempotency_key=None)\n\n    payload_hash = repository.payload_sha256(payload)\n    settings = get_settings()\n    conn = repository.get_connection(str(get_settings().database_url))\n    try:\n        with conn.transaction():\n            await asyncio.to_thread(\n                repository.lock_mcp_idempotency_key,\n                conn,\n                service_key_id=actor.service_key_id,\n                tool_name=tool_name,\n                idempotency_key=idempotency_key,\n            )\n            record = await asyncio.to_thread(\n                repository.get_mcp_idempotency_record,\n                conn,\n                service_key_id=actor.service_key_id,\n                tool_name=tool_name,\n                idempotency_key=idempotency_key,\n                ttl_seconds=settings.mcp_idempotency_ttl_seconds,\n            )\n            if record is not None:\n                if record.payload_sha256 != payload_hash:\n                    raise McpToolError(\n                        \"idempotency_key_payload_mismatch\",\n                        \"The idempotency key was reused with different inputs.\",\n                    )\n                envelope = dict(record.envelope)\n                envelope[\"_mcp_outcome\"] = \"idempotent_replay\"\n                return envelope\n\n            response = await _rest_call(request, actor, \"POST\", rest_path, json_body=payload)\n            _raise_for_rest_error(response)\n            envelope = _enqueue_envelope(response.json(), idempotency_key=idempotency_key)\n            try:\n                await asyncio.to_thread(\n                    repository.create_mcp_idempotency_record,\n                    conn,\n                    service_key_id=actor.service_key_id,\n                    tool_name=tool_name,\n                    idempotency_key=idempotency_key,\n                    payload_sha256=payload_hash,\n                    run_id=envelope[\"run_id\"],\n                    envelope=envelope,\n                    ttl_seconds=settings.mcp_idempotency_ttl_seconds,\n                )\n            except repository.McpIdempotencyConflict as exc:\n                raise McpToolError(\n                    \"idempotency_key_payload_mismatch\",\n                    \"The idempotency key was reused with different inputs.\",\n                ) from exc\n            return envelope\n    finally:\n        conn.close()"
    },
    {
      "file": "tests/test_mcp_core.py",
      "start_line": 517,
      "end_line": 543,
      "language": "python",
      "text": "async def test_concurrent_enqueue_idempotency_does_not_duplicate_rest_enqueue(monkeypatch, mcp_client):\n    client, database_url, _app = mcp_client\n    _owner_user_id, _service_key_id, token = _seed_user_and_service_key(database_url)\n    calls = 0\n\n    async def fake_rest_call(request, actor, method, path, *, json_body=None):\n        nonlocal calls\n        calls += 1\n        await asyncio.sleep(0.05)\n        return httpx.Response(\n            202,\n            json={\"id\": 88, \"status\": \"queued\", \"run_kind\": \"simulation\", \"run_limit\": None},\n        )\n\n    mcp_router = importlib.import_module(\"api.mcp.router\")\n\n    monkeypatch.setattr(mcp_router, \"_rest_call\", fake_rest_call)\n    args = {\"config_name\": \"baseline\", \"idempotency_key\": \"concurrent\"}\n    first, second = await asyncio.gather(\n        _rpc(client, token, \"tools/call\", {\"name\": \"run_simulation\", \"arguments\": args}),\n        _rpc(client, token, \"tools/call\", {\"name\": \"run_simulation\", \"arguments\": args}),\n    )\n    assert first.status_code == 200\n    assert second.status_code == 200\n    assert first.json()[\"result\"][\"structuredContent\"][\"run_id\"] == 88\n    assert second.json()[\"result\"][\"structuredContent\"][\"run_id\"] == 88\n    assert calls == 1"
    }
  ],
  "verification": "Inspected recorded finding, final approval, gate histories and merged implementation/test. Historical tests were not rerun.",
  "gates": {
    "spec_id": "mcp-server-core",
    "gates": {
      "e2e": {
        "attempts": 5,
        "history": [
          {
            "attempt": 1,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:20:21.790686+00:00"
          },
          {
            "attempt": 2,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:40:55.571050+00:00"
          },
          {
            "attempt": 3,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:59:43.760567+00:00"
          },
          {
            "attempt": 4,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T19:20:58.536084+00:00"
          },
          {
            "attempt": 5,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T19:40:49.445235+00:00"
          }
        ],
        "last_command": "make e2e",
        "last_status": "passed"
      },
      "lint": {
        "attempts": 5,
        "history": [
          {
            "attempt": 1,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:18:12.730594+00:00"
          },
          {
            "attempt": 2,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:38:50.658113+00:00"
          },
          {
            "attempt": 3,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T18:57:38.134293+00:00"
          },
          {
            "attempt": 4,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T19:18:50.803313+00:00"
          },
          {
            "attempt": 5,
            "error_count": null,
            "failed_count": null,
            "failed_tests": null,
            "failure_fingerprint": "",
            "passed_count": null,
            "status": "passed",
            "timestamp": "2026-06-23T19:38:42.985216+00:00"
          }
        ],
        "last_command": "make lint",
        "last_status": "passed"
      },
      "test": {
        "attempts": 5,
        "history": [
          {
            "attempt": 1,
            "error_count": 0,
            "failed_count": 0,
            "failed_tests": [],
            "failure_fingerprint": "",
            "passed_count": 3686,
            "status": "passed",
            "timestamp": "2026-06-23T18:18:12.727675+00:00"
          },
          {
            "attempt": 2,
            "error_count": 0,
            "failed_count": 0,
            "failed_tests": [],
            "failure_fingerprint": "",
            "passed_count": 3689,
            "status": "passed",
            "timestamp": "2026-06-23T18:38:50.655384+00:00"
          },
          {
            "attempt": 3,
            "error_count": 0,
            "failed_count": 0,
            "failed_tests": [],
            "failure_fingerprint": "",
            "passed_count": 3691,
            "status": "passed",
            "timestamp": "2026-06-23T18:57:38.131449+00:00"
          },
          {
            "attempt": 4,
            "error_count": 0,
            "failed_count": 0,
            "failed_tests": [],
            "failure_fingerprint": "",
            "passed_count": 3693,
            "status": "passed",
            "timestamp": "2026-06-23T19:18:50.800519+00:00"
          },
          {
            "attempt": 5,
            "error_count": 0,
            "failed_count": 0,
            "failed_tests": [],
            "failure_fingerprint": "",
            "passed_count": 3696,
            "status": "passed",
            "timestamp": "2026-06-23T19:38:42.982271+00:00"
          }
        ],
        "last_command": "make test",
        "last_status": "passed"
      }
    }
  }
}
