CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/Claude Code
Claude Code/2026-05-20中級

Claude Code が『テスト全通過』と報告したのに CI で落ちる — 終了コードを正しく拾うための検証設計

ローカルでは『テスト全通過』と報告された変更が、push 後の GitHub Actions で真っ赤になる。原因の多くは Claude Code 側ではなく、シェルとテストランナーの終了コード解釈にあります。実例ベースで切り分け方を整理しました。

Claude Code197テスト3CI4GitHub Actions12Bash4トラブルシューティング37

ローカルで Claude Code に「全テスト通っています」と報告された変更を、勢いに任せて push したら GitHub Actions のジョブが真っ赤になっていた。個人開発で 12 年ほどアプリを作ってきた中で、これは Claude Code 登場以降にいちばん増えた種類の事故だと感じています。

不思議なのは、Claude Code が嘘をついているわけではないことです。ターミナルの記録を遡ると、本人なりには pass: 142 のような出力を確かに読み取っています。落ちているのはたいてい、その手前にある「テストフレームワークから返ってきた終了コードを Bash がどう扱ったか」のレイヤーです。

Vitest / Jest / pytest / Go test を素材に、Claude Code がテスト結果を誤判定してしまう典型パターンを4つに整理し、それぞれに対する仕組みベースの対策を順に書いていきます。2014年から累計5,000万DL超のアプリを個人で運営してきた廣川政樹の現場ノウハウとして読んでいただければ嬉しいです。

起きていること — 「成功と報告された変更が CI で落ちる」3つの兆候

兆候自体はわかりやすいので、まず3つに分けておきます。

ひとつ目は、ローカルの Claude Code は ✅ all tests passed と書いてきたのに、push 直後の CI で同じテストが Fail します。コードは何も変えていないのに環境差で落ちる、というパターンです。

ふたつ目は、Claude Code が走らせたテストの出力をスクロールすると Tests: 1 failed, 141 passed と書いてあるのに、Claude Code 自身は「すべて成功」と要約しているケースです。これはモデルが見ているテキストと、シェルが返した終了コードがずれている状態です。

みっつ目は、Claude Code がテストを走らせている途中で「タイムアウトしました、リカバリーします」と一度報告し、その後で別のテストだけ実行して passed を確認し、最終的に「全テスト通過」と総括してしまうパターン。watch モード絡みで頻発します。

兆候は違いますが、根っこは同じです。Bash の終了コードがテストの真の状態を反映していないか、反映されているのに Claude Code が見ないように作られているかのどちらかです。

原因1: シェルの && チェーンと || true の癖

いちばんよく見かけるのが、Claude Code が組み立てる Bash コマンドが、暗黙のうちにエラーを握り潰す形になっているパターンです。

# Claude Code がよく書いてしまう例
npm install && npm run build && npm test || echo "test failed but continuing"

人間が手で書いたなら「テスト落ちたら止めるよね」と即座に気付くのですが、Claude Code は CI の文脈で || echo|| true を学んでいるので、似たような変形をしれっと出してきます。最終的な終了コードは echo0 になり、Claude Code は「コマンドは成功扱いで返ってきた」と判定します。

切り分けは単純で、テストの直後に必ず終了コードを変数に取り出すように矯正します。

npm test
TEST_EXIT=$?
echo "TEST_EXIT=$TEST_EXIT"
[ "$TEST_EXIT" -eq 0 ] || exit "$TEST_EXIT"

$? を明示的に拾うコードを 1 度書いておくと、Claude Code はそのプロジェクトでは同じスタイルを継承してくれることが多いです。CLAUDE.md に「テスト実行後は必ず TEST_EXIT=$? を確認する」と書いておくと、さらに安定します。

原因2: テストランナー側が exit 0 を返してしまう設定

シェルのほうを締め上げても、テストフレームワーク側が「失敗らしい失敗を返さない」モードで動いていることがあります。これも実は、本人たちにとっては正当な仕様です。

Jest と Vitest には --passWithNoTests というオプションがあり、テストファイルが 0 件のときに緑で抜けます。便利ですが、tests/__obsolete__ のような旧ディレクトリだけが残っていて、新しい場所のテストが拾われていない、という状況で素通りされると気付けません。

pytest にも --exitfirst--maxfail の挙動を変える組み合わせがあり、特に pytest -o "norecursedirs=tests" のような上書きをやってしまうと「テストが 1 件もありません」が成功扱いで返ります。

Go の go test ./... は基本的に厳しめですが、-run フラグで何にもマッチしないパターンを指定するとやはり 0 件成功で返ります。Claude Code がリファクタリングのなかで -run 'TestNewFoo' のようにスコープを絞り、結果として 0 件のテストを走らせて「通った」と判定する場面に何度か遭遇しました。

対策としては、「ローカルで走るテストの最低件数」を明示的に検証する一行を CLAUDE.md に書いてしまうのが手っ取り早いです。

# vitest の例
npx vitest run --reporter=json | tee vitest-report.json
COUNT=$(jq '.numTotalTests' vitest-report.json)
[ "$COUNT" -ge 10 ] || { echo "テスト件数が少なすぎます ($COUNT)"; exit 1; }

10 件という数字に意味はなく、プロジェクトに応じて変えれば構いません。大切なのは「成功なら必ずある件数以上が走っているはず」という前提を、機械が読める形で残しておくことです。

原因3: 並列・watch モードで「途中の成功」だけが Claude Code の目に入る

これは個人開発で React Native のアプリを書いていたときに 2 回はまりました。npx jest --watch を起動したまま Claude Code に作業を任せると、ファイルが変わるたびに Jest が自動的に再実行されます。Claude Code は出力の流れる速度が速いことに引きずられて、

144 passed, 1 failed ... 145 passed (filter changed)

後半の 145 passed だけ を成功の根拠として拾ってしまうことがあります。実際には、フィルター変更によってその 1 件が「次の watch サイクルでは実行されなかった」だけです。

回避策はシンプルで、Claude Code に渡すテスト実行コマンドは原則として watch モードを禁止し、ワンショットで終わるコマンドだけ書く約束にします。

# Watch モードは Claude Code 用ではない
npx jest --watch              # ❌
# 必ずワンショットで終わるコマンドだけ
npx jest --ci --reporters=default --reporters=summary  # ✅

CI フラグを付けると Jest はインタラクティブ機能を抑制し、終了コードもまっすぐ返してくれます。--ci を付けるだけで watch 由来の事故はほぼ消えるので、おすすめです。

原因4: ローカルと CI の環境変数差で条件分岐が変わる

Claude Code がローカルで実行している npm test と、GitHub Actions の npm test が同じスクリプトを叩いていても、結果が違うことがあります。package.jsonscripts.test がこんなふうになっているケースが多いです。

{
  "scripts": {
    "test": "if [ \"$CI\" = \"true\" ]; then vitest run --coverage; else vitest run; fi"
  }
}

CI のほうではカバレッジ計測が走り、vitest.config.tscoverage.thresholds.lines で 80% を切ると Fail します。ローカルでは閾値チェックがないので、テスト本体は全部 pass でも CI だけ落ちる、という状況が生まれます。

これは Claude Code というよりプロジェクト設定の話ですが、Claude Code に「テストを走らせて」と頼むときに CI=true npm test で実行するように躾けておくと、ローカルでの実行が CI に近づきます。CLAUDE.md に書いておく価値のある 1 行です。

# 推奨: ローカルでも CI 相当で実行する
CI=true npm test

防御の組み立て方 — フックと CLAUDE.md でズレを潰す

ここまでの 4 つはどれも、Claude Code 単体で完全に防ぐのは難しい問題です。けれど、3 段階の防御を組み合わせれば、CI で落ちる確率は実用上ゼロに近づきます。私の個人開発リポジトリでは、次の構成に落ち着きました。

第一段階は CLAUDE.md に書く約束ごと。テスト実行コマンドは必ず CI=true を付ける、watch モードは使わない、終了コードを TEST_EXIT=$? で拾う、という 3 つを明文化します。Claude Code は CLAUDE.md を強く参照するので、毎セッション言わなくても守ってくれるようになります。

第二段階は PostToolUse フック。Bash ツールの実行が終わったタイミングで、終了コードが 0 でないなら Claude Code に明示的に伝えるフックを置きます。

// .claude/hooks.jsonc
{
  "PostToolUse": [
    {
      "matcher": "Bash",
      "command": "test \"$CLAUDE_TOOL_EXIT_CODE\" -eq 0 || echo \"⚠️ Bash exit=$CLAUDE_TOOL_EXIT_CODE — テストは失敗です。要約に含めてください\""
    }
  ]
}

このフックを噛ませると、Claude Code は終了コードが 0 でないコマンドを「成功」と要約しづらくなります。要約の段階で「失敗です」という文字列が直前に注入されるので、無視するほうが難しい状態を作れます。

第三段階は CI 側の同じスクリプト。GitHub Actions のワークフローでも、ローカル CLAUDE.md と同じコマンドを CI=true npm test の形で実行します。CI 側でしか起きない事故は、CI 側でも常に検出できる状態を作っておくのが結局いちばん安く、これは Claude Code 時代になってから特に効きが良くなりました。

# .github/workflows/test.yml の該当部分
- name: Run tests with same command as Claude Code
  env:
    CI: "true"
  run: |
    npm test
    TEST_EXIT=$?
    [ "$TEST_EXIT" -eq 0 ] || exit "$TEST_EXIT"

自分のリポでの一回のミスから学ぶ

個人で運営しているアプリのリポでは、Claude Code に「テスト全部通ったから push してください」と任せた直後に CI が真っ赤になり、ストアアップロード前のパイプラインがブロックされたことがあります。原因は、CLAUDE.md にスコープ縮小のための vitest --testPathPattern 'unit/' を書き残していたことでした。ブラックボックステスト群が実は全件スキップされていて、passed: 23 の表示は嘘ではない、けれど「全テスト」ではなかったのです。

このときの教訓を CLAUDE.md に短く書き残しています。

テストを走らせるときは npx vitest run --reporter=json | jq .numTotalTests を必ず叩いて、件数が想定下限を下回らないか確認すること。

たった一行ですが、それ以降同種の事故は起きていません。Claude Code はテキストの指示に驚くほど忠実に従ってくれるので、自分が一度ハマったポイントを「事実上の rule」として書き残しておくと、未来の自分(と Claude Code)が同じ穴に落ちにくくなります。

全体を振り返って — 「成功と報告された」を一段だけ疑う

Claude Code が「テスト全通過」と報告したとき、本当に疑うべきは Claude Code そのものではなく、シェルとテストランナーが返した終了コードです。|| true を排除し、--passWithNoTests のような便利オプションを慎重に扱い、watch モードはローカル人間専用にしておく。これだけで、push 直後に CI が真っ赤になる回数はかなり減ります。

次にテストを Claude Code に任せるときは、まず package.jsonscripts.test を一度開いてみてください。|| true--passWithNoTests が紛れ込んでいたら、それは未来のあなたが踏む地雷です。今のうちに外してしまうと、Claude Code との協働もぐっと安全になります。

同じ問題で時間を奪われた方の参考になれば幸いです。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-06-24
Claude Design 由来のプロトタイプを本番化する — 単一ソース・CI・OGP自動生成の実装
Claude Design で作ったプロトタイプを Claude Code で本番運用に耐える形へ。生成物ランタイムを土台にする判断、ロジックを mixin で分離する構成、取引台帳を単一ソースにするデータ設計、GitHub Actions による自動更新と OGP 画像の実ブラウザ生成まで、実装込みで解説します。
Claude Code2026-05-27
Claude Code Bash ツールで /tmp が Permission denied になる時の対処 — $HOME/repos フォールバックパターン
Claude Code のサンドボックス Bash で git clone が `Permission denied` で止まる症状の原因と、$HOME/repos へ自動退避させる実装パターンを、4サイト並行運用での実体験とともに整理します。
Claude Code2026-05-18
Claude Code の Bash ツールで cd や export が引き継がれない時の対処
Claude Code の Bash ツールで cd や export が次のコマンドに反映されない症状の原因と、実務で使える5つの対処パターンを実例とともに整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →