Claude Haiku 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 Haiku 5.5default · thinking on · Claude Code 2.1.299 (headless -p) · cloud · 2026-10-17 · by @bobsiopener
- Estimated cost
- $0.0055
- Wall time
- 23s
- 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.31. Claude Code's own reported cost for claude-haiku-5.5: $0.00547. 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.0055
- Wall time
- 23s
- Human step-ins
- 0
- Turns
- 6
| Model | Input tok | Output tok | Cost |
|---|---|---|---|
| AClaude Haiku 5.5anthropic/claude-haiku-5.5effort default (no --effort flag) · thinking on | 85,634 | 4,640 | $0.0055 |
Reproduce this run
- Use the use case prompt on the use case page.
- Same setup: Claude Haiku 5.5 (anthropic/claude-haiku-5.5, effort default (no --effort flag) · thinking on) via
Claude Code 2.1.299 (headless -p), cloud. - 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.
- 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?
Automated checks
Trust 80% comes from these checks (how Trust % works).
- Run date is not in the future : passed
- Model is in the dated pricing table : passed
- Cost recomputed from tokens matches : passed
- Cached tokens are consistent : passed
- Output throughput is plausible : passed
- Autonomy matches step-ins : passed
- Step-ins do not exceed turns : passed
- Retries agree with first-try result : passed
- Evidence log SHA-256 matches : passed
- Evidence log is substantial : passed
Evidence log
[
{
"duration_api_ms": 21363,
"stop_reason": "end_turn",
"session_id": "[REDACTED]",
"total_cost_usd": 0.00547017,
"usage": {
"input_tokens": 8,
"cache_creation_input_tokens": 12069,
"cache_read_input_tokens": 73557,
"output_tokens": 4640,
"output_tokens_details": {
"thinking_tokens": 1816
},
"server_tool_use": {
"web_search_requests": 0,
"web_fetch_requests": 0
},
"service_tier": "standard",
"cache_creation": {
"ephemeral_1h_input_tokens": 12069,
"ephemeral_5m_input_tokens": 0
},
"inference_geo": "global",
"iterations": [
{
"input_tokens": 2,
"output_tokens": 646,
"cache_read_input_tokens": 22379,
"cache_creation_input_tokens": 2229,
"cache_creation": {
"ephemeral_5m_input_tokens": 0,
"ephemeral_1h_input_tokens": 2229
},
"type": "message"
}
],
"speed": "standard",
"fallback_credit": null
},
"modelUsage": {
"claude-haiku-5-5": {
"inputTokens": 8,
"outputTokens": 4640,
"cacheReadInputTokens": 73557,
"cacheCreationInputTokens": 12069,
"webSearchRequests": 0,
"costUSD": 0.00547017,
"contextWindow": 1000000,
"maxOutputTokens": 128000,
"thinkingTokens": 1816,
"canonicalModel": "claude-haiku-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": 6,
"subtype": "success",
"api_error_status": null,
"result": "I rewrote `bank.py` to fix all three problems. The stress test passes: 8 threads \u00d7 20,000 transfers finished in 0.37 s, with the total unchanged and no negative balances. A concurrent auditor never saw a bad snapshot.\n\n**What was wrong and what changed**\n\n- **Deadlock:** `transfer(A, B)` and `transfer(B, A)` took the two per-account locks in opposite orders. There is now a single `threading.Lock` that guards `bal` and `log`, so there is no lock ordering to get wrong.\n- **Money drift and negative balances:** the funds check and the read of `current` happened before the lock was taken, so two transfers could both pass the check and one write-back would overwrite the other's update. Now the check, debit, credit, and log entry all happen inside the lock.\n- **Inconsistent `total()`/`snapshot()`:** both now run under the same lock, so they only see states between whole transfers.\n- **Partial updates on bad input:** an unknown `dst` used to raise after `src` had already been debited. All validation now happens before any mutation, and a failed transfer leaves `bal` and `log` untouched.\n\n**Choices you may want to check**\n\n- **Self-transfers (`src == dst`):** these used to return silently without logging. They now check funds like any other transfer and append a log entry, with no balance change. That keeps \"every successful call appears in `log` exactly once\" true without exception.\n- **Unknown account names:** these now raise `KeyError` before any change is made.\n- **`locks` attribute removed:** it was an internal per-account lock dict, not part of the API you listed. Nothing in `bank.py` uses it any more.\n\nThe test script is in `/tmp/bank_stress/stress.py`, outside the work directory. The work directory is not a git repo, so I made no commit.",
"ttft_ms": 1367,
"type": "result",
"duration_ms": 22091,
"uuid": "[REDACTED]",
"ttft_stream_ms": 1171,
"time_to_request_ms": 217,
"first_content_frame_ms": 1172,
"queued_turn_count": 0,
"result_index": 0
}
]