Claude Codeの5時間制限:graceful stopを確認し、チェックポイントを残して続ける

Claude Codeの5時間制限に備える実践手順です。リポジトリを検査し、復元可能なGit状態を保存してから、待機、usage credits、独立APIのどれで続けるか判断します。

目次
Claude Codeの5時間制限:graceful stopを確認し、チェックポイントを残して続ける

Claude Codeがコーディング途中で5時間のサブスクリプション制限に達しても、最後の応答だけで作業完了とは判断できません。新しいgraceful stopはbest effortの終了バッファです。weekly usageから小さな固定枠を使って、より安全な停止点を探すことはできますが、cleanなworking tree、commit、テスト成功、機能完成を保証するものではありません。

次の順番で対応してください。

  1. 表示された制限の種類とreset時刻を読む。
  2. staged、unstaged、untrackedの変更を確認する。
  3. 最小限で意味のある検証を実行する。
  4. 復元可能なGitチェックポイントを作る。
  5. その後で、待機、usage credits、APIのどれを使うか決める。

この順番なら、不確実な中断を管理可能な引き継ぎに変えられます。リポジトリの状態を知らないまま、追加利用に費用をかけることも防げます。

Graceful stopは余裕時間であり、タスク完了ではない

Claude Developersの告知では、5時間制限がタスク途中で来た場合、Claude Codeはgracefulな停止点を探し、weekly usageから小さな固定枠を使って可能な範囲をまとめるとされています。重要なのは、試みるのであって、完了を約束しているわけではない点です。

あるユーザー報告では、以前のように編集途中で切れるより、整理された停止になったとされています。ただし、これは一人の体験であり、すべての環境での再現性を示すものではありません。

次の3つを分けて考えます。

仕組み守れるもの保証しないもの
制限時のgraceful stopエージェントがより整った位置で止まるための短い余裕完了、commit、cleanなtree、テスト成功
Claude Code checkpoint/rewindでセッション中の多くの直接編集を戻せるBash経由の変更、多くのsubagent編集、外部変更、完全な履歴
Gitチェックポイントレビュー、branch、共有、復元ができる状態を記録レビューとテストなしの正しさ

Graceful stopで説明が明確になったり、半端な編集が減ったりすることはあります。しかし、受け入れ確認はGitから始まります。

まず、どの制限が止めたのかを確認する

すべての中断が5時間後に解消するわけではありません。Anthropicのusage limit公式説明は、利用制限とコンテキスト長の制限を分けています。また、同じアカウントに属するClaudeの各画面でusageが共有される場合があります。

メッセージをそのまま読みます。

  • Approaching 5-hour limit:新しい作業を広げず、チェックポイント作成に移る時間です。
  • 5-hour limit reached — resets …:表示された時刻を基準にします。
  • weekly limitやモデル固有の制限は、5時間枠がresetしても残ることがあります。
  • capacityやサービス障害はusage枯渇とは別で、追加料金を払っても障害は直りません。

現在のinteractive clientが、セッションを開いたままresetを待ち、その後自動で続行する選択肢を表示しているなら、最も簡単な経路になることがあります。ただし、インストール済みバージョンと実行モードの表示を確認してください。終了すると待機が解除されることがあり、background、print、API keyの各モードでは挙動が異なる場合があります。

Claudeに続きを任せる前にリポジトリを検査する

リポジトリのrootで、ファイルを変更しないコマンドを実行します。

git status --short --branch
git diff --stat
git diff
git diff --cached
git ls-files --others --exclude-standard

Claude Codeの/diffも確認に使えますが、working treeの事実はGitで確認します。

続行前に5つ答えられる状態にします。

  1. どのファイルが変わったか。
  2. staged、unstaged、untrackedはそれぞれ何か。
  3. 呼び出しだけ変わって型やテストが未更新など、半端な編集がないか。
  4. lockfile、生成ファイル、設定、migrationが予期せず変わっていないか。
  5. 主な破損を見つけられる最小のコマンドは何か。

実際のプロジェクトに合う検証を実行します。

# 例です。リポジトリで本当に使う最小の関連コマンドに置き換えてください。
npm test -- path/to/affected.test.ts
pytest tests/test_affected.py -q
cargo test affected_module
git diff --check

安全な停止とは、Claudeが要約を書いたことではありません。変更ファイル、残作業、検証結果、次の小さな操作を説明できることです。

Diffを理解できない場合は、「残りを全部終わらせて」と頼まないでください。先にチェックポイントを作ります。

復元可能なチェックポイントを2通りから選ぶ

方法1:レビュー済みのlocal WIP commit

現在の状態がある程度まとまっていて、local branchに一時commitを残せる場合に使います。

git status --short --branch
git add -p
# 確認済みの新規ファイルは明示的に追加します。
git add -- path/to/reviewed-new-file
git diff --cached --check
git diff --cached
# ここで最小の関連testまたはlintを実行します。
git commit -m "WIP: checkpoint before Claude Code limit reset"
git rev-parse --short HEAD

git add -Aを無確認で実行しないでください。.env、認証情報、非公開fixture、生成物、大きなbinary、debug dumpを確認します。Local WIP commitは復元点であり、protected branchへpushしてよいという意味ではありません。

短い引き継ぎも残します。

Goal: token expiry validationを完了する
Checkpoint: a1b2c3d
Changed: auth_service.py, tests/test_auth.py
Validation: targeted testは失敗。expected 401, received 200
Next step: callersを変える前にexpiry comparisonを確認

方法2:patchと確認済みuntrackedファイルをリポジトリ外へ保存

Commitを作りたくない場合は、この外部checkpoint方式を使います。Scriptは最初にCLAUDE_CHECKPOINT_DIRの実体パスを解決し、symlink経由を含めてリポジトリ内を指していれば、checkpointファイルを作る前に停止します。

手順は3段階です。まずstaged、unstaged、untrackedの候補とdiffを保存せずに確認し、次に各groupで保存するpath番号を明示的に選び、未選択項目を明確に除外します。最後にSAVE REVIEWEDと入力した場合だけ書き込みます。選択前に対象のdiff全体またはuntrackedファイルを確認し、鍵、非公開データ、保存すべきでない内容を除外してください。1つのファイルの一部だけが安全なら、変更を分割するか上のWIP commit方式を使い、ファイル全体を選択しません。

外部方式は追跡中のファイルの名前変更を検出すると停止するため、確認済みのWIP commitを使ってください。選択したunstagedのパスにstagedの変更もある場合は、両方を確認して保存します。復元前にcheckpointへ保存時に表示された絶対パスを設定してください。基準commitの不一致、変更のあるcheckout、いずれかの手順の失敗で復元は停止します。

repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"

# Step 1: inspect everything. These commands do not save files.
git status --short --branch
git diff --cached
git diff
git ls-files --others --exclude-standard

# Steps 2-3: choose reviewed paths explicitly, then save only that scope.
python3 /dev/fd/3 3<<'PY'
from datetime import datetime, timezone
from pathlib import Path
import json, os, shlex, subprocess, tarfile


def git(*args):
    return subprocess.run(["git", *args], check=True, stdout=subprocess.PIPE).stdout


def path_list(*args):
    return list(dict.fromkeys(Path(os.fsdecode(x)) for x in git(*args).split(b"\0") if x))


def choose(label, items):
    print(f"\n{label} candidates:")
    if not items:
        print("  (none)")
        return []
    for i, path in enumerate(items, 1):
        print(f"  {i:>3}  {shlex.quote(os.fspath(path))}")
    answer = input(
        "Select reviewed numbers to save "
        "(1,3-5; 'all'; blank saves none): "
    ).strip().lower()
    if not answer:
        return []
    if answer == "all":
        return items[:]
    chosen = set()
    try:
        for token in answer.split(","):
            bounds = [int(x) for x in token.strip().split("-", 1)]
            chosen.update(range(min(bounds), max(bounds) + 1))
    except ValueError as exc:
        raise SystemExit("Invalid selection; nothing was saved.") from exc
    if not chosen or min(chosen) < 1 or max(chosen) > len(items):
        raise SystemExit("Selection is outside the displayed range; nothing was saved.")
    return [path for i, path in enumerate(items, 1) if i in chosen]


def patch(cached, items):
    if not items:
        return b""
    args = ["diff"] + (["--cached"] if cached else [])
    return git(*args, "--binary", "--", *(os.fspath(path) for path in items))


repo = Path(git("rev-parse", "--show-toplevel").decode().rstrip("\n")).resolve()
root = Path(os.environ.get(
    "CLAUDE_CHECKPOINT_DIR", os.fspath(Path.home() / "claude-checkpoints")
)).expanduser().resolve(strict=False)
if root == repo or repo in root.parents:
    raise SystemExit("CLAUDE_CHECKPOINT_DIR must resolve outside the repository.")
root.mkdir(parents=True, exist_ok=True)
root = root.resolve()
if root == repo or repo in root.parents:
    raise SystemExit("CLAUDE_CHECKPOINT_DIR must resolve outside the repository.")

# A path-only selection cannot represent both sides of a detected rename.
if (git("diff", "--cached", "--find-renames", "--diff-filter=R", "--name-only", "-z")
        or git("diff", "--find-renames", "--diff-filter=R", "--name-only", "-z")):
    raise SystemExit("Renames require a reviewed WIP commit; nothing was saved.")

candidates = {
    "staged": path_list("diff", "--cached", "--name-only", "-z"),
    "unstaged": path_list("diff", "--name-only", "-z"),
    "untracked": path_list("ls-files", "--others", "--exclude-standard", "-z"),
}
if not any(candidates.values()):
    raise SystemExit("No staged, unstaged, or untracked changes were found.")
selected = {name: choose(name, items) for name, items in candidates.items()}
if not any(selected.values()):
    raise SystemExit("No reviewed changes were selected; nothing was saved.")
missing_baseline = set(selected["unstaged"]) & (
    set(candidates["staged"]) - set(selected["staged"])
)
if missing_baseline:
    raise SystemExit(
        "Selected unstaged paths also have staged changes. Select both reviewed "
        "versions or use a WIP commit; nothing was saved."
    )

not_saved = {
    name: [path for path in candidates[name] if path not in selected[name]]
    for name in candidates
}

print("\nSelected for saving:")
for name, items in selected.items():
    print(f"  {name}: " + (", ".join(shlex.quote(os.fspath(p)) for p in items) or "(none)"))
print("Excluded and NOT saved:")
for name, items in not_saved.items():
    print(f"  {name}: " + (", ".join(shlex.quote(os.fspath(p)) for p in items) or "(none)"))
if input("Type SAVE REVIEWED to save exactly this scope: ").strip() != "SAVE REVIEWED":
    raise SystemExit("Confirmation did not match; nothing was saved.")

current = {
    "staged": path_list("diff", "--cached", "--name-only", "-z"),
    "unstaged": path_list("diff", "--name-only", "-z"),
    "untracked": path_list("ls-files", "--others", "--exclude-standard", "-z"),
}
if current != candidates:
    raise SystemExit("The candidate list changed during review; rerun from step 1.")

stamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
checkpoint = (root / f"{repo.name}-claude-{stamp}").resolve(strict=False)
if checkpoint == repo or repo in checkpoint.parents:
    raise SystemExit("Checkpoint path unexpectedly resolves inside the repository.")
checkpoint.mkdir(exist_ok=False)
(checkpoint / "base-commit.txt").write_bytes(git("rev-parse", "HEAD"))
(checkpoint / "status.txt").write_bytes(git("status", "--short", "--branch"))
(checkpoint / "staged.patch").write_bytes(patch(True, selected["staged"]))
(checkpoint / "unstaged.patch").write_bytes(patch(False, selected["unstaged"]))
(checkpoint / "untracked.list0").write_bytes(b"".join(
    os.fsencode(os.fspath(path)) + b"\0" for path in selected["untracked"]
))

manifest = {
    "selected": {name: [os.fspath(p) for p in items] for name, items in selected.items()},
    "not_saved": {name: [os.fspath(p) for p in items] for name, items in not_saved.items()},
}
(checkpoint / "selection.json").write_text(
    json.dumps(manifest, ensure_ascii=True, indent=2) + "\n", encoding="utf-8"
)
omitted = [
    f"{name}\t{json.dumps(os.fspath(path), ensure_ascii=True)}"
    for name, items in not_saved.items() for path in items
]
(checkpoint / "not-saved.txt").write_text(
    "\n".join(omitted) + ("\n" if omitted else "(none)\n"), encoding="utf-8"
)
if selected["untracked"]:
    with tarfile.open(checkpoint / "untracked.tar.gz", "w:gz", dereference=False) as archive:
        for path in selected["untracked"]:
            archive.add(repo / path, arcname=path.as_posix(), recursive=False)

partial = bool(omitted)
(checkpoint / "scope.txt").write_text(
    "PARTIAL: see not-saved.txt; this is not a complete recovery point.\n"
    if partial else
    "REVIEWED SCOPE: every listed candidate was saved; ignored files remain outside scope.\n",
    encoding="utf-8",
)
print(("PARTIAL checkpoint" if partial else "Reviewed checkpoint") + f" saved to {checkpoint}")
if partial:
    print("Some listed changes were not saved; read not-saved.txt before recovery.")
PY

selection.jsonには選択・除外したpath、not-saved.txtには保存しなかった全項目、scope.txtには保存範囲が記録されます。staged、unstaged、untrackedの候補を1つでも除外すると、terminalとscope.txtは結果をPARTIALと表示します。その場合は完全な復旧用checkpointではなく、選択した範囲だけを復元できます。Ignoredファイルは引き続き対象外であり、このcheckpointはコードの正常動作も証明しません。

復元前にscope.txtとnot-saved.txtを読みます。記録したbase commit上のcleanなcheckoutで、保存済みの範囲だけを復元してください。

(
    set -eu
    checkpoint="/absolute/path/printed-by-the-save-step" # Replace with the actual checkpoint directory.
    test -d "$checkpoint"
    repo_root="$(git rev-parse --show-toplevel)"
    cd "$repo_root"
    for required in base-commit.txt scope.txt not-saved.txt staged.patch unstaged.patch untracked.list0; do
        test -f "$checkpoint/$required"
    done
    cat "$checkpoint/scope.txt" "$checkpoint/not-saved.txt"
    test "$(git rev-parse HEAD)" = "$(cat "$checkpoint/base-commit.txt")"
    test -z "$(git status --porcelain --untracked-files=all)"
    if [ -s "$checkpoint/staged.patch" ]; then
        git apply --index "$checkpoint/staged.patch"
    fi
    if [ -s "$checkpoint/unstaged.patch" ]; then
        git apply "$checkpoint/unstaged.patch"
    fi
    if [ -s "$checkpoint/untracked.list0" ]; then
        tar -xzkf "$checkpoint/untracked.tar.gz"
    fi
    git status --short
)

現在のリポジトリがそのbase commitでなければ、patchを無理に当てないでください。保存したcommitからcleanなbranchまたはworktreeを作り、そこで復元してから意図的に統合します。

Claude Code checkpointは使えるが、Gitの代わりではない

Claude Codeの公式checkpointingドキュメントでは、セッション中の直接的なファイル編集を追跡し、/rewindで確認または復元できると説明されています。各セッションの直近100 checkpointを保持し、conversationに関連付けるため、resume後も利用できる場合があります。

これは悪いagent turnを戻すには便利ですが、リポジトリのbackupではありません。公式に示された制限には、Bashで作られた変更、多くのsubagent編集、外部からの同時変更、symlink/hardlinkの一部が含まれます。Gitなら、永続的なcommit ID、branch、レビュー可能なpatch、Claudeセッションに依存しない復元が得られます。

2層で使います。

  • /rewind:セッション内の素早いrollback。
  • Git commitまたは外部patch bundle:永続的なリポジトリcheck point。

公式CLI referenceには次が記載されています。

claude -c
claude -r "<session>"

Resume後も、編集を許可する前にgit statusを再実行します。Conversationが戻っても、filesystemが以前の要約と一致するとは限りません。

チェックポイント後に、待機・usage credits・APIを選ぶ

状況最初の選択理由
Resetが近く、checkpointが安全で、期限に余裕がある待機追加の従量料金も認証変更もない
同じUIとconversationを維持し、従量課金を許容できるUsage credits対象アカウントはincluded usage後も続行できる
別のkey、provider balance、project budgetが必要API認証と請求をsubscriptionから分けられる
通常の作業日でも何度も止まるPlanとworkflowを見直す1回の中断だけでは恒久upgradeの根拠にならない

切り替えより待機の方が安いなら待つ

Resetが近く、checkpointが完成しているなら、待機が最も安全なことが多いです。正確な時刻とhandoffを記録します。Clientが明示的にwait-and-resumeを提供するときだけ、セッションを開いたままにする挙動へ依存してください。

戻ったら次を実行します。

git status --short --branch
git diff --stat

その後で次の操作を具体的に伝えます。「continue」だけでは、エージェントに状態を推測させることになります。

文脈の連続性が最優先ならusage credits

Anthropicのusage credits公式説明では、対象のPro/MaxユーザーがSettings > Usageで追加のpay-as-you-go usageを有効化し、fundsや支出制御を設定できるとされています。請求はsubscriptionと別で、通常の5時間枠は引き続きresetします。

有効化前に次を決めます。

  • 月間上限とalertを設定する。
  • auto-reloadを許可するか。
  • UIがusage creditsで続行中と表示しているか。
  • 緊急checkpointまたはacceptance testが終わったら停止する。

同じUIとconversationを維持できますが、subscriptionの無料追加枠ではありません。

認証や予算を分けたいならAPI

APIは別の認証・請求経路です。Subscription allowanceを自動継承せず、environment variableを1つ変えるだけでは保存済みloginを上書きできない場合があります。Claude Codeで/statusを確認し、active providerまたはgatewayを確かめ、providerのusage logでrequestを確認します。

BetterTokenはAnthropic互換のpay-as-you-go APIであり、Claude subscriptionの延長ではありません。別keyとbalanceが必要な場合の選択肢ですが、どのproviderでもGit checkpointとhandoffが先です。

同じモデルのAPI標準価格を比べ、タスク総額とは呼ばない

次の標準text-token価格は2026-10-01に確認しました。単位はすべて100万tokenあたりのUSDです。Anthropicは公式API pricing、BetterTokenはpricing pageを参照しています。

正確なmodel IDAnthropic inputAnthropic outputBetterToken inputBetterToken output
claude-opus-5-5$4.00$20.00$2.72$13.60
claude-sonnet-5$2.00$10.00$1.36$6.80
claude-haiku-4-5-20251001$1.00$5.00$0.68$3.40

この表が示すのは、同じmodel IDの標準input/output価格だけです。次の3つは直接同じではありません。

  • Subscription usage:planに含まれる枠で、plan limitに従う。
  • Usage credits:対象のClaude UI内での従量継続。
  • API usage:別のprovider accountでtokenと機能に応じて請求。

実際のタスク費用はinput/output token、cache read/write、long contextやfast mode、retry、tool call、account ruleで変わります。表示価格が低いだけでは、そのrepository task全体が安くなるとは言えません。

再開時は「continue」ではなく状態handoffを渡す

次のセッションへ、短く検証可能な状態を渡します。

Goal: 完了時に満たす条件
Base: branchとcommit/checkpoint ID
Changes: staged, unstaged, untracked
Validation: 実行した正確なcommandと結果
Known failure: 正確なerrorまたはfailing assertion
Do not touch: 機密または無関係なfile
Next action: 小さくtestableな1手

最初にClaude Codeへcurrent diffを確認させ、次に行う操作を言い直させます。古いsummary、別branch、不完全なgraceful stopを新しい編集の前に発見できます。

よくある質問

Graceful stopは自動でcommitしますか?

しません。公式説明はより安全な停止点を探す試みであり、Git commitの約束ではありません。

タスク完了を保証しますか?

しません。残作業を説明して止まる場合も、一部の変更だけで止まる場合もあります。完了はtestとacceptance criteriaで判断します。

Wrap-upに追加料金はかかりますか?

告知された小さな固定枠はweekly usageから使われます。Usage creditsとAPIは別の従量経路なので、アカウント表示のbilling modeを確認してください。

/rewindはGitの代わりになりますか?

なりません。セッション内の直接編集には便利ですが、公式の除外範囲によりすべてのrepository変更を表せません。

Weekly limitと表示された場合は?

5時間resetでは解消しない可能性があります。Checkpointを保存し、clientが示すlimit typeとreset timeを確認し、追加利用を買う前に本当に止めているcounterを確かめてください。

LLM ワークフローを最適化しませんか?

単一 API でモデルを接続し、キーと AI コストを管理できます。

無料で始める