investigating-ci-failures について
このスキルは、CI ログとジョブ履歴を使ってテスト失敗を調査する手順を示します。CI を再実行せずに、PR 起因、トランク起因、フレークの区別と関連コミットの特定を支援します。
主な機能
- 01pytest の失敗を指紋で分類
- 02PR・トランク・フレークを区別
- 03ジョブ履歴からコミット境界を特定
- 04修正コミットと関連 PR を確認
- 05データの鮮度と対象範囲の制約を考慮
活用シーン
- 01個別のテスト失敗を調査
- 02トランクの失敗開始と修正時点を特定
- 03複数ブランチの失敗がトランク起因か確認
- 04失敗がフレークかどうかを評価
MCP 対応
全体の失敗分類には `engineering-analytics-broken-tests` を使い、必要に応じて失敗ログやフレークテストの MCP ツールを呼び出します。調査では `engineering_analytics_ci_failures` と `engineering_analytics_ci_job_history` のウェアハウスビューも参照します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ posthog/ ai-plugin --skill investigating-ci-failuresインストール後、Claude Code で/investigating-ci-failuresを入力して読み込まれたか確認します。
同梱ファイルreferences/investigation-queries.md
詳細
CI の失敗を調査する
目的は、失敗したテストを1つ、または失敗した実行を1つ取り上げ、開発者が対処できる判定にたどり着くことです。つまり、自分の変更によるもの/trunk 起因/不安定 のいずれかを判定し、trunk 起因の場合は、原因となった SHA、その作成者、PR、および修正がすでに取り込まれているかを特定します。以下はすべて、すでに存在するデータから導き出せます。回答するために CI を再実行する必要はありません。
基盤となるのは、2つのデータウェアハウスビューです(どちらも非マテリアライズドで、常に最新の状態です。自由にクエリしてください)。
engineering_analytics_ci_failures— CI ログ内のFAILED <nodeid>行ごとに1行を記録します。事前にフィンガープリント化されており(fingerprint= テスト ID + 数字/16進数を正規化したエラー)、fingerprintでグループ化すると、初回/最終確認時刻、発生回数、ブランチの分布を取得できます。engineering_analytics_ci_job_history—conclusionとコミットの帰属情報を含む、ジョブの試行ごとに1行を記録します。帰属情報にはhead_sha、commit_author_name、commit_message、commit_pr_number(コミットを生み出したマージ済み PR。master の push 実行に帰属させられる唯一の PR)が含まれます。成功実行はここに記録されます。ログにあるのは失敗のみなので、「いつ失敗し、いつ再び成功したか」という質問には、ログではなく必ずここから答えてください。
各ステップですぐに使える SQL は、references/investigation-queries.md にあります。
まず全体を見る:今、何が壊れているか
特定のテストがまだわかっていない段階で、「今、どの CI の失敗に注目すべきか」を調べる場合、engineering-analytics-broken-tests MCP ツールを使うと、以下の分類をすべての未解決の失敗に対して一括で行えます。直近2日間の失敗をフィンガープリントごとにグループ化し、それぞれに breaking_master/blocking_merge_queue/novel_burst/potentially_resolved/flaky/pr_only のラベルを付け、緊急度の高い順に並べます。さらに、breaking_master_jobs(最新の実行が失敗しているデフォルトブランチのジョブ)も表示します。まずこれをトリアージの入口として使い、その後、以下の個別の失敗に関するワークフローに進んで原因を特定してください。これは手作業でのフィンガープリント化を自動化したものです。手動クエリは、特定の失敗を境界と作成者に結び付けるために引き続き使用します。
blocking_merge_queue は、単一ブランチの失敗に見えて実際はそうではないため、以下の手動の表では扱っていない唯一のパターンです。マージキューは、ゲートブランチ(trunk-merge/pr-<n>/…)上で完全なテストスイートを実行します。このブランチには master、その PR、および影響を受けるターゲットが重複する、その前にキューに入ったすべての PR が含まれます(実際にはキューの大半が該当します)。したがって、その失敗は PR 自身の CI をすでに通過したコミットで発生しており、通常はその PR の不具合ではなく、間に取り込まれた変更またはキューに入った変更との競合か、キュー内の別の PR 自身の不具合です。これは「マージを止めたもの」と捉え、ゲートブランチで実行された内容を列挙し(クエリ8)、PR だけを見るのではなく trunk との差分を確認してください。
4つの失敗パターン
まず失敗の指紋を確認し(参考資料のクエリ 1)、次にそのパターンを読み取ります。分類は次の 3 列から判断できます。
| パターン | 読み取り | 次のステップ |
|---|---|---|
| 1 ブランチ、ウィンドウを問わない | その PR 固有の問題 | その失敗行を確認して完了 |
1 つの trunk-merge/pr-<n>/… ゲートブランチ | キュー内の別 PR、またはすでにマージ済み | クエリ 8 を実行し、ゲートブランチと trunk を比較する |
| 複数のブランチ、短時間に集中、master に到達 | trunk の破損(master が赤、または赤かった) | 境界クエリ → 原因の特定(以下を参照) |
| 複数のブランチ、数日〜数週間にわたり散発的 | 不安定なテスト | engineering-analytics-flaky-tests で裏付けを取る |
ブランチをまたぐ失敗が trunk を意味する理由:PR の CI は、PR を master とマージした状態 で実行されるため、問題のある master のコミットは、同時に実行されているすべての PR を失敗させます。無関係な複数のブランチで短時間に失敗が現れるのは、それらの PR のコードではなく、master とのマージによる破損を示す特徴です。質問者の PR に原因がない場合は、そのことを明確に伝えてください。通常、それが回答の中で最も価値のある一文です。
trunk の破損 → 原因の特定
境界クエリ(クエリ 2)を実行します。失敗したジョブについて、master のみを対象にした履歴を created_at の順に並べます。パターンはそのまま読み取れます。
... success success | failure failure ... failure | success ...
^ first red = the culprit row ^ first green = the fix row
原因となった行にはすべての情報が含まれています。head_sha、commit_author_name、変更内容を示す commit_message、commit_pr_number です。最初に成功した行からも同じ方法で修正を特定できます。誰かを名指しする前に、確度を確認してください。原因となったコミットが失敗箇所に影響している可能性はありますか(メッセージ/PR の差分と、失敗したテストのモジュールを比較してください)。境界が無関係なコミットを指している場合は、シャーディングまたはタイミングのノイズです。断定する前に対象期間を広げ、隣接するコミットを確認してください。
続いて、ci_failures の失敗期間が一致することを確認します(原因となったコミットのマージ直後に first_seen があり、PR キューが解消された後、修正の少し後に last_seen がある状態です)。一致しない場合は、同じテストで別々の問題を見ていることになります。
不安定なテスト → 推測せず裏付けを取る
「不安定なテスト」と言う前に、関連する再試行を確認してください。 1 回の実行だけでは、不安定な失敗と決定的な失敗を区別できません。また、ci_job_history にはブランチごとのすべての試行の conclusion がすでに含まれています。ブランチの実行がすべて失敗しているなら、見つけるべき不安定なテストはありません。再実行を提案しても、入力は何も変わらないため、実行を無駄にします。
散発的なパターンだけでは兆候にすぎず、証明にはなりません。engineering-analytics-flaky-tests MCP ツールは、テストごとの CI スパン(再実行で成功したシグナル。同じジョブで失敗した後、再試行で成功したテスト)を読み取り、対応範囲内ではより強いシグナルを提供します。カウントのみで、割合は使用しません。エミッターの継続時間しきい値を下回る成功した実行は記録されないため、正当な分母が存在しません。
すべての回答に必ず反映する注意事項
- ログに記録されるのは失敗のみです。
ci_failuresには正常終了のベースラインがありません。フィンガープリントがないことは、弱い根拠にしかなりません(ジョブが単に実行されなかった可能性があります)。正常終了の記録はci_job_historyにのみあります。 - フィンガープリントの対象は pytest のみです(v1)。 Jest/playwright/cargo の失敗は生の失敗ログには表示されますが、
ci_failuresには含まれません。この場合は、MCP ツールengineering-analytics-ci-failure-logs(PR スコープ)/engineering-analytics-run-failure-logs(実行スコープ)を使って、生の失敗ログを確認してください。実行情報を取得する際は、run_idとともにci_engineを、またはlatest_run_idとともにlatest_ci_engineを渡してください。エンジンが null の場合は、過去の由来が不明であることを意味します。整数 ID だけで既知のエンジンと結合しないでください。 - テストの実行前に失敗したジョブは、テストレベルのすべての情報源から見えません。
FAILED行がなければ、フィンガープリントもスパンもなく、flaky-tests ツールが読み取れるものもありません。失敗は実際に起きていますが、テストレベルの回答は「問題なし」ではなく、何もありません。セットアップ、docker、runner の失敗はジョブの結論(クエリ 7)でのみ確認でき、正常終了も記録されるため、ここで挙げた情報源の中で正直な失敗率を算出できるのもこの方法だけです。 - キャッシュを原因とする前に
runs-onを確認してください。depot-*ランナーで実行されるジョブは、actions/cacheを Depot Cache に対して解決します。GitHub Actions のキャッシュ API からは確認できないため、そこで結果が空でも、退避された証拠にはなりません。実際に要求されたキーは、ジョブ自身のCache not found for input keys:行で確認してください。いずれのステップも、結果にかかわらず正常終了になります。actions/cache/restoreはキャッシュが見つからなくても成功し、actions/cache/saveの失敗は警告にすぎないため、正常終了した生成ジョブが何も保存していない可能性もあります。 - 情報源によって最新性は異なります。 ログはほぼリアルタイムで配信されますが、ウェアハウスの jobs/runs テーブルは webhook 同期を経由するため、反映が遅れることがあります。インシデント対応中は、まず
ci_failuresを参照し、境界を信頼する前にウェアハウスのmax(created_at)を確認してください(クエリ 5)。古いウェアハウスのデータから境界を計算すると、誤ったコミットが示されます。 - 実行の
conclusionは、workflow_runwebhook の処理が完了するまで古いままの場合があります(SPEC §7)。 直近の「失敗なし」の履歴は慎重に扱ってください。 - 再試行:
run_attempt > 1の行は、同じジョブの再実行です。2 回目の試行で解消する失敗はフレークの兆候です。5 回目以降も失敗する場合は、決定論的な失敗です。 - リバート: リバートは、新たな初回の正常終了(または初回の失敗)コミットとして現れます。その
commit_pr_numberはリバートした PR を指すため、元の PR ではなくリバートに帰属します。 - ログのクエリには必ず時間範囲を指定してください。 失敗ログのストリームは大きいため、範囲を指定しない検索は読み取り上限に達します。ほとんどの調査では、14 日間あれば十分です。
- ウェアハウス側の対応するクエリも組み合わせてください。
created_atだけで期間を絞ったci_job_historyクエリは、jobs 全体のスキャンを引き起こします。解析済みタイムスタンプは計算列であり、parquet スキャンで対象を絞り込めないためです。スキャン対象を減らすため、正確なcreated_atの条件と合わせて、期間より 1 日前の日付を指定する粗いcreated_at_raw >= '<YYYY-MM-DD>'の文字列条件を追加してください。正確なフィルターには引き続きcreated_atを使います。
用途の選び方
| 質問 | 使用するもの |
|---|---|
| 「現在、CI で何が壊れていますか?」 | engineering-analytics-broken-tests MCP ツール(トリアージ済み、分類済み) |
| 同じ内容をターミナルから確認する場合 | hogli ci:insights — これらのエンドポイントを、チェックアウトしたリポジトリに限定して使用します |
| 「自分の PR の CI が失敗したのはなぜですか?」 | engineering-analytics-ci-failure-logs MCP ツール(PR に限定、グループ化済み) |
| 「誰が master を壊しましたか/X はいつから始まりましたか?」 | 2 つのビューと、上記のワークフロー |
| 「X は不安定ですか?」 | ci_failures と flaky-tests ツールから傾向を把握します |
| 「このセットアップ/インフラストラクチャの障害はよくありますか?」 | ジョブの結論、クエリ 7(該当するテスト行は存在しません) |
| 「現在 master で何が失敗していますか?」 | broken-tests ツールの breaking_master 行と breaking_master_jobs |
| 「CI が遅い/コストが高い/PR が滞っていますか?」 | diagnosing-ci-and-merge-bottlenecks スキル |
| 「これをダッシュボード/インサイトとして保存するには?」 | turning-engineering-analytics-into-insights スキル |
出力の期待事項
まず、判定と、責任がないこと/原因を平易な言葉で示します(「あなたの PR が原因ではありません — #68727 により 08:01 から 09:58 UTC まで master が壊れていました。#68855 で修正されました」)。続いて、境界の行、フィンガープリントの時間帯、発生件数/ブランチ数といった根拠を示します。作成者の名前は事実として記載します(原因となったコミットの作成者です)。非難するような書き方は避けてください。コミットメッセージと PR のリンクを見れば、読者が変更の妥当性を判断できます。また、「原因」となったものの半数ほどは、モック化されていないテスト依存関係がある、妥当な変更でした。