Skip to content

Claude Opus 5.5 (effort default (no --effort flag) · thinking on) on Fix a lock-ordering deadlock, a lost-update race and torn snapshots in a threaded bank ledger

AClaude Opus 5.5default · thinking on · Claude Code 2.1.299 (headless -p) · cloud · 2026-10-17 · by @bobsiopener

Trust 80%Fully autonomoussuccess · first try
Estimated cost
$0.14
Wall time
28s
Human step-ins
0
Outcome
Pass

Compare with other models on this task →

Summary

Exact prompt

# Task: fix the concurrency bugs in bank.py `bank.py` is called from many worker threads at once. In production the worker pool sometimes hangs forever, money occasionally appears or disappears (the bank total drifts), balances have gone negative, and auditors say `total()`/`snapshot()` sometimes report a state that never existed. Fix `bank.py` so that, under heavy concurrent use: - `transfer` can never deadlock, never loses or creates money, never makes a balance negative, and raises `InsufficientFunds` (leaving everything unchanged) when the source lacks funds; - `total()` and `snapshot()` return a consistent point-in-time view even while transfers are running; - every successful transfer is in `log` exactly once, failed ones not at all. Keep the public API (class names, method names/signatures, `bal` and `log` attributes). Do not make it single-threaded by removing threading support from callers; a global lock is acceptable only if it keeps throughput reasonable (the grader runs 8 threads x 20,000 transfers in under 30 s). Standard library only. No network.

Three distinct bugs: check-then-act outside the lock (lost updates/negatives), opposite lock order (deadlock), unlocked snapshot/total (torn reads). Fixing one exposes the others; the stress test forces frequent thread switches. Original benchmark task written for usecaserank.com (not based on a specific public post). Headless, offline run on a reproducible fixture. Each run's notes say how its cost was measured (estimates are marked).

Success criteria: 1. no_deadlock: each of 3 stress runs (8 threads x 20,000 random transfers plus a concurrent auditor) finishes within 30 s. 2. final_total_ok: total money is conserved in every run. 3. no_negative: no balance is ever negative (final state and every audited snapshot). 4. log_exact: log has exactly one entry per successful transfer. 5. snapshots_consistent: every snapshot()/total() taken during the run sums to the true total. 6. nsf_raises_unchanged: an overdraft raises InsufficientFunds and changes neither balances nor log.

Setup notes: Use Case Scout batch 5 case e03-bank-deadlock-race (offline fixture). Prepared with batch5/prepare.sh; hidden key/tests kept outside the agent's work dir. Prompt = TASK.md verbatim. Scored with the scout's scorer: `python score.py WORKDIR KEYDIR`. Tools: Read/Write/Edit/Glob/Grep/Bash, web disabled. Single run, no retries. 1-min load average at scoring: 0.58. Claude Code's own reported cost for claude-opus-5.5: $0.138694. Score: 6/6 checks. Cost is an ESTIMATE: measured tokens x pricing.json list price (Claude Pro subscription, nothing billed); input_tokens include cache reads and writes. Single run.

Estimated cost List-price estimate; not actually billed.

Metrics

Estimated cost
$0.14
Wall time
28s
Human step-ins
0
Turns
5
Model usage
ModelInput tokOutput tokCost
AClaude Opus 5.5anthropic/claude-opus-5.5effort default (no --effort flag) · thinking on96,1082,654$0.14

Reproduce this run

  1. Use the use case prompt on the use case page.
  2. Same setup: Claude Opus 5.5 (anthropic/claude-opus-5.5, effort default (no --effort flag) · thinking on) via Claude Code 2.1.299 (headless -p), cloud.
  3. It counts as a success only if it meets every success criterion: 1. no_deadlock: each of 3 stress runs (8 threads x 20,000 random transfers plus a concurrent auditor) finishes within 30 s. 2. final_total_ok: total money is conserved in every run. 3. no_negative: no balance is ever negative (final state and every audited snapshot). 4. log_exact: log has exactly one entry per successful transfer. 5. snapshots_consistent: every snapshot()/total() taken during the run sums to the true total. 6. nsf_raises_unchanged: an overdraft raises InsufficientFunds and changes neither balances nor log.
  4. Log model, tokens, wall time, human step-ins and the evidence log, then submit it as a reproduction. A different model? Submit a head-to-head rerun.

How runs are scored: Methodology.

Was this useful?

⇄ Rerun on another model

Automated checks

Trust 80% comes from these checks (how Trust % works).

Evidence log
[
 {
  "duration_api_ms": 26834,
  "stop_reason": "end_turn",
  "session_id": "[REDACTED]",
  "total_cost_usd": 0.13869420000000002,
  "usage": {
   "input_tokens": 10,
   "cache_creation_input_tokens": 8507,
   "cache_read_input_tokens": 87591,
   "output_tokens": 2654,
   "output_tokens_details": {
    "thinking_tokens": 496
   },
   "server_tool_use": {
    "web_search_requests": 0,
    "web_fetch_requests": 0
   },
   "service_tier": "standard",
   "cache_creation": {
    "ephemeral_1h_input_tokens": 8507,
    "ephemeral_5m_input_tokens": 0
   },
   "inference_geo": "global",
   "iterations": [
    {
     "input_tokens": 2,
     "output_tokens": 858,
     "cache_read_input_tokens": 20009,
     "cache_creation_input_tokens": 644,
     "cache_creation": {
      "ephemeral_5m_input_tokens": 0,
      "ephemeral_1h_input_tokens": 644
     },
     "type": "message"
    }
   ],
   "speed": "standard",
   "fallback_credit": null
  },
  "modelUsage": {
   "claude-opus-5-5": {
    "inputTokens": 10,
    "outputTokens": 2654,
    "cacheReadInputTokens": 87591,
    "cacheCreationInputTokens": 8507,
    "webSearchRequests": 0,
    "costUSD": 0.13869420000000002,
    "contextWindow": 1000000,
    "maxOutputTokens": 128000,
    "thinkingTokens": 496,
    "canonicalModel": "claude-opus-5-5",
    "provider": "firstParty",
    "costBasis": "list"
   }
  },
  "permission_denials": [],
  "terminal_reason": "completed",
  "fast_mode_state": "off",
  "fast_mode_disabled_reason": "sdk_opt_in_required",
  "subagent_stats": {
   "spawned": 0,
   "requested": {
    "background": 0,
    "foreground": 0,
    "unset": 0
   },
   "started_in_background": 0,
   "max_depth": 0,
   "spawned_by_subagents": 0,
   "completed": 0,
   "failed": 0,
   "killed": {
    "parent": 0,
    "user": 0,
    "system": 0
   },
   "refused": {
    "depth_limit": 0,
    "concurrency_limit": 0,
    "budget": 0
   },
   "by_type": {}
  },
  "safety_stops": 0,
  "is_error": false,
  "num_turns": 5,
  "subtype": "success",
  "api_error_status": null,
  "result": "I fixed `bank.py`. A stress test with 8 threads \u00d7 20,000 random transfers across 20 accounts, plus an auditor thread calling `total()` and `snapshot()` the whole time, finished in 0.34 s against your 30 s limit. The total was conserved, no balance went negative, the log entries matched the successful transfers exactly, and the auditor never saw an inconsistent view.\n\n**What was wrong:**\n1. **Money appearing or disappearing:** `current = self.bal[src]` was read before any lock was taken. The write later used that stale value (`current - amt`), so two concurrent debits from the same account could overwrite each other.\n2. **Negative balances:** the funds check also happened outside the lock. Two transfers could both pass the check and then both debit the account.\n3. **Hangs:** each transfer locked `src` then `dst`, so A\u2192B and B\u2192A running at once could each grab one lock and wait forever for the other.\n4. **Audit reports of states that never existed:** `total()` and `snapshot()` read balances with no locking, so they could catch a transfer halfway, with money taken from one account but not yet added to the other.\n5. **Log entries for failed transfers:** the log was written before the balances were updated, so a transfer that failed partway would still be logged. An unknown `dst` would also raise an error after `src` had already been debited, so that money was lost.\n\n**The fix:** one lock now covers `bal` and `log` together:\n- `transfer` checks that both accounts exist, checks funds, then updates both balances and the log, all while holding that lock. A transfer either fully happens and is logged once, or nothing changes.\n- `total()` and `snapshot()` take the same lock, so they always see a state between transfers.\n- With only one lock there's no lock order to get wrong, so it can't deadlock.\n\nA global lock doesn't hurt speed here: Python only runs one thread's code at a time anyway, so separate per-account locks wouldn't add throughput. They would also need careful lock ordering, plus taking every lock to get a consistent snapshot.\n\nThe public API and the `bal` and `log` attributes are unchanged. The old per-account `locks` dict is gone. There's one small behaviour change: a transfer from an account to itself is still a silent no-op, but if that account doesn't exist it now raises `KeyError`.\n\nThe stress test is at `/tmp/banktest/stress.py`, outside the project directory.",
  "ttft_ms": 1734,
  "type": "result",
  "duration_ms": 27589,
  "uuid": "[REDACTED]",
  "ttft_stream_ms": 940,
  "time_to_request_ms": 240,
  "first_content_frame_ms": 940,
  "queued_turn_count": 0,
  "result_index": 0
 }
]

Get new runs and ranking changes weekly