CRUDBench LocalMaxxing v2
Official question setTerminal-Bench 2.1 style CRUD API repair tasks, published through the Pro-gated terminal workflow.
Category: Software engineeringType: Question setQuestions: 2Question batches: 2Runs: 2
Dry-run first:
lmx eval shard crudbench-localmaxxing-v2 --base-url http://localhost:8000 --questions 2 --dry-runThen submit with a real model and hardware profile:
lmx eval shard crudbench-localmaxxing-v2 --base-url http://localhost:8000 --questions 2 --model <hfId> --hardware hardware.json --submitEach question counts once per model, using its latest answer. The leaderboard is ranked by the lower bound of a 95% confidence interval, so a model needs enough answered questions to rank highly.
Leaderboard
Qwen/Qwen3.8-27B · 2 of 2 questions · 2 runs
100.0%
95% CI 34.2–100.0%
2/2 correct
Stability— historical rerun transparency
Leaderboard rank uses the latest approved answer to each question. These numbers also include earlier submissions, so reruns and changed answers are visible but do not affect rank.
2 historical runs · 2 unique questions
Ranked
100.0%
Row avg
100.0%
Run avg
100.0%
Repeated
0
Changed
0
Runs— sample traces per run
by Lottolabs · shard 2 · 8/28/2026, 1:31:10 PM · cmtczpjii000qlm0156j2fcpo100.0%1/1 correct · 1 correct traces · 0 incorrect traces
by Lottolabs · shard 2 · 8/28/2026, 1:31:10 PM · cmtczpjii000qlm0156j2fcpo
100.0%
Correct samples
sample 1 · product-crud-paginationpass · 100.0% · 65003ms · 4cfcef20a8c7
Question
Finish the product API in `/app/server.py` without adding third-party dependencies.
Required behavior:
- `GET /products?limit=N&offset=N` returns `{items, total, limit, offset}`. Defaults are `limit=20`, `offset=0`; limit must be 1–100 and offset must be non-negative. Invalid values return `400`.
- `PATCH /products/{id}` accepts `name` and/or `price`. Reject an empty update, blank names, and non-positive prices with `400`. Preserve unspecified fields and return `404` for unknown products.
- `DELETE /products/{id}` returns `204` for an existing product and `404` otherwise.
Keep the existing `POST /products` contract and JSON content types working.Rendered prompt
You control a persistent Linux shell session inside a container. State persists across replies: your working directory, environment variables, and background jobs carry over from one command block to the next. Each reply MUST contain exactly one ```bash fenced block containing one or more non-interactive shell commands, which are executed in that same shell; stdout/stderr and exit code are returned. Prefer batching related inspection/edit/test commands instead of spending one model turn per tiny command. When the task is complete, reply with the single token TASK_COMPLETE and no code block. If you need Python/Ruby/Node/etc., run it from bash with a heredoc (for example: python3 <<'PY' ... PY). Avoid dumping huge files; inspect with head/tail/grep/scripts. Bound password crackers and deliberately long-running commands yourself with timeout, but do not prematurely cap package installs, builds, or tests unless they are clearly stuck. Never run foreground servers; start them in the background and verify them.
Agent trace
Turn 1
Assistant
{
"analysis": "The terminal is at a fresh prompt. I need to inspect the existing /app/server.py to understand the current state before making changes.",
"plan": "First, read the contents of /app/server.py to understand the existing code structure, then implement the required endpoints.",
"commands": [
{
"keystrokes": "cat /app/server.py\n",
"duration": 0.5
}
]
}Command
$ cat /app/server.py
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse
products = {}
next_id = 1
class Handler(BaseHTTPRequestHandler):
def log_message(self, *_): pass
def send_json(self, status, value):
body = json.dumps(value).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def read_json(self):
try:
value = json.loads(self.rfile.read(int(self.headers.get("Content-Length", "0"))) or b"{}")
return value if isinstance(value, dict) else None
except Exception: return None
def product_id(self):
path = urlparse(self.path).path.strip("/").split("/")
return int(path[1]) if len(path) == 2 and path[0] == "products" and path[1].isdigit() else None
def do_POST(self):
global next_id
if urlparse(self.path).path != "/products": return self.send_json(404, {"error": "not found"})
value = self.read_json()
if not value or not isinstance(value.get("name"), str) or not value["name"].strip() or not isinstance(value.get("price"), (int, float)) or value["price"] <= 0:
return self.send_json(400, {"error": "valid name and price are required"})
product = {"id": next_id, "name": value["name"], "price": value["price"]}
products[next_id] = product; next_id += 1
self.send_json(201, product)
def do_GET(self):
self.send_json(501, {"error": "not implemented"})
def do_PATCH(self):
self.send_json(501, {"error": "not implemented"})
def do_DELETE(self):
self.send_json(501, {"error": "not implemented"})
ThreadingHTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
[exit=0]Turn 2
Assistant
{
"analysis": "I can see the existing server.py with a working POST /products endpoint and stub implementations for GET, PATCH, and DELETE. I need to implement all three endpoints with the specified validation rules.",
"plan": "I'll write the complete server.py with all required endpoints implemented: GET /products with
…[29880 characters truncated — full trace in blob]…
led = 0
def check(desc, actual, expected):
global passed, failed
if actual == expected:
passed += 1
print(f" PASS: {desc}")
else:
failed += 1
print(f" FAIL: {desc} -> got {actual}, expected {expected}")
print("=== POST ===")
s, b = req("POST", "/products", {"name": "A", "price": 1.0})
check("POST valid", (s, b["id"]), (201, 1))
s, b = req("POST", "/products", {"name": "B", "price": 2.0})
check("POST valid 2", (s, b["id"]), (201, 2))
s, b = req("POST", "/products", {"name": "C", "price": 3.0})
check("POST valid 3", (s, b["id"]), (201, 3))
s, b = req("POST", "/products", {"name": "", "price": 1.0})
check("POST blank name", s, 400)
s, b = req("POST", "/products", {"name": "X", "price": -1})
check("POST neg price", s, 400)
print("\n=== GET ===")
s, b = req("GET", "/products")
check("GET default limit", b["limit"], 20)
check("GET default offset", b["offset"], 0)
check("GET total", b["total"], 3)
check("GET items count", len(b["items"]), 3)
s, b = req("GET", "/products?limit=2&offset=1")
check("GET limit=2 offset=1 items", len(b["items"]), 2)
check("GET limit=2 offset=1 first id", b["items"][0]["id"], 2)
check("GET limit=2 offset=1 total", b["total"], 3)
s, b = req("GET", "/products?limit=1&offset=0")
check("GET limit=1 offset=0", len(b["items"]), 1)
s, b = req("GET", "/products?limit=0")
check("GET limit=0 -> 400", s, 400)
s, b = req("GET", "/products?limit=101")
check("GET limit=101 -> 400", s, 400)
s, b = req("GET", "/products?offset=-1")
check("GET offset=-1 -> 400", s, 400)
s, b = req("GET", "/products?limit=abc")
check("GET limit=abc -> 400", s, 400)
s, b = req("GET", "/products?offset=abc")
check("GET offset=abc -> 400", s, 400)
s, b = req("GET", "/products?limit=100")
check("GET limit=100 -> 200", s, 200)
s, b = req("GET", "/products?limit=1")
check("GET limit=1 -> 200", s, 200)
print("\n=== PATCH ===")
s, b = req("PATCH", "/products/1", {"name": "A2"})
check("PATCH name only", (s, b["name"], b["price"]), (200, "A2", 1.0))
s, b = req("PATCH", "/products/1", {"price": 9.9})
check("PATCH price only", (s, b["name"], b["price"]), (200, "A2", 9.9))
s, b = req("PATCH", "/products/2", {"name": "B2", "price": 8.8})
check("PATCH both", (s, b["name"], b["price"]), (200, "B2", 8.8))
s, b = req("PATCH", "/products/1", {})
check("PATCH empty -> 400", s, 400)
s, b = req("PATCH", "/products/1", {"name": " "})
check("PATCH blank name -> 400", s, 400)
s, b = req("PATCH", "/products/1", {"price": 0})
check("PATCH zero price -> 400", s, 400)
s, b = req("PATCH", "/products/1", {"price": -5})
check("PATCH neg price -> 400", s, 400)
s, b = req("PATCH", "/products/999", {"name": "X"})
check("PATCH unknown -> 404", s, 404)
print("\n=== DELETE ===")
s, b = req("DELETE", "/products/3")
check("DELETE existing -> 204", (s, b), (204, None))
s, b = req("DELETE", "/products/3")
check("DELETE again -> 404", s, 404)
s, b = req("DELETE", "/products/999")
check("DELETE unknown -> 404", s, 404)
print("\n=== FINAL STATE ===")
s, b = req("GET", "/products")
check("Final total", b["total"], 2)
check("Final items", [(i["id"], i["name"], i["price"]) for i in b["items"]], [(1, "A2", 9.9), (2, "B2", 8.8)])
print(f"\n{'='*40}")
print(f"Results: {passed} passed, {failed} failed")
TESTEOF
=== POST ===
PASS: POST valid
PASS: POST valid 2
PASS: POST valid 3
PASS: POST blank name
PASS: POST neg price
=== GET ===
PASS: GET default limit
PASS: GET default offset
PASS: GET total
PASS: GET items count
PASS: GET limit=2 offset=1 items
PASS: GET limit=2 offset=1 first id
PASS: GET limit=2 offset=1 total
PASS: GET limit=1 offset=0
PASS: GET limit=0 -> 400
PASS: GET limit=101 -> 400
PASS: GET offset=-1 -> 400
PASS: GET limit=abc -> 400
PASS: GET offset=abc -> 400
PASS: GET limit=100 -> 200
PASS: GET limit=1 -> 200
=== PATCH ===
PASS: PATCH name only
PASS: PATCH price only
PASS: PATCH both
PASS: PATCH empty -> 400
PASS: PATCH blank name -> 400
PASS: PATCH zero price -> 400
PASS: PATCH neg price -> 400
PASS: PATCH unknown -> 404
=== DELETE ===
PASS: DELETE existing -> 204
PASS: DELETE again -> 404
PASS: DELETE unknown -> 404
=== FINAL STATE ===
PASS: Final total
PASS: Final items
========================================
Results: 33 passed, 0 failed
[exit=0]Turn 7
Assistant
{
"analysis": "All 33 tests passed with 0 failures. Every required behavior is verified:\n- GET /products with default limit=20, offset=0, returns {items, total, limit, offset}\n- GET with limit/offset validation (400 for limit<1, limit>100, offset<0, non-numeric values)\n- PATCH /products/{id} with name-only, price-only, both, empty body (400), blank name (400), non-positive price (400), unknown id (404)\n- DELETE /products/{id} returns 204 for existing, 404 for unknown\n- POST /products contract preserved (201 for valid, 400 for invalid)\n- JSON content types working throughout",
"plan": "Clean up the background server process and mark the task as complete.",
"commands": [
{
"keystrokes": "kill %1 2>/dev/null; echo 'Server stopped'\n",
"duration": 0.5
}
],
"task_complete": true
}Command
$ kill %1 2>/dev/null; echo 'Server stopped' Server stopped [exit=0]
Note
Model marked task complete after command batch.
Verifier
[verifier exit=0] reward: 1
by Lottolabs · shard 1 · 8/28/2026, 1:29:56 PM · cmtcznyxp000klm01qqafs0cf100.0%1/1 correct · 1 correct traces · 0 incorrect traces
by Lottolabs · shard 1 · 8/28/2026, 1:29:56 PM · cmtcznyxp000klm01qqafs0cf
100.0%
Correct samples
sample 1 · user-crud-apipass · 100.0% · 36721ms · f26524426201
Question
Complete the JSON user API in `/app/server.py`.
The existing `POST /users` and `GET /users` behavior must keep working. Implement:
- `GET /users/{id}`: return the user or `404`.
- `PUT /users/{id}`: accept a JSON object, update `name` and/or `email`, reject an empty update or empty string values with `400`, preserve unspecified fields, and return the updated user. Return `404` for an unknown ID.
- `DELETE /users/{id}`: delete an existing user and return HTTP `204` with an empty body. Return `404` for an unknown ID.
All JSON responses must set `Content-Type: application/json`. Do not change the port or introduce third-party dependencies.Rendered prompt
You control a persistent Linux shell session inside a container. State persists across replies: your working directory, environment variables, and background jobs carry over from one command block to the next. Each reply MUST contain exactly one ```bash fenced block containing one or more non-interactive shell commands, which are executed in that same shell; stdout/stderr and exit code are returned. Prefer batching related inspection/edit/test commands instead of spending one model turn per tiny command. When the task is complete, reply with the single token TASK_COMPLETE and no code block. If you need Python/Ruby/Node/etc., run it from bash with a heredoc (for example: python3 <<'PY' ... PY). Avoid dumping huge files; inspect with head/tail/grep/scripts. Bound password crackers and deliberately long-running commands yourself with timeout, but do not prematurely cap package installs, builds, or tests unless they are clearly stuck. Never run foreground servers; start them in the background and verify them.
Agent trace
Turn 1
Assistant
{
"analysis": "I need to inspect the current state of /app/server.py to understand the existing implementation before making changes.",
"plan": "First, read the existing server.py file to understand the current structure, then implement the required endpoints.",
"commands": [
{
"keystrokes": "cat /app/server.py\n",
"duration": 0.5
}
]
}Command
$ cat /app/server.py
import json
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
users = {}
next_id = 1
class Handler(BaseHTTPRequestHandler):
def log_message(self, *_):
pass
def send_json(self, status, value):
body = json.dumps(value).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def read_json(self):
try:
size = int(self.headers.get("Content-Length", "0"))
value = json.loads(self.rfile.read(size) or b"{}")
return value if isinstance(value, dict) else None
except (ValueError, json.JSONDecodeError):
return None
def user_id(self):
parts = self.path.strip("/").split("/")
if len(parts) != 2 or parts[0] != "users" or not parts[1].isdigit():
return None
return int(parts[1])
def do_GET(self):
if self.path == "/users":
return self.send_json(200, list(users.values()))
user_id = self.user_id()
if user_id is None or user_id not in users:
return self.send_json(404, {"error": "not found"})
self.send_json(200, users[user_id])
def do_POST(self):
global next_id
if self.path != "/users":
return self.send_json(404, {"error": "not found"})
value = self.read_json()
if not value or not value.get("name") or not value.get("email"):
return self.send_json(400, {"error": "name and email are required"})
user = {"id": next_id, "name": value["name"], "email": value["email"]}
users[next_id] = user
next_id += 1
self.send_json(201, user)
def do_PUT(self):
self.send_json(501, {"error": "not implemented"})
def do_DELETE(self):
self.send_json(501, {"error": "not implemented"})
ThreadingHTTPServer(("0.0.0.0", 8080), Handler).serve_forever()
[exit=0]Turn 2
Assistant
{
"analysis": "The existing server.py has GET /users, GET /users/{id}, and POST /users implemented. PUT and DELETE are stubs returning 501. I need to implement PUT /users/{id} and DELETE
…[17181 characters truncated — full trace in blob]…
ers/2 (204)\ns, b, h = req(\"DELETE\", \"/users/2\")\nprint(f\"DELETE /users/2: {s} body='{b}' CT={h.get('Content-Type')}\")\n\n# GET /users/2 after delete (404)\ns, b, h = req(\"GET\", \"/users/2\")\nprint(f\"GET /users/2 after del: {s} {b} CT={h.get('Content-Type')}\")\n\n# DELETE /users/999 (404)\ns, b, h = req(\"DELETE\", \"/users/999\")\nprint(f\"DELETE /users/999: {s} {b} CT={h.get('Content-Type')}\")\n\n# GET /users/1 to verify preserved fields\ns, b, h = req(\"GET\", \"/users/1\")\nprint(f\"GET /users/1 final: {s} {b} CT={h.get('Content-Type')}\")\n\nprint(\"\\nAll tests done.\")\nPYEOF\n",
"duration": 2.0
}
]
}Command
$ python3 << 'PYEOF'
import urllib.request, json
def req(method, path, data=None):
url = f"http://localhost:8080{path}"
body = json.dumps(data).encode() if data is not None else None
r = urllib.request.Request(url, data=body, method=method)
if body:
r.add_header("Content-Type", "application/json")
try:
resp = urllib.request.urlopen(r)
return resp.status, resp.read().decode(), dict(resp.headers)
except urllib.error.HTTPError as e:
return e.code, e.read().decode(), dict(e.headers)POST user 1
s, b, h = req("POST", "/users", {"name": "Alice", "email": "[email protected]"})
print(f"POST /users: {s} {b} CT={h.get('Content-Type')}")POST user 2
s, b, h = req("POST", "/users", {"name": "Bob", "email": "[email protected]"})
print(f"POST /users: {s} {b} CT={h.get('Content-Type')}")GET /users
s, b, h = req("GET", "/users")
print(f"GET /users: {s} {b} CT={h.get('Content-Type')}")GET /users/1
s, b, h = req("GET", "/users/1")
print(f"GET /users/1: {s} {b} CT={h.get('Content-Type')}")GET /users/999 (404)
s, b, h = req("GET", "/users/999")
print(f"GET /users/999: {s} {b} CT={h.get('Content-Type')}")PUT /users/1 update name only
s, b, h = req("PUT", "/users/1", {"name": "Alice Updated"})
print(f"PUT /users/1 name: {s} {b} CT={h.get('Content-Type')}")PUT /users/1 update email only
s, b, h = req("PUT", "/users/1", {"email": "[email protected]"})
print(f"PUT /users/1 email: {s} {b} CT={h.get('Content-Type')}")PUT /users/1 empty update (400)
s, b, h = req("PUT", "/users/1", {})
print(f"PUT /users/1 empty: {s} {b} CT={h.get('Content-Type')}")PUT /users/1 empty string name (400)
s, b, h = req("PUT", "/users/1", {"name": ""})
print(f"PUT /users/1 empty name: {s} {b} CT={h.get('Content-Type')}")PUT /users/999 (404)
s, b, h = req("PUT", "/users/999", {"name": "X"})
print(f"PUT /users/999: {s} {b} CT={h.get('Content-Type')}")DELETE /users/2 (204)
s, b, h = req("DELETE", "/users/2")
print(f"DELETE /users/2: {s} body='{b}' CT={h.get('Content-Type')}")GET /users/2 after delete (404)
s, b, h = req("GET", "/users/2")
print(f"GET /users/2 after del: {s} {b} CT={h.get('Content-Type')}")DELETE /users/999 (404)
s, b, h = req("DELETE", "/users/999")
print(f"DELETE /users/999: {s} {b} CT={h.get('Content-Type')}")GET /users/1 to verify preserved fields
s, b, h = req("GET", "/users/1")
print(f"GET /users/1 final: {s} {b} CT={h.get('Content-Type')}")
print("\nAll tests done.")
PYEOF
POST /users: 201 {"id": 1, "name": "Alice", "email": "[email protected]"} CT=application/json
POST /users: 201 {"id": 2, "name": "Bob", "email": "[email protected]"} CT=application/json
GET /users: 200 [{"id": 1, "name": "Alice", "email": "[email protected]"}, {"id": 2, "name": "Bob", "email": "[email protected]"}] CT=application/json
GET /users/1: 200 {"id": 1, "name": "Alice", "email": "[email protected]"} CT=application/json
GET /users/999: 404 {"error": "not found"} CT=application/json
PUT /users/1 name: 200 {"id": 1, "name": "Alice Updated", "email": "[email protected]"} CT=application/json
PUT /users/1 email: 200 {"id": 1, "name": "Alice Updated", "email": "[email protected]"} CT=application/json
PUT /users/1 empty: 400 {"error": "empty update"} CT=application/json
PUT /users/1 empty name: 400 {"error": "name must be a non-empty string"} CT=application/json
PUT /users/999: 404 {"error": "not found"} CT=application/json
DELETE /users/2: 204 body='' CT=None
GET /users/2 after del: 404 {"error": "not found"} CT=application/json
DELETE /users/999: 404 {"error": "not found"} CT=application/json
GET /users/1 final: 200 {"id": 1, "name": "Alice Updated", "email": "[email protected]"} CT=application/json
All tests done.
[exit=0]Turn 5
Assistant
{
"analysis": "All tests pass perfectly:\n- POST /users: 201 with correct JSON and Content-Type: application/json ✓\n- GET /users: 200 with list of users ✓\n- GET /users/1: 200 with user object ✓\n- GET /users/999: 404 ✓\n- PUT /users/1 (name only): 200, email preserved ✓\n- PUT /users/1 (email only): 200, name preserved ✓\n- PUT /users/1 (empty {}): 400 ✓\n- PUT /users/1 (empty string name): 400 ✓\n- PUT /users/999: 404 ✓\n- DELETE /users/2: 204 with empty body ✓\n- GET /users/2 after delete: 404 ✓\n- DELETE /users/999: 404 ✓\n- GET /users/1 final: both fields updated correctly ✓\n\nAll JSON responses have Content-Type: application/json. Port 8080 unchanged. No third-party dependencies used.",
"plan": "The task is complete. All endpoints are implemented and verified working correctly.",
"commands": [],
"task_complete": true
}Note
Model marked task complete.
Verifier
[verifier exit=0] reward: 1
