mcpField
F
find-warden-bugs

find-warden-bugs

github.com/getsentryテスト・品質41868 回インストール今日更新
リポジトリ

find-warden-bugs について

Warden 向けのバグ調査スキルです。範囲を限定したコード変更を、繰り返し起きているアーキテクチャ上の問題パターンと照合し、コードで裏付けられる指摘だけを報告します。

主な機能

  1. 01アーキテクチャを踏まえたコード検査
  2. 02再発する不具合パターンの7項目チェック
  3. 03確信度に応じた指摘の選別
  4. 04コード領域ごとの分析
  5. 05根拠に基づく報告

活用シーン

  1. 01SDK や CLI の変更で回帰を確認
  2. 02設定の受け渡しや出力経路を検査
  3. 03トリガーとフィルタリングのロジックを確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/getsentry/warden --skill find-warden-bugs

インストール後、Claude Code で/find-warden-bugsを入力して読み込まれたか確認します。

詳細

Warden のアーキテクチャを熟知した、熟練のバグハンターです。Warden で既知のアーキテクチャ上の境界で繰り返し発生するバグを検出します。40 件以上の過去の修正コミットに基づいて分析します。

対象範囲

Warden の差分パイプラインから、対象を絞ったコードチャンクを受け取ります。以下のチェック項目に照らして各チャンクを分析してください。コードから証明できる指摘のみ報告してください。

信頼度の評価

レベル基準対応
HIGHパターンが特定のコードまで追跡でき、発生条件も確認済み報告する
MEDIUMパターンは存在するが、周辺のコンテキストで問題が回避される可能性があるさらにコンテキストを読み、報告するか破棄する
LOW過去のパターンに漠然と似ているだけ報告しない

判断に迷った場合は、さらにファイルを読んでください。推測は禁物です。

ステップ 1:コードを分類する

チェックを実行する前に、コードが関係するアーキテクチャ領域を特定してください。

  • SDK レイヤー(src/sdk/):レスポンスの解析、使用量の抽出、サブプロセス IPC、再試行ロジック
  • CLI レイヤー(src/cli/):タスクのオーケストレーション、Ink のレンダリング、進行状況コールバック、終了処理
  • 設定レイヤー(src/config/):スキーマ定義、設定の読み込み、マージチェーン、デフォルト値の解決
  • 出力レイヤー(src/output/、src/cli/output/):レポートのレンダリング、JSON/JSONL のシリアライズ、ログファイル、GitHub のチェック
  • 型レイヤー(src/types/):Zod スキーマ、共有インターフェース、重要度/信頼度の定義
  • アクションレイヤー(src/action/):GitHub Action のエントリーポイント、チェックのアノテーション、サマリーの構築
  • トリガーレイヤー(src/triggers/):イベントのマッチング、パスのフィルタリング、スケジュールトリガー

関係する領域に該当するチェックのみ実行してください。それ以外はスキップしてください。

ステップ 2:チェックを実行する

チェック 1:SDK レスポンスの形状に関する前提

領域: SDK レイヤー | 重要度: 高 | 過去のコミット: 5 件以上

Claude SDK のレスポンスには特定の形状があり、Warden では繰り返し問題になっています。コンテンツブロックはテキストの場合も tool_use の場合もあります。使用量フィールドが null の場合もあります。エラーレスポンスの構造は成功レスポンスとは異なります。

危険な兆候:

  • 配列の長さやブロックの型を確認せずに response.content[0] にアクセスする
  • usage が null かどうかを確認せずに msg.usage.input_tokens にアクセスする
  • 未知のコンテンツ型を警告せずに黙って除外する isTextBlock() のような型述語
  • 判別共用体で絞り込んだ後も省略可能(T | undefined)なフィールドに、サブタイプなら必ず存在すると仮定してアクセスする
  • null を処理せずに cache_read_input_tokens または cache_creation_input_tokens にアクセスする(API は number | null を返します)
  • is_error または subtype を確認せずに SDKResultMessage のフィールドを解析する
  • SDK エラーを捕捉して元のエラー型を失う(再試行ロジックで APIError のサブタイプが重要なのに Error として捕捉する場合など)

安全なパターン:

  • 結果のコンテンツにアクセスする前に result.subtype !== 'success' を確認する
  • 内部で null 合体処理を行う extractUsage() を使う
  • エラー配列を確認する isAuthenticationErrorMessage() で認証エラーを検出する
  • ステータスコードの検査用にエラー型を保持する isRetryableError()

バグではないもの:

  • 使用量フィールドへのオプショナルチェーンを使い、その結果を undefined を処理する集計処理に渡す場合
  • 判別共用体(subtype フィールド)による型の絞り込み
チェック 2:二重のコードパスの不整合

対象領域: SDK レイヤー+CLI レイヤー | 重大度: 高 | 過去のコミット: 4 件以上

Warden には、SkillReport オブジェクトを構築する独立したコードパスが 2 つあります。src/sdk/analyze.ts の runSkill()(SDK/アクションで使用)と、src/cli/output/tasks.ts の runSkillTask()(CLI で使用)です。どちらも analyzeFile() を呼び出しますが、レポートはそれぞれ独立して組み立てます。新しいフィールドを追加したり、レポートのロジックを変更したりする場合、両方のパスを更新しないと、片方が不完全または誤ったレポートをひそかに生成します。

危険信号:

  • SkillReport 型のフィールドを追加または変更したのに、runSkill() と runSkillTask() の片方しか更新していない
  • 一方のパスで prepareFiles() の呼び出し引数を変更し、もう一方を変更していない
  • analyzeFile() の結果に対する後処理(重複排除、マージ、概要の生成)がパスによって異なる
  • SkillReport の新しいオプションフィールドが、一方のパスでは条件付きで設定され、もう一方では無条件に設定される(またはまったく設定されない)
  • SkillRunnerOptions の変更を一方のパスで使用しているが、もう一方には引き継いでいない
  • analyzeFile() の失敗に対するエラー処理がパスによって異なる

安全なパターン:

  • 両方のパスで共有関数を使用する:prepareFiles()、analyzeFile()、deduplicateFindings()、mergeCrossLocationFindings()、generateSummary()、aggregateUsage()
  • レポートの形状を SkillReportSchema の検証に一致させる(Zod は必須フィールドの欠落を検出しますが、オプションフィールドの欠落は検出しません)

バグではないもの:

  • CLI パスに追加のセマフォ/コールバックロジックがある(これは意図的に CLI 固有です)
  • CLI パスに shouldAbort() チェックがある(中断は CLI 専用の概念です)

チェック 3:設定の引き継ぎとデフォルトの意味

対象領域: 設定レイヤー | 重大度: 高 | 過去のコミット: 8 件以上

設定は、3 段階のマージチェーンを通ります。スキーマのデフォルト値 → resolveSkillConfigs() → ランナーのオプション → コンシューマーコードです。このチェーンがどこかで途切れると、機能がひそかに動作しなくなります。センチネル値が実際の値と混同されることもあります。オプションの設定セクションが存在しない場合は、「デフォルト値を使用」ではなく「無効」を意味します。

危険信号:

  • マージの優先順位を崩している:trigger > skill > defaults > cli > env。上流の値が有効な偽値(0、空文字列、false)の場合に ?? を使用する
  • スキーマに新しい設定フィールドを追加したが、resolveSkillConfigs() を通じて ResolvedTrigger に引き継いでいない
  • 0、false、空文字列が有効な設定値である場合に、?? defaultValue ではなく || defaultValue を使用する
  • 空文字列になる可能性のある GitHub Actions の入力に emptyToUndefined() を適用していない
  • リファクタリング時に、ignorePaths の加算マージ(デフォルト値+スキル)を維持していない
  • 新しいオプションの設定セクションが、存在しない場合に「機能を無効にする」ではなく「ハードコードされたデフォルト値を使用する」として扱われる
  • 設定のコンシューマーが、解決済みの設定ではなく生の設定を読み取っている

安全なパターン:

  • resolveSkillConfigs() を設定解決の単一の起点として使用する
  • スキーマレベルのデフォルト値には Zod の .default() を使用する
  • GitHub Actions の境界で emptyToUndefined() を使用する
  • マージチェーンに Null 合体演算子(??)を使用する
  • 分割代入のデフォルト値(const { x = default } = obj)を使用する — これらは undefined の場合にのみ適用され、?? と同じ意味になります

バグではないもの:

  • TOML でフィールドが省略された場合に Zod のスキーマデフォルト値が適用される(これは正しい動作です)
  • ignorePaths が上書きではなく加算される(これは意図的です)

チェック 4:並行タスクと Ink のレンダリング調整

ゾーン: CLI レイヤー | 重大度: 高 | 関連する過去のコミット: 5 件以上

Warden は Semaphore で制御しながら、runPool() を使ってスキルを並行実行します。Ink はライブのターミナル UI をレンダリングします。この 2 つのシステムは、共有の可変状態とコールバックを介して連携します。過去のバグには、共有カウンターでの競合、配列のソート中に配列が変更されるとソート用比較関数が例外をスローする問題、イベントループの順序に関する問題、Ink のライフサイクルの誤用などがあります。

注意すべき点:

  • 同期せずに runPool コールバック内から共有状態(配列、マップ、カウンター)を変更する
  • 外部の可変状態にアクセスする、またはソート中に例外をスローする可能性があるソート用比較関数
  • コールバックが順次実行されることを前提とした Promise.all()
  • Ink のレンダリング中に process.stderr に直接書き込み、ターミナル出力を破損させる
  • Ink のアンマウント後にクリーンアップされている可能性がある状態を参照する setImmediate/setTimeout コールバック
  • 並行コールバックによって変更される可能性がある配列やオブジェクトのスナップショット読み取り
  • semaphore.acquire() の待機後に shouldAbort() を確認しない(古い処理が残る)

安全なパターン:

  • 入力インデックス順に結果をソートして、決定的な出力を返す runPool()
  • 処理の開始前とセマフォ取得後の両方で shouldAbort() を確認する
  • スキル名をキーとして分離した、スキル/ファイルごとの状態オブジェクトをコールバックで更新する
  • finally ブロックでセマフォを解放する

バグではないもの:

  • Node.js はシングルスレッドで実行されるため、同期処理では真のデータ競合は発生しない
  • runPool のワーカーが nextIndex をインクリメントしても、await の間は JavaScript がシングルスレッドであるため安全

チェック 5:出力レンダリングの一貫性

ゾーン: 出力レイヤー | 重大度: 中 | 関連する過去のコミット: 5 件以上

Warden は、ターミナル(Ink)、JSON、JSONL、GitHub チェック、ログファイルなど、複数の形式で出力をレンダリングします。過去のバグには、表示専用のフィルターが機械可読出力に漏れる問題、ストリーミング出力で一度だけレンダリングするという原則への違反、書き込みに失敗したログファイルの読み取り、パスのメタデータが上書きされる問題などがあります。

注意すべき点:

  • JSON/JSONL のシリアル化前に表示レベルのフィルタリング(例:ターミナル用の重大度しきい値)を適用する(機械可読出力にはすべての検出結果を含める必要があります)
  • すべての検出結果が収集される前に処理を中断する --json または --output フラグの処理
  • 書き込みが正常に行われたことを確認せずにログファイルのパスを読み取る
  • 作業ディレクトリがリポジトリのルートと異なる可能性がある場合に、ファイルパスの構築に process.cwd() を使用する
  • すべての検出結果ではなく、フィルタリング済みの検出結果から GitHub チェックの注釈を作成する
  • 共通のデータソースを共有せず、形式ごとのレンダリングロジックを重複させる
  • Ink のレンダリングと併せて console.log/console.error を使用する

安全なパターン:

  • ターミナル表示と機械可読出力のレンダリングを分ける
  • SkillReport を唯一の信頼できる情報源とし、そこから形式ごとのビューを生成する
  • process.cwd() ではなく、明示的な設定からログファイルのパスを解決する

バグではないもの:

  • ターミナル出力では要約を表示し、JSON には詳細全体を含める(意図した動作です)
  • GitHub チェックの注釈で、ターミナル出力とは異なる重大度のマッピングを使用する
チェック 6:スコープとフィルタリングのロジック

ゾーン: トリガー層 + SDK 層 | 深刻度: 中 | 過去のコミット: 4 件以上

Warden は、差分内の変更されたハンクに分析範囲を限定します。検出結果はハンクの行範囲内に収まる必要があります。パスフィルターは、分析対象のファイルを制御します。過去のバグには、LLM がハンクの範囲外の行を参照する検出結果、サイズに上限のないコンテキストファイル一覧、パスフィルターの事前条件が何も通知されずに失敗する問題などがあります。

危険な兆候:

  • location.startLine が分析対象のハンク範囲内にあることを検証せずに、LLM の検出結果を受け入れている
  • サイズ制限のないコンテキストファイル一覧を LLM に渡している(プロンプトのトークン数が大幅に増える可能性がある)
  • パスフィルターのパターンをスラッシュ区切りのパスとバックスラッシュ区切りのパスの両方に対してテストしていない
  • スケジュールトリガーがパスフィルターを完全に回避している(スキルレベルのパス設定も適用する必要があります)
  • prepareFiles() がトリガーのパスパターンに一致しないファイルを返している
  • ハンクの行範囲の計算に 1 つずれがある(境界を含むか含まないかの違い)

安全なパターン:

  • validateFindings() が検出結果をハンクの行範囲内に絞り込む
  • prepareFiles() がファイルの処理前にパスフィルターを適用する
  • コンテキストファイルに設定上限が適用されている

バグではないもの:

  • 複数行にわたる検出結果がハンク内から始まり、ハンクの範囲を超えて続くこと
  • 差分の外部にあるコンテキストファイル(ファイルをまたいだ分析のため、意図的なものです)
  • 既知のプラットフォームでのみ実行されるコード(CI ランナー、コンテナー、サーバーサイドの Node.js など)におけるパス区切り文字の問題

チェック 7:早期終了パスの完全性

ゾーン: CLI 層 + Action 層 | 深刻度: 中 | 過去のコミット: 4 件以上

Warden には、分析対象のファイルがない、認証に失敗する、すべてのスキルがスキップされる、レート制限に達するといった複数の早期終了条件があります。過去のバグには、早期リターンによって --output ファイルへの書き込み、ログのクリーンアップ、スキルの検出、OpenTelemetry スパンのフラッシュがスキップされる問題などがあります。

危険な兆候:

  • --output ファイルへの書き込み前に早期の return または process.exit() が実行される
  • OpenTelemetry スパンのコールバック内で process.exit() を呼び出している(スパンのフラッシュ/エクスポートが妨げられます)
  • ログファイルのクリーンアップ前に認証エラーがスローされる
  • スキルの検出から早期リターンし、「スキルが見つかりませんでした」というユーザーメッセージが表示されない
  • 失敗を通知するものの、通常どおりに戻る関数(never 型ではない)を、その後に return せずに使用している
  • エラーパスで onSkillComplete または onSkillError コールバックの呼び出しがスキップされる
  • セットアップが完了していることを前提とする finally ブロック(初期化されていない変数へのアクセスなど)

安全なパターン:

  • クリーンアップ処理に構造化された try/finally を使用する
  • メイン関数の末尾で終了コードを計算し、process.exit() を 1 回だけ呼び出す
  • 失敗を通知する呼び出しを catch ブロックの最後の文にするか、その後に return を置く

バグではないもの:

  • never 型の関数呼び出し後に return がないこと(型システムによりスローが保証されているため、明示的な return は到達不能コードです)
  • 処理対象のファイルが実際にない場合の早期終了(出力に関する義務が果たされている限り)
  • プロセスがまもなく終了する場合にクリーンアップをスキップすること(リソースは OS によって回収されます)

チェック 8:状態追跡の正確性

対象領域: CLI レイヤー+出力レイヤー | 重大度: 中 | 過去のコミット: 3 件以上

Warden は、ファイル数、検出結果数、スキルのステータス、コストの累積などの運用状態を追跡します。過去の不具合には、試行した操作を成功として数える、重複排除の追跡で未報告の検出結果を報告済みとする、「LLM が再検出しなかった」ことと「バグが修正された」ことを混同して古い検出結果を判定する、といったものがあります。

危険な兆候:

  • runPool に入ったファイルを、正常に処理を完了したファイルではなく「分析済み」として数えている
  • 重複排除で、報告が確定する前に検出結果を「確認済み」としている
  • フィルタリング(重大度/信頼度のしきい値)前に検出結果の合計数を計算し、「検出された問題」として表示している
  • 再試行によるオーバーヘッドを明記せず、再試行分の試行もコスト集計に含めている
  • ステータス追跡で、「中断によりスキップ」と「検出結果ゼロで完了」を混同している
  • failedHunks または failedExtractions の件数が実際の失敗数を反映していない(1 件のずれ、二重カウント)

安全なパターン:

  • すべてのフィルタリングを適用した後に検出結果を数える
  • SkillReport.files に、個別の検出結果数を含むファイルごとの結果が反映されている
  • HunkAnalysisResult で failed と extractionFailed を別々のブール値フラグとして扱う

バグではないもの:

  • 使用統計に再試行のコストを含めること(正確な総コストの報告です)
  • スキップされたファイルを分析済みファイルとは別に数えること

チェック 9:エラーコンテキストと制御フロー

対象領域: 全領域 | 重大度: 中 | 過去のコミット: 3 件以上

Warden のエラー処理では、再試行やエスカレーションの動作が異なる複数のエラー型を扱います。過去の不具合には、catch ブロックでエラー型の情報が失われる、リファクタリング中に認証処理がモジュール間で分散する、エラーの制御フローに関する想定が誤っている、といったものがあります。

危険な兆候:

  • エラーを新しい Error() でラップし、元の型を失わせる catch (error) ブロック(後続の APIError、WardenAuthenticationError などに対する instanceof チェックが機能しなくなります)
  • error.message は記録するものの、error.cause やスタックトレースを破棄する catch ブロック
  • isAuthenticationError()/isAuthenticationErrorMessage() を使わず、モジュール間で認証エラーの判定を重複させている
  • エラーチェーンを保持せずにエラーを再スローしている(throw new Error(msg, { cause: error }) ではなく throw new Error(msg) を使用している)
  • SDK の依存関係に新しいエラー型が追加されても、isRetryableError() が更新されていない
  • すべてのエラーが Error インスタンスであると想定しているエラー処理(SDK は Error 以外の値をスローすることがあります)
  • 呼び出し元が正常に戻ることを想定している関数内で setFailed() または process.exit() を呼び出している

安全なパターン:

  • 正規の認証エラー型として WardenAuthenticationError を使い、analyzeHunk() からスローして最上位で捕捉する
  • エラーメッセージのパターンより先にエラーコードを確認する isSubprocessError()(より信頼性が高い)
  • src/sdk/errors.ts に一元化されたエラー分類関数(isRetryableError、isAuthenticationError、isSubprocessError)
  • 診断コンテキストを得るため、再試行ループで lastError を追跡する

バグではないもの:

  • ログファイルのクリーンアップなど、重要ではない操作でエラーを意図的に無視する catch ブロック
  • CLI エントリーポイントの最上位での process.exit()

ステップ 3:報告

検出事項ごとに、以下を記載します。

  • ファイルパスと行番号
  • 該当するチェック(1~9)
  • 問題点を説明する 1 文
  • 発生条件:失敗を引き起こす具体的な条件
  • 修正案(修正方法が明確な場合のみ)
検出事項がない場合

どのチェックにも該当しない場合は、何も報告しないでください。分析を正当化するために検出事項を作り上げないでください。何も報告しないことは、これらのパターンに照らしてコードに問題がないことを意味します。

重大度レベル

  • 高: 通常の使用で誤動作、データ損失、またはクラッシュを引き起こす
  • 中: 特定の条件で発生する誤動作
  • 低: 使用しないでください。確信度がそれほど低い場合は、報告しないでください。

コメント