[
  {
    "task_id": "bugfix-median",
    "agent": "golden",
    "score": 1.0,
    "passed": true,
    "steps": 1,
    "duration": 0.43615158400000004,
    "error": null,
    "grade": {
      "task_id": "bugfix-median",
      "score": 1.0,
      "passed": true,
      "components": [
        {
          "name": "constraints",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "changed_files": [
              "stats.py"
            ],
            "violations": [],
            "rules_checked": 3
          },
          "messages": []
        },
        {
          "name": "tests",
          "score": 1.0,
          "passed": true,
          "role": "objective",
          "weight": 1.0,
          "details": {
            "fail_to_pass": {
              "tests/test_stats.py::test_median_even": "passed",
              "tests/test_hidden_median.py::test_median_even_unsorted_negative": "passed",
              "tests/test_hidden_median.py::test_median_two_values": "passed"
            },
            "pass_to_pass": {
              "tests/test_stats.py::test_mean": "passed",
              "tests/test_stats.py::test_mean_empty": "passed",
              "tests/test_stats.py::test_median_odd": "passed",
              "tests/test_stats.py::test_variance": "passed",
              "tests/test_hidden_median.py::test_median_does_not_mutate_input": "passed"
            },
            "fail_to_pass_passing": 3,
            "fail_to_pass_total": 3,
            "pass_to_pass_broken": [],
            "exit_code": 0,
            "duration": 0.169,
            "num_cases": 8,
            "stdout_tail": "........                                                                 [100%]\n- generated xml file: /var/folders/vw/nb62dcpx661fyvd3p005p5jw0000gn/T/repogym-bugfix-median-9wdpi9da.scratch/junit-8688358b.xml -\n8 passed in 0.01s\n",
            "stderr_tail": ""
          },
          "messages": []
        }
      ],
      "patch": "diff --git a/stats.py b/stats.py\nindex 4bcbfa2..79ca32a 100644\n--- a/stats.py\n+++ b/stats.py\n@@ -23,7 +23,7 @@ def median(values: Sequence[float]) -> float:\n     mid = n // 2\n     if n % 2 == 1:\n         return ordered[mid]\n-    return ordered[mid]\n+    return (ordered[mid - 1] + ordered[mid]) / 2\n \n \n def variance(values: Sequence[float]) -> float:\n",
      "files_changed": [
        "stats.py"
      ],
      "graded_at": "2026-09-22T12:07:25.346854Z"
    },
    "trajectory_path": null
  },
  {
    "task_id": "bugfix-node-slugify",
    "agent": "golden",
    "score": 1.0,
    "passed": true,
    "steps": 1,
    "duration": 0.37188891599999996,
    "error": null,
    "grade": {
      "task_id": "bugfix-node-slugify",
      "score": 1.0,
      "passed": true,
      "components": [
        {
          "name": "constraints",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "changed_files": [
              "slugify.js"
            ],
            "violations": [],
            "rules_checked": 2
          },
          "messages": []
        },
        {
          "name": "tests",
          "score": 1.0,
          "passed": true,
          "role": "objective",
          "weight": 1.0,
          "details": {
            "fail_to_pass": {
              "strips trailing separators": "passed",
              "custom separator strips trailing too": "passed"
            },
            "pass_to_pass": {
              "lowercases and joins words": "passed",
              "collapses runs of punctuation": "passed",
              "transliterates accents": "passed",
              "strips leading separators": "passed",
              "custom separator": "passed",
              "maxLength truncates on separator boundary": "passed",
              "rejects non strings": "passed"
            },
            "fail_to_pass_passing": 2,
            "fail_to_pass_total": 2,
            "pass_to_pass_broken": [],
            "exit_code": 0,
            "duration": 0.083,
            "num_cases": 9,
            "stdout_tail": "",
            "stderr_tail": ""
          },
          "messages": []
        }
      ],
      "patch": "diff --git a/slugify.js b/slugify.js\nindex 34f06e3..e30a4f7 100644\n--- a/slugify.js\n+++ b/slugify.js\n@@ -21,7 +21,8 @@ function slugify(text, options = {}) {\n     .replace(/[\u0300-\u036f]/g, '')\n     .toLowerCase()\n     .replace(/[^a-z0-9]+/g, separator);\n-  slug = slug.replace(new RegExp(`^${escapeRegExp(separator)}`), '');\n+  const sep = escapeRegExp(separator);\n+  slug = slug.replace(new RegExp(`^(?:${sep})+|(?:${sep})+$`, 'g'), '');\n   if (maxLength && slug.length > maxLength) {\n     slug = slug.slice(0, maxLength);\n     const idx = slug.lastIndexOf(separator);\n",
      "files_changed": [
        "slugify.js"
      ],
      "graded_at": "2026-09-22T12:07:25.721139Z"
    },
    "trajectory_path": null
  },
  {
    "task_id": "feature-ratelimiter",
    "agent": "golden",
    "score": 1.0,
    "passed": true,
    "steps": 1,
    "duration": 0.43347466700000004,
    "error": null,
    "grade": {
      "task_id": "feature-ratelimiter",
      "score": 1.0,
      "passed": true,
      "components": [
        {
          "name": "constraints",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "changed_files": [
              "ratelimit.py"
            ],
            "violations": [],
            "rules_checked": 2
          },
          "messages": []
        },
        {
          "name": "tests",
          "score": 1.0,
          "passed": true,
          "role": "objective",
          "weight": 1.0,
          "details": {
            "fail_to_pass": {
              "tests/test_token_bucket.py::test_bucket_starts_full": "passed",
              "tests/test_token_bucket.py::test_bucket_refills_over_time": "passed",
              "tests/test_token_bucket.py::test_bucket_never_exceeds_capacity": "passed",
              "tests/test_token_bucket.py::test_acquire_raises_with_retry_after": "passed",
              "tests/test_token_bucket.py::test_invalid_arguments": "passed",
              "tests/test_token_bucket_hidden.py::test_keys_are_independent": "passed",
              "tests/test_token_bucket_hidden.py::test_fractional_tokens_accumulate": "passed",
              "tests/test_token_bucket_hidden.py::test_acquire_multiple_tokens": "passed",
              "tests/test_token_bucket_hidden.py::test_cannot_request_more_than_capacity": "passed"
            },
            "pass_to_pass": {
              "tests/test_sliding_window.py::test_allows_up_to_limit": "passed",
              "tests/test_sliding_window.py::test_window_slides": "passed",
              "tests/test_sliding_window.py::test_check_raises_with_retry_after": "passed"
            },
            "fail_to_pass_passing": 9,
            "fail_to_pass_total": 9,
            "pass_to_pass_broken": [],
            "exit_code": 0,
            "duration": 0.149,
            "num_cases": 12,
            "stdout_tail": "............                                                             [100%]\n- generated xml file: /var/folders/vw/nb62dcpx661fyvd3p005p5jw0000gn/T/repogym-feature-ratelimiter-vlu_eeu4.scratch/junit-24990631.xml -\n12 passed in 0.02s\n",
            "stderr_tail": ""
          },
          "messages": []
        },
        {
          "name": "quality",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "rules": {
              "max_cyclomatic_complexity": 0
            },
            "function_metrics": {
              "ratelimit.py::RateLimitExceeded.__init__": {
                "length": 4,
                "complexity": 1
              },
              "ratelimit.py::SlidingWindowLimiter.__init__": {
                "length": 9,
                "complexity": 4
              },
              "ratelimit.py::SlidingWindowLimiter._prune": {
                "length": 6,
                "complexity": 3
              },
              "ratelimit.py::SlidingWindowLimiter.allow": {
                "length": 8,
                "complexity": 2
              },
              "ratelimit.py::SlidingWindowLimiter.check": {
                "length": 7,
                "complexity": 2
              },
              "ratelimit.py::SlidingWindowLimiter.remaining": {
                "length": 3,
                "complexity": 1
              },
              "ratelimit.py::TokenBucket.__init__": {
                "length": 11,
                "complexity": 4
              },
              "ratelimit.py::TokenBucket._refill": {
                "length": 6,
                "complexity": 1
              },
              "ratelimit.py::TokenBucket.acquire": {
                "length": 11,
                "complexity": 4
              },
              "ratelimit.py::TokenBucket.tokens": {
                "length": 3,
                "complexity": 1
              },
              "ratelimit.py::TokenBucket.try_acquire": {
                "length": 11,
                "complexity": 4
              },
              "tests/test_sliding_window.py::FakeClock.__call__": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_sliding_window.py::FakeClock.__init__": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_sliding_window.py::FakeClock.advance": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_sliding_window.py::test_allows_up_to_limit": {
                "length": 6,
                "complexity": 4
              },
              "tests/test_sliding_window.py::test_check_raises_with_retry_after": {
                "length": 8,
                "complexity": 2
              },
              "tests/test_sliding_window.py::test_window_slides": {
                "length": 6,
                "complexity": 3
              },
              "tests/test_token_bucket.py::FakeClock.__call__": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_token_bucket.py::FakeClock.__init__": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_token_bucket.py::FakeClock.advance": {
                "length": 2,
                "complexity": 1
              },
              "tests/test_token_bucket.py::test_acquire_raises_with_retry_after": {
                "length": 7,
                "complexity": 2
              },
              "tests/test_token_bucket.py::test_bucket_never_exceeds_capacity": {
                "length": 5,
                "complexity": 2
              },
              "tests/test_token_bucket.py::test_bucket_refills_over_time": {
                "length": 9,
                "complexity": 6
              },
              "tests/test_token_bucket.py::test_bucket_starts_full": {
                "length": 7,
                "complexity": 5
              },
              "tests/test_token_bucket.py::test_invalid_arguments": {
                "length": 5,
                "complexity": 1
              }
            }
          },
          "messages": []
        }
      ],
      "patch": "diff --git a/ratelimit.py b/ratelimit.py\nindex db31397..2b38883 100644\n--- a/ratelimit.py\n+++ b/ratelimit.py\n@@ -56,3 +56,65 @@ class SlidingWindowLimiter:\n         if len(events) >= self.limit:\n             raise RateLimitExceeded(key, events[0] + self.window - now)\n         events.append(now)\n+\n+\n+class TokenBucket:\n+    \"\"\"Classic token bucket: ``capacity`` tokens, refilled at ``refill_rate`` per second.\n+\n+    Each key has its own bucket that starts full.  Tokens accumulate continuously\n+    (fractional tokens are tracked) but never exceed ``capacity``.\n+    \"\"\"\n+\n+    def __init__(\n+        self, capacity: float, refill_rate: float, clock: Optional[Callable[[], float]] = None\n+    ):\n+        if capacity <= 0:\n+            raise ValueError(\"capacity must be positive\")\n+        if refill_rate <= 0:\n+            raise ValueError(\"refill_rate must be positive\")\n+        self.capacity = float(capacity)\n+        self.refill_rate = float(refill_rate)\n+        self.clock = clock or time.monotonic\n+        self._state: Dict[str, \"tuple[float, float]\"] = {}\n+\n+    def _refill(self, key: str) -> float:\n+        now = self.clock()\n+        tokens, last = self._state.get(key, (self.capacity, now))\n+        tokens = min(self.capacity, tokens + (now - last) * self.refill_rate)\n+        self._state[key] = (tokens, now)\n+        return tokens\n+\n+    def tokens(self, key: str) -> float:\n+        \"\"\"Current token balance for ``key`` (after refilling).\"\"\"\n+        return self._refill(key)\n+\n+    def try_acquire(self, key: str, tokens: float = 1.0) -> bool:\n+        \"\"\"Consume ``tokens`` if available and return whether it succeeded.\"\"\"\n+        if tokens <= 0:\n+            raise ValueError(\"tokens must be positive\")\n+        if tokens > self.capacity:\n+            raise ValueError(\"cannot request more tokens than the bucket capacity\")\n+        available = self._refill(key)\n+        if available < tokens:\n+            return False\n+        self._state[key] = (available - tokens, self._state[key][1])\n+        return True\n+\n+    def acquire(self, key: str, tokens: float = 1.0) -> None:\n+        \"\"\"Like :meth:`try_acquire` but raises :class:`RateLimitExceeded` with ``retry_after``.\"\"\"\n+        if tokens <= 0:\n+            raise ValueError(\"tokens must be positive\")\n+        if tokens > self.capacity:\n+            raise ValueError(\"cannot request more tokens than the bucket capacity\")\n+        available = self._refill(key)\n+        if available < tokens:\n+            retry_after = (tokens - available) / self.refill_rate\n+            raise RateLimitExceeded(key, retry_after)\n+        self._state[key] = (available - tokens, self._state[key][1])\n+\n+\n+__all__ = [\n+    \"RateLimitExceeded\",\n+    \"SlidingWindowLimiter\",\n+    \"TokenBucket\",\n+]\n",
      "files_changed": [
        "ratelimit.py"
      ],
      "graded_at": "2026-09-22T12:07:26.154945Z"
    },
    "trajectory_path": null
  },
  {
    "task_id": "perf-dedupe-records",
    "agent": "golden",
    "score": 1.0,
    "passed": true,
    "steps": 1,
    "duration": 9.486517583,
    "error": null,
    "grade": {
      "task_id": "perf-dedupe-records",
      "score": 1.0,
      "passed": true,
      "components": [
        {
          "name": "constraints",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "changed_files": [
              "dedupe.py"
            ],
            "violations": [],
            "rules_checked": 2
          },
          "messages": []
        },
        {
          "name": "tests",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "fail_to_pass": {},
            "pass_to_pass": {
              "tests/test_dedupe.py::test_normalize_email": "passed",
              "tests/test_dedupe.py::test_dedupe_keeps_first_and_order": "passed",
              "tests/test_dedupe.py::test_dedupe_empty": "passed",
              "tests/test_dedupe.py::test_merge_names_distinct_in_order": "passed",
              "tests/test_dedupe.py::test_dedupe_does_not_mutate_input": "passed"
            },
            "fail_to_pass_passing": 0,
            "fail_to_pass_total": 0,
            "pass_to_pass_broken": [],
            "exit_code": 0,
            "duration": 0.136,
            "num_cases": 5,
            "stdout_tail": ".....                                                                    [100%]\n- generated xml file: /var/folders/vw/nb62dcpx661fyvd3p005p5jw0000gn/T/repogym-perf-dedupe-records-o2830gkh.scratch/junit-23846564.xml -\n5 passed in 0.01s\n",
            "stderr_tail": ""
          },
          "messages": []
        },
        {
          "name": "perf",
          "score": 1.0,
          "passed": true,
          "role": "objective",
          "weight": 1.0,
          "details": {
            "baseline_seconds": 2.889083,
            "candidate_seconds": 0.047672,
            "speedup": 60.603,
            "min_speedup": 3.0,
            "noise_floor": 1.15,
            "timing": "wall",
            "baseline_timings": [
              3.018521,
              2.889083,
              2.924143
            ],
            "timings": [
              0.047672,
              0.048611,
              0.049925
            ]
          },
          "messages": []
        }
      ],
      "patch": "diff --git a/dedupe.py b/dedupe.py\nindex 83ce1d4..2d371be 100644\n--- a/dedupe.py\n+++ b/dedupe.py\n@@ -2,7 +2,7 @@\n \n from __future__ import annotations\n \n-from typing import Dict, Iterable, List\n+from typing import Dict, Iterable, List, Set\n \n \n def normalize_email(email: str) -> str:\n@@ -19,14 +19,11 @@ def dedupe_records(records: Iterable[Dict[str, str]]) -> List[Dict[str, str]]:\n     The *first* occurrence of an email wins and the original ordering is kept.\n     \"\"\"\n     unique: List[Dict[str, str]] = []\n+    seen: Set[str] = set()\n     for record in records:\n         email = normalize_email(record[\"email\"])\n-        duplicate = False\n-        for existing in unique:\n-            if normalize_email(existing[\"email\"]) == email:\n-                duplicate = True\n-                break\n-        if not duplicate:\n+        if email not in seen:\n+            seen.add(email)\n             unique.append(record)\n     return unique\n \n@@ -34,11 +31,12 @@ def dedupe_records(records: Iterable[Dict[str, str]]) -> List[Dict[str, str]]:\n def merge_names(records: Iterable[Dict[str, str]]) -> Dict[str, List[str]]:\n     \"\"\"Map each normalised email to the distinct normalised names seen for it.\"\"\"\n     result: Dict[str, List[str]] = {}\n+    seen: Dict[str, Set[str]] = {}\n     for record in records:\n         email = normalize_email(record[\"email\"])\n         name = normalize_name(record[\"name\"])\n-        names = result.get(email, [])\n-        if name not in names:\n-            names = names + [name]\n-        result[email] = names\n+        names_seen = seen.setdefault(email, set())\n+        if name not in names_seen:\n+            names_seen.add(name)\n+            result.setdefault(email, []).append(name)\n     return result\n",
      "files_changed": [
        "dedupe.py"
      ],
      "graded_at": "2026-09-22T12:07:35.641262Z"
    },
    "trajectory_path": null
  },
  {
    "task_id": "refactor-order-pipeline",
    "agent": "golden",
    "score": 1.0,
    "passed": true,
    "steps": 1,
    "duration": 0.42591954199999904,
    "error": null,
    "grade": {
      "task_id": "refactor-order-pipeline",
      "score": 1.0,
      "passed": true,
      "components": [
        {
          "name": "constraints",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "changed_files": [
              "orders.py"
            ],
            "violations": [],
            "rules_checked": 3
          },
          "messages": []
        },
        {
          "name": "tests",
          "score": 1.0,
          "passed": true,
          "role": "gate",
          "weight": 1.0,
          "details": {
            "fail_to_pass": {},
            "pass_to_pass": {
              "tests/test_orders.py::test_basic_us_order": "passed",
              "tests/test_orders.py::test_free_shipping_over_threshold": "passed",
              "tests/test_orders.py::test_save10_coupon": "passed",
              "tests/test_orders.py::test_save20_below_50_gives_10_percent": "passed",
              "tests/test_orders.py::test_save20_above_50": "passed",
              "tests/test_orders.py::test_flat5_caps_at_subtotal": "passed",
              "tests/test_orders.py::test_bogo": "passed",
              "tests/test_orders.py::test_unknown_coupon": "passed",
              "tests/test_orders.py::test_loyalty_gold_stacks_after_coupon": "passed",
              "tests/test_orders.py::test_platinum_free_shipping": "passed",
              "tests/test_orders.py::test_heavy_item_surcharge": "passed",
              "tests/test_orders.py::test_grocery_not_taxed": "passed",
              "tests/test_orders.py::test_unknown_country_no_tax_high_shipping": "passed",
              "tests/test_orders.py::test_gift_fee": "passed",
              "tests/test_orders.py::test_validation": "passed",
              "tests/test_orders.py::test_discount_never_exceeds_subtotal": "passed"
            },
            "fail_to_pass_passing": 0,
            "fail_to_pass_total": 0,
            "pass_to_pass_broken": [],
            "exit_code": 0,
            "duration": 0.151,
            "num_cases": 16,
            "stdout_tail": "................                                                         [100%]\n- generated xml file: /var/folders/vw/nb62dcpx661fyvd3p005p5jw0000gn/T/repogym-refactor-order-pipeline-n91_m7rm.scratch/junit-c6bc3046.xml -\n16 passed in 0.02s\n",
            "stderr_tail": ""
          },
          "messages": []
        },
        {
          "name": "quality",
          "score": 1.0,
          "passed": true,
          "role": "objective",
          "weight": 1.0,
          "details": {
            "rules": {
              "max_function_length": 0,
              "max_cyclomatic_complexity": 0
            },
            "function_metrics": {
              "orders.py::LineItem.line_total": {
                "length": 2,
                "complexity": 1
              },
              "orders.py::_coupon_bogo": {
                "length": 7,
                "complexity": 5
              },
              "orders.py::_coupon_discount": {
                "length": 9,
                "complexity": 4
              },
              "orders.py::_coupon_flat5": {
                "length": 2,
                "complexity": 1
              },
              "orders.py::_coupon_save20": {
                "length": 2,
                "complexity": 2
              },
              "orders.py::_discount": {
                "length": 4,
                "complexity": 1
              },
              "orders.py::_shipping": {
                "length": 7,
                "complexity": 5
              },
              "orders.py::_subtotal": {
                "length": 2,
                "complexity": 2
              },
              "orders.py::_tax": {
                "length": 7,
                "complexity": 5
              },
              "orders.py::_validate": {
                "length": 8,
                "complexity": 5
              },
              "orders.py::process_order": {
                "length": 18,
                "complexity": 2
              }
            }
          },
          "messages": []
        }
      ],
      "patch": "diff --git a/orders.py b/orders.py\nindex d79f90e..681f88e 100644\n--- a/orders.py\n+++ b/orders.py\n@@ -3,7 +3,7 @@\n from __future__ import annotations\n \n from dataclasses import dataclass, field\n-from typing import Dict, List, Optional\n+from typing import Callable, Dict, List, Optional\n \n \n @dataclass\n@@ -13,6 +13,10 @@ class LineItem:\n     unit_price: float\n     category: str = \"general\"\n \n+    @property\n+    def line_total(self) -> float:\n+        return self.quantity * self.unit_price\n+\n \n @dataclass\n class Order:\n@@ -26,79 +30,99 @@ class Order:\n \n TAX_RATES = {\"US\": 0.07, \"CA\": 0.13, \"GB\": 0.20, \"DE\": 0.19}\n FREE_SHIPPING_THRESHOLD = 100.0\n+BASE_SHIPPING = {\"US\": 5.99, \"CA\": 9.99, \"GB\": 14.99, \"DE\": 14.99}\n+DEFAULT_SHIPPING = 24.99\n+HEAVY_SURCHARGE = 20.0\n+HEAVY_CATEGORIES = {\"furniture\", \"appliance\"}\n+TAX_EXEMPT_CATEGORIES = {\"grocery\", \"book\"}\n+LOYALTY_RATES = {\"silver\": 0.02, \"gold\": 0.05, \"platinum\": 0.10}\n+GIFT_FEE = 2.0\n \n \n-def process_order(order: Order) -> Dict[str, float]:\n-    \"\"\"Compute subtotal, discounts, shipping, tax and total for an order.\"\"\"\n+def _validate(order: Order) -> None:\n     if not order.items:\n         raise ValueError(\"order has no items\")\n-    subtotal = 0.0\n     for item in order.items:\n         if item.quantity <= 0:\n             raise ValueError(f\"invalid quantity for {item.sku}\")\n         if item.unit_price < 0:\n             raise ValueError(f\"invalid price for {item.sku}\")\n-        subtotal = subtotal + item.quantity * item.unit_price\n-    discount = 0.0\n-    if order.coupon is not None:\n-        if order.coupon == \"SAVE10\":\n-            discount = discount + subtotal * 0.10\n-        elif order.coupon == \"SAVE20\":\n-            if subtotal >= 50:\n-                discount = discount + subtotal * 0.20\n-            else:\n-                discount = discount + subtotal * 0.10\n-        elif order.coupon == \"FLAT5\":\n-            if subtotal > 5:\n-                discount = discount + 5.0\n-            else:\n-                discount = discount + subtotal\n-        elif order.coupon.startswith(\"BOGO-\"):\n-            sku = order.coupon[5:]\n-            for item in order.items:\n-                if item.sku == sku and item.quantity >= 2:\n-                    discount = discount + (item.quantity // 2) * item.unit_price\n-        else:\n-            raise ValueError(\"unknown coupon\")\n-    if order.loyalty_tier == \"gold\":\n-        discount = discount + (subtotal - discount) * 0.05\n-    elif order.loyalty_tier == \"platinum\":\n-        discount = discount + (subtotal - discount) * 0.10\n-    elif order.loyalty_tier == \"silver\":\n-        discount = discount + (subtotal - discount) * 0.02\n-    if discount > subtotal:\n-        discount = subtotal\n+\n+\n+def _subtotal(order: Order) -> float:\n+    return sum(item.line_total for item in order.items)\n+\n+\n+def _coupon_save20(order: Order, subtotal: float) -> float:\n+    return subtotal * (0.20 if subtotal >= 50 else 0.10)\n+\n+\n+def _coupon_flat5(order: Order, subtotal: float) -> float:\n+    return min(5.0, subtotal)\n+\n+\n+def _coupon_bogo(order: Order, subtotal: float) -> float:\n+    sku = (order.coupon or \"\")[5:]\n+    return sum(\n+        (item.quantity // 2) * item.unit_price\n+        for item in order.items\n+        if item.sku == sku and item.quantity >= 2\n+    )\n+\n+\n+COUPONS: Dict[str, Callable[[Order, float], float]] = {\n+    \"SAVE10\": lambda order, subtotal: subtotal * 0.10,\n+    \"SAVE20\": _coupon_save20,\n+    \"FLAT5\": _coupon_flat5,\n+}\n+\n+\n+def _coupon_discount(order: Order, subtotal: float) -> float:\n+    if order.coupon is None:\n+        return 0.0\n+    if order.coupon.startswith(\"BOGO-\"):\n+        return _coupon_bogo(order, subtotal)\n+    handler = COUPONS.get(order.coupon)\n+    if handler is None:\n+        raise ValueError(\"unknown coupon\")\n+    return handler(order, subtotal)\n+\n+\n+def _discount(order: Order, subtotal: float) -> float:\n+    discount = _coupon_discount(order, subtotal)\n+    discount += (subtotal - discount) * LOYALTY_RATES.get(order.loyalty_tier, 0.0)\n+    return min(discount, subtotal)\n+\n+\n+def _shipping(order: Order, discounted: float) -> float:\n+    if order.loyalty_tier == \"platinum\" or discounted >= FREE_SHIPPING_THRESHOLD:\n+        return 0.0\n+    shipping = BASE_SHIPPING.get(order.country, DEFAULT_SHIPPING)\n+    if any(item.category in HEAVY_CATEGORIES for item in order.items):\n+        shipping += HEAVY_SURCHARGE\n+    return shipping\n+\n+\n+def _tax(order: Order, subtotal: float, discounted: float) -> float:\n+    rate = TAX_RATES.get(order.country)\n+    if rate is None:\n+        return 0.0\n+    ratio = discounted / subtotal if subtotal else 0.0\n+    exempt = sum(item.line_total * ratio for item in order.items if item.category in TAX_EXEMPT_CATEGORIES)\n+    return max(0.0, discounted - exempt) * rate\n+\n+\n+def process_order(order: Order) -> Dict[str, float]:\n+    \"\"\"Compute subtotal, discounts, shipping, tax and total for an order.\"\"\"\n+    _validate(order)\n+    subtotal = _subtotal(order)\n+    discount = _discount(order, subtotal)\n     discounted = subtotal - discount\n-    shipping = 0.0\n-    if discounted < FREE_SHIPPING_THRESHOLD:\n-        if order.country == \"US\":\n-            shipping = 5.99\n-        elif order.country == \"CA\":\n-            shipping = 9.99\n-        elif order.country == \"GB\" or order.country == \"DE\":\n-            shipping = 14.99\n-        else:\n-            shipping = 24.99\n-        heavy = False\n-        for item in order.items:\n-            if item.category == \"furniture\" or item.category == \"appliance\":\n-                heavy = True\n-        if heavy:\n-            shipping = shipping + 20.0\n-    if order.loyalty_tier == \"platinum\":\n-        shipping = 0.0\n-    tax = 0.0\n-    if order.country in TAX_RATES:\n-        taxable = discounted\n-        for item in order.items:\n-            if item.category == \"grocery\" or item.category == \"book\":\n-                taxable = taxable - item.quantity * item.unit_price * (discounted / subtotal if subtotal else 0)\n-        if taxable < 0:\n-            taxable = 0.0\n-        tax = taxable * TAX_RATES[order.country]\n+    shipping = _shipping(order, discounted)\n+    tax = _tax(order, subtotal, discounted)\n     total = discounted + shipping + tax\n-    if \"gift\" in order.notes and order.notes[\"gift\"] == \"yes\":\n-        total = total + 2.0\n+    if order.notes.get(\"gift\") == \"yes\":\n+        total += GIFT_FEE\n     return {\n         \"subtotal\": round(subtotal, 2),\n         \"discount\": round(discount, 2),\n",
      "files_changed": [
        "orders.py"
      ],
      "graded_at": "2026-09-22T12:07:36.068860Z"
    },
    "trajectory_path": null
  }
]