--- name: triage-investigation description: Coverity の警告1件(または同じ原因の数件)が本物のバグか誤検知かを、警告経路を呼び出し元や定義までコードでたどって調べ、判定(true_bug / false_positive / intentional / undetermined)・確信度・推奨(修正か逸脱か)と、根拠のファイルと行を返す。「CID 12345(NULL_RETURNS、src/foo.c の 120 行)の警告が本物か調べて」「この FORWARD_NULL は誤検知?」「issue-20001.json の警告を調べて、結果をこのファイルに書いて」のように、警告の真偽・確信度・推奨を求められたら、答える前に必ず使う。警告経路と前後のコードを ★ 付きで見やすく出すスクリプトもある。 allowed-tools: ["shell(uv run:*)"] --- # 警告の真偽の調べ方 Coverity の警告経路は「こう動けば問題が起きる」という**仮説**です。仮説の各段階がコードで本当に成り立つかを、呼び出し元や定義までたどって1つずつ確かめ、判定・確信度・推奨を決めます。 この手順は調べるだけです。コードを直さず、Coverity に何も書き込みません。動きを試すためにビルドするときも、読むコードのフォルダの中には一時のファイルを作らず、一時フォルダで試します。 ## 頼まれ方で変わること 手順は同じで、受け取り方と返し方だけが違います。 | | チャットで直接頼まれたとき | 頼みの文や指示ファイルで、入力と出力の場所を示されたとき | |---|---|---| | 警告の情報 | 示されたもの。足りなければ聞く(下の「警告の情報を集める」) | 示されたファイルと文。聞かない。足りないことは結果に書き、確信度を下げる | | 読むコード | 利用者の作業中のファイル(読むだけ) | 示された「調べるコード」のフォルダ(読むだけ) | | 返し方 | 下の「返す形」でチャットに返す。頼まれない限りファイルを作らない | 示されたファイルに、示された形で書く。返事も示された形にする | | 進み具合 | 書かない | 進み具合のファイルを求められたときだけ、示された形で節目ごとに上書きする(書けなくても調査は止めない) | ## 基本の決まり - 根拠は、**実際に読んだ**コードの場所(リポジトリの一番上からの相対パスと行)で示す。読んでいない場所や推測の行は書かない。 - 「たぶん」「ふつうは」「〜と思われる」で決めない。確かめられないことは、確かめられないと書き、確信度を下げる。 - コードと知識・説明が食い違うときは、コードを優先する。 ## 警告の情報を集める 要るもの:CID、チェッカー、ファイルと行(関数)、警告経路(イベントの並びと ★ の場所)、チェッカーの説明。道具で取るときは、ストリームの名前も要ることがある。 1. 頼みに書かれたもの、渡されたファイル(`issue-.json` など)を使う。 2. 警告経路がなく、Coverity から警告経路とチェッカーの説明を取れる道具が使えるなら、それで取る(ファイルに書かせず、結果を会話に返させる。`main` が真のイベントが ★)。 3. それでも足りないものは、チャットなら頼んだ人に聞く。推測で補わない。警告経路が取れないときは、警告の場所と、チェッカーの説明から考えられる経路で調べ、そのことを結果に書く。 4. Coverity のパスが手元のパスと違うときは、末尾が合うファイルを探して読み、そう対応づけたことを結果に書く。 ### プロジェクトの知識 リポジトリに `.coverity-triage/knowledge.md` があれば(頼みで別の場所を示されたらそこ)、調べる前に読む。人が確かめて書いた、このプロジェクトの事実です。 - 当てはまる知識(戻らない関数、呼び出し元が保証している前提など)は判断に使い、調べた経過(rationale)に「プロジェクトの知識:〇〇」と書く。できれば、その知識もコードで確かめる。 - 知識とコードが食い違えば、コードを優先し、食い違いを調べた経過に書く。 - 「## 推奨の方針(修正か逸脱か)」の節は、下の推奨の決め方より優先する。 ## 警告経路と前後のコードを出す(スクリプト) コマンドを使えるときは、次を**動かして**(中身を読む必要はない)、出力の JSON の `text` を読みます。読むだけで、ファイルを作りません。 ``` uv run --native-tls --quiet "<このスキルのフォルダ>/scripts/show_path.py" show --repo "<コードを読むフォルダ>" --issue-file "" ``` - 警告経路のファイルがないとき:`--file "<ファイル>" --line <行>` で ★ の場所だけを出す。イベントが分かれば、`--event "<ファイル>:<行>:<タグ>:<説明>"` を経路の順に並べ、★ の番号を `--main <番号>` で示す(省くと `--line` の行か最後のイベント)。CID とチェッカーは `--cid`・`--checker` で添える。 - Coverity から警告経路を取れる道具がファイルに書いた `issue-.json` は、そのまま `--issue-file` に渡す(パスは Coverity のままでよい。行の番号のないイベントは、コードなしで経路に並ぶ)。取れなかった CID のファイル(`error` があるもの)は `ok: false` になる。 - グループなら `--issue-file` を CID の数だけ並べる。前後の行数は `--context`(既定 20)。経路が長いと、すべてのイベントが見えるよう、スクリプトが前後の行数を減らす(`notes` に出る)。足りない場所は、ファイルを直接読み足す。 - `notes`(パスを対応づけたこと)と `drift_hints`(ファイルがない、行数が足りない、関数が見つからない)を、下の手順 ⑥ に使う。`files` には各ファイルの文字コードと改行がある。 - `ok: false` なら `error` と `problems` を読み、引数を直して動かし直す。 - コマンドを使えないとき(ほかのエージェントから頼まれた作業など)は、警告経路の各行の前後をファイルで直接読み、同じ手順を最後まで行う。 ## 手順 次のチェックリストを写して、進めながら印を付けます。 ``` 調査の進み具合: - [ ] ① チェッカーの見方を確かめた - [ ] ② 警告経路を順に読んだ(各イベントの前後 20 行。★ が問題の起きる場所) - [ ] ③ 経路が成り立つための条件を書き出した - [ ] ④ 条件を1つずつ、呼び出し元・定義までたどって確かめた - [ ] ⑤ 判定・確信度・推奨を決めた - [ ] ⑥ 最新のコードで直っていないか、コードのずれがないかを確かめた ``` 1. **チェッカーの見方**:チェッカーごとの見方をまとめた資料(スキルなど)が使えれば、チェッカー名に合う項目を読む。なければ、Coverity のチェッカーの説明と、[references/criteria.md](references/criteria.md) の「チェッカーの見方の要点」を使う。 2. **警告経路を順に読む**:スクリプトの出力か、ファイルの各イベントの前後(目安 ±20 行)を、経路の順に読む。各イベントの説明(「NULL を返しうる」など)が、その行のコードと合うかも見る。 3. **条件を書き出す**:その経路を通るのに要る条件(分岐の条件、引数の値、戻り値、ループの回数、マクロの値)を挙げる。 4. **条件を1つずつ確かめる**:値の出どころを、関数名・マクロ名・変数名で検索して、呼び出し元と定義までたどる。 - すべての呼び出し元が条件を満たさない(例:どの呼び出し元も NULL を除いてから渡す)なら、経路は成り立たない。 - 1つでも条件を満たす呼び出し元があれば、経路は成り立ちうる。 - 呼び出し元をすべて確かめてどれも満たさないときは、「宣言がその値を禁じていない」「これから呼び出し元が増えるかもしれない」を本物のバグの理由にしない。 - 呼び出し元がリポジトリにない(公開の API、コールバック、割り込み)ときは、そのことを書き、推定として扱う。 5. **判定・確信度・推奨を決める**:下の表で決める。迷うときの細かい基準と例は [references/criteria.md](references/criteria.md)。 6. **最新のコードとずれ**: - 最新のコード(直すフォルダを示されていればそこ、チャットなら作業中のファイル)で同じ場所を読み、もう直っていないか確かめる。直っていれば「最新で直っている」(`fix.already_fixed_on_latest` が真)と、その場所を書く。 - 読んだコードが Coverity の解析したリビジョンと違うかもしれないとき(解析したリビジョンが分からない、作業中のファイルを読んだ)は、経路の各行がイベントの説明と合うかを確かめる。合わなければ `detected` とずれの中身、合えば `none`、確かめきれなければ `unknown`。ずれがあれば確信度は低。 グループ(同じ原因らしい数件)のときは、まず代表の CID を調べ、残りが**同じ理由**で同じ結論になるかを確かめる。理由が違う CID は外し(`group_excluded_cids`)、個別に調べ直すよう書く。迷うときも外す。全部は外さない。 ## 判定・確信度・推奨 | 判定 | いつ | |---|---| | `true_bug`(本物のバグ) | 経路が成り立ち、今の呼び出し元のどれかで、実際に問題が起きる | | `false_positive`(誤検知) | 経路が成り立たない(Coverity が見落とした条件がある)。呼び出し元がすべて条件を満たさないときも、関数の中だけなら経路が成り立つとしても、これにする | | `intentional`(意図的) | 経路は成り立つが、設計どおりの動きで問題にならない(意図したフォールスルー、構成で消えるコードなど) | | `undetermined`(判定できない) | 実行時の値や外部の仕様によるため、コードからは決められない | | 確信度 | 基準 | |---|---| | `high`(高) | 警告経路をコードで最後まで追え、根拠がすべてコードにある | | `medium`(中) | 根拠の一部に推定がある(呼び出し元がリポジトリにない、マクロの展開を推定した など) | | `low`(低) | コードから決められない前提による。またはリビジョンのずれがある | 確信度の理由(`confidence_reason`)には、どの基準に当たるかを具体的に書く(例「呼び出し元 3 か所すべてで NULL を除いていることをコードで確かめた」)。 推奨(知識に「推奨の方針」があれば、そちらが先): - `true_bug` → **修正**(`fix`)。 - `false_positive` / `intentional` → どんな状況でも問題にならないことを、**条件や但し書きを付けずに言い切れる**ときだけ**逸脱**(`deviation`)。言い切れなければ修正。直すのが小さく安全で、コードの意図も分かりやすくなるなら、修正でもよい。 - `undetermined` → 修正のリスクが小さければ修正、大きければ逸脱。確信度は低。 「言い切れるか」の確かめ方:問題にならない理由を、「誤検知。」「意図的。」「本物の不具合。」で始まる 200 字以内の1〜2文にし、「〜の場合に限る」「〜を確かめた場合は」のような条件なしで書けるか試す。書けなければ、推奨を修正にする。 ## 返す形 **チャットのとき**は、次の順で返す(値は日本語の名前と英語の値の両方を書く)。 ``` 判定:誤検知(false_positive) 見立て:get_buf() を呼ぶ 2 か所とも NULL を除いてから使う 確信度:高(high)— 呼び出し元 2 か所をすべてコードで確かめた 推奨:逸脱(deviation)— どんな状況でも NULL を参照しないと言い切れる 根拠: - src/io/reader.c:120 — read_all() が get_buf() の戻り値の NULL を除いている - src/io/writer.c:88 — write_all() も同じく NULL を除いている 調べた経過:1. … 2. … 3. … リビジョンのずれ:なし(none) ``` - 見立て(`summary`)は、判断の決め手を 40 字ほどの1行で。人はまずここだけを読むので、ここだけで判断できるように書く。 - 調べた経過(`rationale`)は、経路のどの段階をどう確かめたかを順に書く。 - 最新で直っているとき、知識を使ったとき、グループで外した CID があるときは、そのことも書く。 - 修正の差分や逸脱コメントの文は、頼まれたときだけ作る(それぞれの手順があれば、それに従う)。 **ファイルに書くよう頼まれたとき**は、示された形と場所に書く。`result.json` の形なら、調査が受け持つ項目は次のとおり。ほかの項目(修正の中身、逸脱コメント)は、それぞれの仕事の決まりで作る。 | 項目 | 中身 | |---|---| | `verdict.judgement` / `verdict.summary` / `verdict.rationale` | 判定、見立て、調べた経過 | | `verdict.evidence` | `[{"file": "src/a.c", "line": 120, "note": "ここで NULL を除いている"}]` | | `confidence` / `confidence_reason` | 確信度とその理由 | | `recommendation` | `fix` か `deviation` | | `revision_drift` | `{"status": "none / detected / unknown", "detail": "ずれの中身"}` | | `fix.already_fixed_on_latest` | 最新で直っていれば真 | | `group_excluded_cids` | グループで外した CID(グループのときだけ) | 書いたら、示された形で返事をする(形の指示がなければ「: <推奨>(確信度 <高/中/低>)— <見立て>」の1行)。