CLAUDE LABJP
PRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yetPRICING — September 1 was the scheduled date for the Sonnet 5 price increase, and it did not happen. The introductory $2/$10 per MTok now stands as the regular pricePARTNER — Salesforce and Anthropic announced Claudeforce, an expanded partnership. The Salesforce in Claude plugin ships with 37 prebuilt sales skills, from meeting prep to pipeline managementTRUST — Claudeforce serves Claude through Amazon Bedrock inside the Salesforce Trust Boundary, so data and inference never leave the security perimeter — an answer aimed squarely at regulated industriesBETA — Salesforce in Claude is with select pilot customers for now, with an open beta expected during SeptemberLIMITS — The 50% weekly-limit boost runs through September 13. From September 14 the permanent level is 25% above the pre-promotion baseline, roughly a 17% cut from todayRELEASE — Claude Code has shipped nothing since v2.1.251 on August 28. Against a pace of one release every 0.8 days, a four-day gap is among the longest yet
Articles/Claude Code
Claude Code/2026-04-23Advanced

Finishing Long-Running Claude Code Tasks: A Resilience Playbook You Can Ship

Multi-hour Claude Code jobs — bulk refactors, TypeScript migrations, mass test generation — always stop before they finish, and recovery is painful when you cannot tell what already ran. This guide ships concrete patterns: a checkpoint-driven manifest, a three-state circuit breaker, idempotent retry rules, and a freeze-and-resume protocol you can copy into your repo today.

Claude Code243long-running taskstroubleshooting89operations26resilience10circuit breaker

You hand Claude Code a multi-hour task — a 500-file refactor, a TypeScript migration, generating tests across a monorepo — and it stops partway through. The chat buffer is ambiguous about how far it got. You do not know whether to resume, re-run, or start over. A day of work is now a triage exercise.

Running jobs like this reliably is not about getting better recovery prompts. It is about designing the task from day one to assume it will stop. This playbook walks through the patterns I rely on in production — a checkpoint manifest, a circuit breaker, idempotent retries, and a freeze protocol — with copy-pasteable code.

The four stop modes (and why one fix does not cover all of them)

Grouping every "Claude Code stopped" failure together is why the fixes do not stick. There are four distinct modes.

Mode 1 — output token or context ceiling. A single turn maxes out its output budget, or the whole session fills the context window and quality degrades until Claude forgets the task. Fix: split prompts; shorten the per-turn scope; start new sessions at natural boundaries.

Mode 2 — API rate limits and transient errors. Anthropic returns a 429 or a 503, Claude Code auto-retries, but sustained failures eventually halt the run. External factor. Fix with retry strategy + circuit breaker.

Mode 3 — local environment hit a wall. Disk full, process OOM, shell session dropped, SSH tunnel timed out. Claude Code "went silent" but the real culprit is your laptop or VM. Fix with environment monitoring, not prompt changes.

Mode 4 — scope design error. The most common. You asked for "one file at a time," but Claude Code pulled in the whole dependency graph to reason about imports and blew context in the first hour. Or you bundled npm test into each step and every edit now waits for a 4-minute suite. Fix with better scope engineering.

A real production setup addresses all four separately. Most "resilience" tips in the wild conflate them.

Checkpoint-driven design: put state on disk, not in memory

The single most impactful pattern I use is to stop relying on Claude Code's session memory for progress, and instead write progress to a JSON manifest on disk.

The minimal manifest

{
  "task_id": "typescript-migration-2026-04",
  "total_items": 487,
  "completed": [
    { "item": "src/user/profile.js", "finished_at": "2026-04-23T09:12:31Z", "status": "ok" },
    { "item": "src/user/settings.js", "finished_at": "2026-04-23T09:13:02Z", "status": "ok" }
  ],
  "failed": [
    { "item": "src/legacy/auth.js", "reason": "ambiguous imports", "last_tried_at": "2026-04-23T09:15:48Z" }
  ],
  "queued": [
    "src/legacy/rbac.js",
    "src/legacy/session.js"
  ]
}

In the opening prompt, declare the contract explicitly: "Update this manifest the moment each item finishes, and commit. If you are restarted, read this file and resume from the head of queued."

Why this works where session memory does not

When Claude Code stops, the manifest still exists. On resume you only need to instruct: "Read task_manifest.json and start from the first entry in queued." You get an exact resume point instead of a paragraph of guesswork.

Two design rules make the difference:

  • Update after every single item, not every batch. Batch updates lose progress if you stop mid-batch.
  • Bind the update to a git commit. If the manifest is corrupted, git log is your source of truth.

Bash wrapper for the update step

#!/usr/bin/env bash
# mark_done.sh — record one item and commit
set -euo pipefail
 
MANIFEST="./task_manifest.json"
ITEM="$1"
STATUS="${2:-ok}"
 
python3 - "$MANIFEST" "$ITEM" "$STATUS" << 'PY'
import sys, json, datetime
manifest_path, item, status = sys.argv[1], sys.argv[2], sys.argv[3]
with open(manifest_path) as f:
    m = json.load(f)
m["completed"].append({
    "item": item,
    "finished_at": datetime.datetime.utcnow().isoformat() + "Z",
    "status": status
})
if item in m["queued"]:
    m["queued"].remove(item)
with open(manifest_path, "w") as f:
    json.dump(m, f, indent=2, ensure_ascii=False)
PY
 
git add "$MANIFEST"
git commit -m "progress: $ITEM ($STATUS)" --quiet

Claude Code calls ./mark_done.sh src/user/profile.js after each item. Your git log becomes a line-by-line audit trail.

A three-state circuit breaker

Runaway retries against a failing API will happily burn through your quota. A circuit breaker is the classic fix from distributed systems, and it transplants cleanly here.

The states

  • Closed — normal operation, retry on failure up to a threshold
  • Open — too many consecutive failures; refuse all calls for a cooldown window
  • Half-open — cooldown elapsed, allow one probe; if it succeeds, go Closed; if it fails, back to Open

Reference implementation

import time, json, pathlib
 
STATE_PATH = pathlib.Path("./cb_state.json")
THRESHOLD = 3            # consecutive failures before opening
COOLDOWN_SEC = 600       # seconds to stay open
 
def load_state():
    if not STATE_PATH.exists():
        return {"state": "closed", "fail_count": 0, "opened_at": 0}
    return json.loads(STATE_PATH.read_text())
 
def save_state(s):
    STATE_PATH.write_text(json.dumps(s, indent=2))
 
def allow_call():
    s = load_state()
    if s["state"] == "closed":
        return True
    if s["state"] == "open":
        if time.time() - s["opened_at"] >= COOLDOWN_SEC:
            s["state"] = "half_open"
            save_state(s)
            return True
        return False
    return True  # half_open
 
def record_success():
    save_state({"state": "closed", "fail_count": 0, "opened_at": 0})
 
def record_failure():
    s = load_state()
    s["fail_count"] += 1
    if s["fail_count"] >= THRESHOLD:
        s["state"] = "open"
        s["opened_at"] = time.time()
    save_state(s)

Hook allow_call / record_success / record_failure around the call Claude Code makes. A threshold of 3 with a 10-minute cooldown is conservative — transient 503s self-heal, real outages stop burning tokens.

Idempotency is the price of admission

None of this helps if rerunning a unit corrupts state. Every item must be safe to run twice.

Three rules that get you there:

Check the current state before acting. If you are "converting a file to TypeScript," inspect the extension and import style first and skip if already converted. Trusting only the manifest's completed list breaks if you stop between the update and the commit.

Write all outputs to attempt-scoped paths. Logs and intermediate artifacts live at logs/{task_id}/{item}_{attempt}.log. Overwriting a stable path destroys the evidence you need for the next attempt.

Defer shared-resource changes to a final commit phase. DB migrations, key issuance, outbound notifications — anything that is hard to make idempotent — runs in a single finalization step after all items succeed. Never leave half-completed side effects behind.

Freeze-and-resume: preserving partial work

When the breaker trips or no response has come back for 20 minutes, automate a freeze.

#!/usr/bin/env bash
# freeze.sh — snapshot current working state to a freeze branch
set -euo pipefail
 
TASK_ID=$(python3 -c "import json; print(json.load(open('task_manifest.json'))['task_id'])")
FREEZE_BRANCH="freeze/${TASK_ID}-$(date +%Y%m%d-%H%M%S)"
 
git checkout -b "$FREEZE_BRANCH"
git add -A
git commit -m "freeze: ${TASK_ID} at $(date -Iseconds)" --allow-empty
git push origin "$FREEZE_BRANCH"
echo "Frozen to $FREEZE_BRANCH"

Keep freeze branches off main. On resume, cross-check against the manifest: if the freeze is consistent, rebase it onto main; if not, discard and restart from queued.

Pair the freeze with a notification so a human actually finds out:

curl -X POST -H 'Content-Type: application/json' \
  -d "{\"text\": \"Circuit opened on ${TASK_ID}. Check freeze/${TASK_ID}-*.\"}" \
  "$SLACK_WEBHOOK_URL"

Silent stops waste hours before anyone notices.

The design checklist I run every long task through

Before kicking off any multi-hour Claude Code job I run this five-item gate:

  • Is each unit of work scoped to 30 seconds to 5 minutes? Larger units inflate failure cost.
  • Is each unit idempotent — or made idempotent by an explicit state check before running?
  • Does progress live in a manifest that survives a crash?
  • Is there a failure notification path? Silent stops get discovered hours too late.
  • Are the circuit breaker threshold and cooldown written down, not implicit?

If a task fails this checklist, I redesign the task before touching a prompt. Every time I have skipped this check, I have paid for it.

Start with one thing

You do not need to implement all of this at once. The one pattern with the best return on effort is the manifest-driven progress tracker. One JSON file and one Bash wrapper gives you a clean resume point and a commit-level audit trail, which together solve maybe 70% of the pain.

Add the circuit breaker next. Freeze protocol and idempotency tightening come after that, once you see where your own pipeline actually breaks.

Stops will still happen. The goal is not a task that never stops — it is a stop you can recover from without losing a day.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-09-01
Once I passed a dozen MCP servers, I stopped trusting the startup list
Only some of the servers that say connection failed at startup are ones you can actually fix. Here is a probe that separates no-response, launch failure, and protocol rejection, measured across a 14-server fleet where sequential 22.03s became parallel 5.09s, and where trimming the deadline quietly turned healthy servers into failures.
Claude Code2026-08-31
Half of My Scheduled Runs Vanished Without a Single Error
A batch job set to run twice a day was only firing once. No errors, no failure alerts. Here is how to expand your own schedule, count expected runs, and reconcile them against execution records to catch silent misses.
Claude Code2026-08-30
Your Config Asks for effort xhigh With Thinking Off, and It Now Quietly Runs at high
Disabling thinking while asking for effort xhigh or max returns a 400. That failure now degrades silently to high instead, so here is a small preflight that catches the mismatch before you send the request.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →