verify-with-controls について
verify-with-controlsは、テスト結果や不在、成功、停止状態を報告する前に、根拠を確かめるエンジニアやエージェント向けのレビュー用スキルです。もっともらしい数値やログを信頼するのではなく、プローブやチェッカー自体を検証します。
主な機能
- 01ゼロ件には根拠または明示的なエラー
- 02不在の判断前にポジティブコントロール
- 03異常値を別の方法で再検証
- 04ログだけでなく事後条件を確認
- 05報告直前に再測定
- 06ネガティブコントロールでチェッカーを検証
活用シーン
- 01検索結果ゼロが正しいか確認
- 02デプロイや変更の反映を検証
- 03アラート送信前に再確認
- 04CIゲートが失敗することをテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vivswan/ skills --skill verify-with-controlsインストール後、Claude Code で/verify-with-controlsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.mdREADME.md.codex-plugin/plugin.jsonagents/openai.yaml詳細
検証には統制を
読み取り結果は、発見そのものではありません。「一致なし」(ゼロ)、「チェックの半分が消えた」(警報)、「作業が反映された」(成功の主張)、「何も実行されていない」(静止状態)を主張する前に、読み取り結果が統制を通過していなければなりません。具体的には、根拠を添えること、ゼロのたびに陽性対照を行うこと、計測器を疑うこと、事後条件を確認すること、送信時点で再測定すること、そして失敗するところを実際に確認したチェッカーを使うことです。
本番環境への反映寸前だった誤った結論から導き出した、6つのルールです。
| # | ルール | 防ぐ失敗 |
|---|---|---|
| 1 | 根拠かエラーを返し、単なるゼロは返さない | 「存在しない」と「調べられなかった」の両方に同じ 0 を返す |
| 2 | ゼロを信頼する前に陽性対照を行う | 見えていない計測器が、間違った質問に対して正直に答える |
| 3 | まず計測器を疑う | もっともらしい数値が、違うものを測定している |
| 4 | 真実を示すのは事後条件 | 状態が変化していないのに成功ログが記録される |
| 5 | 様子を見るには2回観測し、送信前に再測定する | 報告内容と、その対象となる状態が競合する |
| 6 | チェッカーには陰性対照を行う | 赤になれないゲート |
1. 根拠かエラーを返し、単なるゼロは返さない
「存在しない」と「調べられなかった」の両方に 0 を返すプローブは、設計上すでに壊れています。出力をどれだけ注意深く読んでも直りません。2つの意味が同じバイトとして届くからです。
$ git diff --numstat origin/main...HEAD -- src/fetaure/
$ # empty, exit 0: reads as "this branch touched nothing there"
文字を1つ入れ替えるだけで、パススペックは一度も存在したことのないものに一致します。「そのパスには変更がない」と「間違った場所を調べた」は、同じ空の正常な読み取り結果になります。この形のゼロが続いたことで、実際に反映された作業が欠落していると報告されかけました。
適切なプローブは、ゼロを信頼する前に、質問に答えられる状態だったことを証明し、取得できなかった読み取りにはエラーを返します。測定で読み取るのと同じエンドポイントに対して、パススペックが有効か検証してください。この文には、3つの落とし穴があります。
- 3点リーダーの diff が比較するのは、指定された2つの先端ではなく MERGE BASE と HEAD です
- 検証が失敗しても終了コード 0 なら、単なるゼロと変わりません
- パイプが返すのは最後のコマンドのステータスだけです。そのため
pipefailを設定して、失敗したls-treeがもう一方のエンドポイントでの一致に隠れないようにします
$ set -o pipefail
$ base=$(git merge-base origin/main HEAD) &&
{ git ls-tree -r --name-only "$base" -- src/fetaure/ &&
git ls-tree -r --name-only HEAD -- src/fetaure/; } | grep . ||
{ echo "ERROR: no trustworthy reading (instrument error above, or pathspec at neither endpoint)" >&2; exit 1; }
ERROR: no trustworthy reading (instrument error above, or pathspec at neither endpoint) # and exit 1
壊れたゼロは、大きな非ゼロのエラーになります。いずれかのエンドポイントに存在するパススペック(ブランチによって完全に削除されたものも含む)なら、同じプローブが一致したパスを出力して終了コード 0 を返します。つまり、成功時にも根拠を示し、そのうえで初めて空の diff を読み取り結果として扱います。非ゼロの読み取り結果も、件数だけでなく、一致した行を同じように報告します。
帰結として、動作しているプローブを絞り込んだバリエーションは、どれも新たなプローブです。結果が空でも信頼する前に、必ず一致するはずのケースで、絞り込んだ形式が空でない結果を返すことを証明してください。動作したばかりのコマンドを絞り込んで、あるパス配下の作業について stash を調べた例です。
$ git stash show --numstat 'stash@{0}' # works: prints the stash's files
$ files=$(git stash show --numstat 'stash@{0}' -- docs/ 2>/dev/null)
$ echo "${files:-nothing under docs/}"
nothing under docs/
stash には docs/ 配下の作業が確かに含まれていました。git stash show はパススペックを受け取らないため、絞り込んだコマンドは何も測定していません。エラーは破棄された stderr に出力され、取得された stdout はどちらの場合も空でした。
これを明らかにする対照方法は、stash に必ず含まれているパスに対して絞り込んだ形式を実行し、空の結果が何かを意味するものとして扱う前に、そこでの読み取り結果も空になることを確認することです。
2. 陽性対照:必ず非ゼロになる読み取りを1回行う
ゼロや「存在しない」という読み取り結果を信頼する前に、同じ計測器で、必ず非ゼロになる読み取りを1回行ってください。何もない世界と、見えていない計測器は、同じゼロを返します。両者を区別できるのは対照だけです。
間違ったリポジトリルートがハードコードされた反映チェッカーは、反映済みの2つのファイルを DROPPED と報告しました。取得された読み取り結果はすべて内部的に整合しており、間違った質問に対する正直な答えでした。プローブ対象のツリーには、そのファイルが存在する可能性がなかったのです。
$ landed-check docs/new-guide.md # 0 - alarming reading, do not report yet
$ landed-check README.md # control: known present, must read non-zero
0 # it reads zero -> the instrument is blind
コマンドを1つ追加するだけで、「ファイルが消えた」を「チェッカーからは見えない」に変えられます。警報を出す前に確認してください。
3. 計測器のほうが壊れている可能性はありませんか?
警告を要する読み取り値を報告する前に、別の方法でもう一度導き出してください。計測器に不具合があると、自信を持てるもっともらしい誤った数値が出ます。そして、もっともらしい数値を見ても、壊れているとは分かりません。
$ gh pr checks 123 | tr -s ' ' '\t' | awk -F'\t' '{print $2}' | grep -c pass
10 # of 14 known checks - four "missing"?
名前にスペースが含まれていると、余分なフィールドに分割されます。そのため、すべての行でフィールド 2 がステータス列だったわけではありません。14 件中 10 件という数値は、別のものを測っているという点ではもっともらしい値です。2 つ目の導出では解析を完全に省き、結果が一致しません。
$ gh pr checks 123 --json state --jq 'map(.state) | group_by(.) | map({(.[0]): length})'
[{"SUCCESS":14}]
2 つの導出が一致しない場合は、現実より先に計測器を疑い、一致するまで読み取り値を報告に含めないでください。
4. ログは近似値、事後条件が真実
終了コードで重大な失敗を検出できます。出力の判定で、ツールが独自の失敗メッセージを出力しながら 0 で終了するような、表面的な中断を検出できます。ただし、どちらも必要としていた状態を確認する代わりにはなりません。
本番環境でよくある 2 つの例です。
$ sync-tool push
ERROR: Push failed (remote rejected)
Sync finished successfully.
$ echo $?
0 # the tool's verdict line and its exit code both read success
$ merge-queue add 456
queued OK # success means ENQUEUED; the commits are not on the mainline yet
どちらのメッセージも、別のことについては正直です。必要な主張は現実の状態についてなので、実際の状態を確認してください。
$ git fetch origin main && git merge-base --is-ancestor "$sha" FETCH_HEAD && echo landed
ref は本当に必要な場所にありますか?それが事後条件です。ログはあくまでその近似値にすぎません。
フェッチが書き込んだ ref である FETCH_HEAD をテストしてください。origin/main 自体が更新されるかどうかは、リモートに設定されたフェッチマッピングによって異なります。また、古いリモート追跡 ref は、ログと同じように近似値にすぎません。
祖先関係から確定的な結論を出せるのは、取り込み後もコミット ID が保持される場合に限られます。Squash、rebase、cherry-pick による取り込みでは $sha が書き換えられるため、その場合、祖先関係のチェックに失敗しただけでは何も証明できません。
こうした場合の事後条件は、FETCH_HEAD にあるコンテンツそのもの、つまり変更されたファイルの正確な内容がフェッチ先の先端に存在することです。件名の一致は、取り込み先を見つけるために使うとしても、チェックには使いません。squash では件名も書き換えられる可能性があるためです。
5. フラグを立てる前に 2 回観測し、送信前に再測定する
状態は変化するため、報告内容との食い違いが生じます。目的の異なる 2 つの基準があります。
- LOOK するかどうかは、一定の時間を空けた 2 回の観測で判断します。1 回の静止した読み取りだけでは、静止しているとは言えません。
- SEND するかどうかは、送信直前に新たに再測定して判断します。このルールの根拠となったインシデントでは、誤報になりかけたケースはすべて最初の読み取りではなく、最後の再測定で解消しました。
reading 1 no change -> keep watching, not a flag yet
reading 2 still no change -> the flag is earned on this evidence
re-measure before sending changed 37 seconds ago -> withhold; the claim went stale
確かな証拠に基づいて基準を満たしたフラグでも、送信する時点では誤りになっている可能性があります。結論は測定した時点の状態に結び付いています。それを送信することは「今」の状態を主張することなので、今測定してください。
取り消す場合も同じです。取り消しも現在の状態に関する主張です。
6. ネガティブコントロール:チェッカーが失敗することを証明する
赤にならないゲートは何も検証しておらず、失敗するところを一度も確認していないチェッカーは、成功したときにも何も証明できません。実際のチェックで使うものと同じアサーション経路を通して、必ず失敗するセンチネルを与えてください。テスト対象を別の経路でテストすると、誤った計測器を使うことになります。
// The real checks and the control share assertContains, so the control
// certifies the exact code path every green check ran through.
test("negative control: the shared assertion fails on an impossible token", () => {
expect(() => assertContains(doc, "zzImpossibleToken414")).toThrow();
});
取り込みやデプロイを検証するチェッカーの場合は、必ず失敗する状態(取り込み前の ref、ファイルのないツリー)に対して実行し、緑を信頼する前に、そこで赤になることを確認してください。
結論を出す直前に
このルールは、結論を出す直前に行う 1 回の確認にまとめられます。
- 読み取り値がゼロ、または存在しない場合:プローブは証拠かエラーを返す仕様ですか(1)?同じ計測器を通じてポジティブコントロールも成功しましたか(2)?
- 警告を要する読み取り値の場合:独立した別の方法でもう一度導き出しましたか(3)?
- 「成功しました」の場合:事後条件を確認しましたか。それとも近似値にすぎないログしか確認していませんか(4)?
- 時間経過後も静止または不在であることを確認する場合:2 回観測し、送信直前に再測定しましたか(5)?
- 緑のチェッカーの場合:赤になるところを一度でも確認しましたか(6)?
レビュー基準
コードレビューを実行するスキル(/rubber-duck-review など)では、このスキルのインストール時に、このセクションの内容がレビュアーへのプロンプトに展開されます。レビュアーには、以下を指摘するよう依頼してください。
- 「存在しない」場合と「確認に失敗した」場合に同じ値を返す可能性があるプローブやチェック:エラーの握りつぶし、検証されていないパス、そのまま信用される grep やフィルターのゼロ
- ネガティブコントロールのないチェッカーやゲート:成功時と同じアサーション経路で失敗できることを証明するものがない
- 事後条件自体を確認できるにもかかわらず、ログ行や終了コードから成功を結論づけているもの
上記の「結論を出す時点」のパスに沿って、検出結果をトリアージしてください。