Old Coder skillでコードではなく証拠をレビューする方法
Old Coder skillは、実装前に承認するSPECと、実行コマンドや実測値を再確認できるEVIDENCEを作成します。カバレッジに加え、ミューテーションテストで何が分かるかを解説します。
Old Coder skill が実際に変更する内容
Old Coder skill は、コードレビューをドキュメントレビューに置き換えます。コーディングエージェントは、コードを書く前に SPEC を作成します。あなたがそのドキュメントを承認してから、実装を開始します。その後、エージェントは gauntlet と呼ばれる固定の自動チェック群で自分の作業を検証し、実行した正確なコマンドと実際の数値を記載した EVIDENCE レポートを提示します。あなたが読むドキュメントは 2 つです。差分を読む必要はありません。
この置き換えが機能するのは、2 つのドキュメントが、従来の差分と同じ信頼性を持つ場合だけです。SPEC が信頼できるのは、コードが存在する前にあなたが承認するためです。そのため、エージェントがすでに作成したコードに合わせて内容を変更することはできません。EVIDENCE レポートが信頼できるのは、そこに記載されたすべての数値が、あなた自身で実行して同じ数値になることを確認できるコマンドから得られるためです。どちらか一方でも基準を満たさなければ、レビューをレビューの要約に置き換えたことになります。完了したように見える分、差分を読むより悪い結果になります。
この skill は通常の markdown で記述されているため、書かれた指示に従うエージェントであれば利用できます。Claude Code、Codex CLI、Cursor、自分で構築したループが対象です。これは Ponytail lazy senior developer persona skill と同じ系列に属します。このファイル形式に慣れていない場合は、agent skill とは何か、エージェントがどのように読み込むか で仕組みを確認できます。
SPEC は、あなたが行う最後の意思決定です
SPEC は、コードが存在する前に作成するテスト計画です。スキルファイルでは、SPEC に次の4つを含めるよう求めています。
- 具体的なシナリオ: 入力、期待する出力、エッジケース、エラーケース。
divide(1, 0) raises ZeroDivisionError with message X、「不正な入力を処理する」では不十分です。 - 否定的制約: 既存のテストや公開 API のシグネチャなど、変更してはならないもの。
- セットアップ計画: 使用するすべてのツールと新しい依存関係を列挙し、それぞれに用途を1行で記載します。
- 絶対ファイルパス: 探し回らずにファイルを開けるようにします。
セットアップ計画では、モデルの学習データのカットオフが表れます。記憶に基づくツールや固定バージョンは、1年前の情報になっている可能性があるためです。そのため、エージェントに セルフホストした SearXNG インスタンスでの Web 検索を提供し、提案する前に現行バージョンを確認させると有効です。
承認したら、コミットしてください。承認後に編集できる SPEC は契約として機能しません。コミットがあることで、後から証拠が、あなたが署名した内容に基づいて測定されたかを確認できます。
あなたに残された yes または no の判断はこれだけです。これが重要な点であり、同時にリスクでもあります。これは本番環境への変更として扱ってください。実際にそうした変更だからです。すでに エージェントのアクションの前に明示的な承認ゲートをワークフローに設けている場合は、SPEC の承認も同じ場所に組み込めます。
EVIDENCE レポート: コマンドに紐付いた数値
EVIDENCE レポートは、最後に確認する報告書です。このスキルでは、各仕様上の動作を、それを検証するテストに対応付ける必要があります。また、各 gauntlet レイヤーについて、実行したコマンドと実際の結果を記載し、すべての数値を最後のコード編集後に行った 1 回の新しい実行から取得し、スキップしたレイヤーは理由とともにすべて列挙します。形容詞は証拠になりません。「41 個のテストがすべて成功し、カバレッジは 49/49 statements」は結果です。「十分にテストされている」は結果ではありません。
リポジトリ内のデモレポート demo-rate-limiter/evidence.md では、ソースの状態を commit と sha256 tree hash の組み合わせで示しています。この行は見た目以上に重要です。どの正確なバイト列からその数値が生成されたのかが分かるためです。この情報がないと、レポートが、すでに存在しない working tree を記述していても気付けません。
3 つの不正操作防止ルールにより、レポートの信頼性を保ちます。skill file では、これらを例外のないルールとして定めています。テストを成功させるために弱めてはいけません。アサーションの範囲を広げたり、許容値を引き上げたりしてはいけません。green にするために、同じ手順でテストと実装を同時に編集してはいけません。同時編集では、どちらに誤りがあったのか分からなくなるためです。実行していないレイヤーを実行済みとして報告してはいけません。「skipped, no tool available, manual mutation instead」と記載すれば信頼性を保てますが、架空の結果を記載すると、この仕組み全体が失われます。
スキルをインストールし、インストールしたコミットを固定します
リポジトリは AmazingAng/old-coder で、MIT ライセンスです。README には、skills CLI を使った 1 行のインストール方法が記載されています。
npx skills add https://github.com/amazingang/old-coderこれにより、実行時点で main が指している内容がインストールされます。また、CLI 自体も変更される可能性があります。スキルが有効になっていると判断する前に、ファイルの配置先を確認してください。
ls ~/.claude/skills/old-coder/SKILL.md と references/ ディレクトリが表示されるはずです。そこに何もなければ、スキルは Claude Code が参照する場所にありません。skills CLI の古いリリースでは、~/.agents/skills/ に書き込むだけで、結果を ~/.claude/skills/ にリンクしないことがありました。その場合、ファイルはディスク上に存在しても、エージェントは読み込みません。npx skills@latest add ... を実行すると、古い CLI による問題を避けられます。
インストール内容を記録できるため、手動でインストールする方法を推奨します。
git clone https://github.com/AmazingAng/old-coder.git
cd old-coder
git checkout acc5a89
git rev-parse HEAD
mkdir -p ~/.claude/skills
cp -r skills/old-coder ~/.claude/skills/acc5a89 は 2026 年 8 月 17 日時点で main の先頭にあったコミットです。使用するコミットを自分で選び、記録してください。リポジトリは活発に開発されており、gauntlet の参照、テンプレート、verifier のプロトコルはすでに複数のファイル間で移動しています。EVIDENCE レポートに、そのスキルのどのバージョンで評価したかが記載されていなければ、コードの変更とルールの変更を区別できません。そのコミットハッシュを SPEC の隣に、適用対象のコードと同じリポジトリ内で保存してください。
~/.claude/skills を読み取らないエージェントの場合は、システムプロンプトまたは rules ファイルに skills/old-coder/SKILL.md と skills/old-coder/references/gauntlet.md を追加します。統合に必要なのはこれだけです。
ガントレットの中で実行する処理
ガントレットは、仕様上のすべての動作が成功した後に 1 回実行する、複数層のチェックです。このスキルでは、次の処理を指定します。
- 回帰を確認する完全なテストスイート。新たな失敗は 0 件とし、既存の失敗がある場合は事前にベースラインとして記録します。
- 静的型チェック、lint、フォーマット。バグの広い分類とコードの乖離を検出します。
- 変更行カバレッジ。しきい値を下回った場合は、終了ステータスを 0 以外にする必要があります。
- Mutation testing。何も検証していないテストを検出します。
- Property-based testing。誰も想定していなかったエッジケースを検証します。
- 複雑度の予算、実際のプログラムを 1 回実行するチェック、サプライチェーンと Secret のスキャン、テストスイートをランダムな順序で実行する健全性チェック。
- タスクのリスクに応じて選ぶドメイン固有の層。並行処理のストレステスト、API 互換性、ロールバックのリハーサル、レイテンシベンチマークなどです。
デモでは、これらを demo-rate-limiter/tools/gauntlet.sh という 1 つのスクリプトにまとめ、requirements-dev.txt で固定したツールチェーンに対して実行します。使用するツールは pytest、pytest-cov、coverage、hypothesis、mypy、ruff、pip-audit、pytest-randomly です。各ツールは正確なバージョンに固定しています(2026 年 8 月時点で pytest 9.1.1、ruff 0.16.0)。次のように実行します。
cd demo-rate-limiter
python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e .
./tools/gauntlet.shスクリプトは各層につき 1 つのバナーを出力します。たとえば === tests + coverage === や === mutation === です。最後に === gauntlet: all layers green === を出力します。set -e の下で実行されるため、最初に失敗した層で処理が停止し、最後のバナーは出力されません。最後のバナーが表示されたことが確認になります。これは、その上にあるすべての層が終了ステータス 0 で完了したことを意味します。
このスクリプトには、自分の構成にも取り入れる価値がある点が 1 つあります。カバレッジの層は次のように記述されています。
pytest -q --cov=ratelimiter --cov-report=term-missing --cov-fail-under=100--cov-fail-under がない場合、pytest --cov はカバレッジ率を出力するだけで、カバレッジがどれほど低下していても終了ステータス 0 を返します。これは、最初の行で最初に失敗した層で停止すると宣言しているスクリプト内の fail-open な層です。失敗できないガントレットの層は、飾りにすぎません。
カバレッジを超えてミューテーションテストがもたらすもの
カバレッジが答えるのは、「テストスイートはこの行を実行したか」という問いです。実際に知りたいのは、その行が誤っていた場合に、どれかのアサーションが失敗したかどうかです。関数を呼び出して何も検証しないテストでも、触れたすべての行について 100% のカバレッジが報告されます。カバレッジで検出できるのは、テストされていないコードです。テストが何も検証していないことは検出できません。
ミューテーションテストは、2 つ目の問いに直接答えます。コードを意図的に、一度に 1 つの小さな変更だけ書き換え、テストスイートを再実行します。テストスイートが失敗すれば、そのミュータントは kill されたことになります。つまり、どれかのアサーションがその動作を検証していました。テストスイートが通れば、ミュータントは生存しています。その行は実行されましたが、結果を検証するものはありませんでした。
このデモでは tools/mutants.py を使います。src/ratelimiter/__init__.py に番号付きの単一の変更を挿入し、それぞれの変更後に pytest を実行してから、ファイルを元に戻します。変更内容は、疲れた人が犯しやすいミスです。>= を > に変えたり、戻り値を削除したりします。
このランナーの kill 判定は、自作のミューテーションスクリプトで最も間違えやすい部分です。kill として数えるのは pytest の終了コード 1 だけです。1 はテストが実行され、少なくとも 1 件が失敗したことを示すためです。終了コード 0 はミュータントが生存したことを示します。それ以外の終了コード、たとえばテストの収集エラーやテストが 1 件も収集されなかった場合は、何も検証されていないため、数えてはいけません。「ゼロ以外」を kill として扱うスクリプトでは、自身のクラッシュが成功として数えられ、数値が上がることしかありません。
同じファイルには、正直に扱うべき詳細がさらに 2 つあります。ミュータント M11 は等価ミュータントであるため、リストから除外されています。期限切れのエントリをすべて削除する代わりに 1 件だけ削除しても、単調なクロックでは観測可能な動作が同じになるため、どのテストでも kill できません。また、ランナーは PYTHONDONTWRITEBYTECODE=1 を設定し、gauntlet は最初にすべての __pycache__ を削除します。同じ秒に同じサイズのミュータントを 2 つ書き込むと、同じキャッシュ済みの .pyc を共有することがあり、その場合、2 つ目が 1 つ目の判定を引き継ぐためです。
この問題があるため、gauntlet は実際のミューテーション実行の前に、次のネガティブコントロールを実行します。
.venv/bin/python tools/mutants.py --negative-control
.venv/bin/python tools/mutants.pyこのコントロールでは、固定した変更時刻の下で 2 つのミュータントを実行します。1 つは必ず kill され、もう 1 つは厳密に等価で、必ず生存します。両方が kill として返された場合、実行間でバイトコードキャッシュが漏れており、レポートのすべての kill 数が水増しされています。これは、チェッカーに関するこのスキルの原則を具体化したものです。pytest と mypy は、長年にわたって失敗時の動作が検証されてきました。先週自分で書いたスクリプトには、その実績がありません。通過したときに信頼する前に、失敗できることを証明し、その証明を EVIDENCE に記録してください。
The data behind this chart
[
{
"label": "Scenario tests",
"mutants_killed": 22,
"mutants_run": 22
},
{
"label": "Property tests",
"mutants_killed": 3,
"mutants_run": 22
}
]このデモ自身の EVIDENCE レポートは、単一の集計値では依然として詳細が隠れる理由を示しています。シナリオスイートは、22 個中 22 個のミュータントを kill しました。同じミュータントに対してプロパティベーステストだけを再実行すると、3 個を kill しました。kill は最初に失敗したテストに割り当てられるため、合計が完全であっても、スイート全体の妥当性しか示さず、その内部の特定の層については何も示しません。それでも、列挙していなかった入力形状を検出するため、これらのプロパティには存在意義があります。ただし、正しさの検証を主に担っているわけではありません。各層を単独で測定して初めて、そのことが分かります。
サーバー上で一連の検証を実行し、ノート PC では実行しない
EVIDENCE レポートは、いくつかのコマンドが特定の数値を出力したという主張です。別の人が同じ数値を出力できなければ、その主張は検証できません。ノート PC は、その検証に最も適していない環境です。Python のパッチリリースが異なり、使用している PATH には、次のマシンにはないツールが含まれているためです。ミューテーションテストでは、ミューテーションごとにテストスイート全体を再実行するため、さらに問題が大きくなります。デモのリストだけでも、22 回の追加実行が必要になります。
VPS 上のコンテナで実行します。コンテナによってオペレーティングシステムとインタープリターが固定され、固定した requirements-dev.txt によってツールが固定されます。また、VPS にはブラウザーも同時に実行していないマシンを用意できます。
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 python3-venv git ca-certificates \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /workdocker build -t gauntlet:24.04 .
docker run --rm -v "$PWD:/work" -w /work/demo-rate-limiter gauntlet:24.04 \
sh -c 'python3 -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e . && ./tools/gauntlet.sh'正常に実行できると、=== gauntlet: all layers green === で終了します。外部ネットワークが必要な手順は 2 つあります。固定したツールを pip でインストールする手順と、依存関係を脆弱性サービスで確認する pip-audit レイヤーです。オフライン実行で、このレイヤーを暗黙にスキップしてはいけません。実行は失敗します。ゲートには、その動作が必要です。
すべての push で実行する場合も、同じスクリプトを CI ジョブの 1 つのステップとして使用できます。リポジトリは、Python 3.12 を使用して ubuntu-latest 上の GitHub Actions で独自の一連の検証を実行します。runs-on を self-hosted runner に指定すると、ジョブを VPS 上で実行できます。
name: gauntlet
on:
push:
branches: [main]
jobs:
gauntlet:
runs-on: self-hosted
defaults:
run:
working-directory: demo-rate-limiter
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- run: python -m venv .venv && .venv/bin/pip install -r requirements-dev.txt -e .
- run: ./tools/gauntlet.shトリガーは、管理対象のブランチへの push に限定します。pull request を fork から受け取り、ビルドも行う self-hosted runner では、runner の認証情報を使って、第三者のコードをサーバー上で実行することになります。同じ考え方はエージェント自体にも適用されます。ワークステーションではなく、破棄できる使い捨ての VM を与えます。変更について議論している場所に一連の検証結果を届ける場合は、self-hosted PR review agent と連携させます。
このアプローチが機能しない場合
最初の問題は構造上のもので、どれだけツールを導入しても解消できません。gauntlet は SPEC の制約を実行可能な証拠に変換します。しかし、SPEC が正しかったかどうかは判断できません。誤った要件を記述した spec を承認すると、誤ったプログラムに対する完全な EVIDENCE レポートが生成されます。カバレッジは完全で、すべての mutant が排除され、すべてのレイヤーが green でも、ソフトウェアは望んでいない動作をします。diff を読まないことで節約した時間は、すべて spec を読むことに充ててください。
2 つ目は checker です。リポジトリ自身の evidence file にも、この点が正直に記録されています。独立検証プロトコルは 6 ラウンド実行され、6 回目の結果は failed でした。そのラウンド後に行われた修正は再検証されていないため、リリースされる状態はエンドツーエンドで検証されていません。shell lint レイヤーは pass ではなく、利用不可として記録されています。以前のラウンドでは、すでに green と報告された状態の背後に、実際の動作上の欠陥と、健全でない mutation runner があることが見つかりました。green の gauntlet 自体が正しいことを証明するわけではありません。
3 つ目は範囲です。手作業で作成した mutant の一覧では、誰かが仕込もうと考えたバグしか対象になりません。また、その一覧から除外された同値 mutant のそれぞれについて、判断を信頼する必要があります。既製の mutation tool(mutmut、cosmic-ray、Stryker、PIT)は mutant を体系的に生成するため、使用する言語に対応するものがある場合は、こちらを標準の選択肢にすべきです。
リスクに応じて作業量を調整する
このスキルでは、3 つのティアを定義し、エージェントが選択したティアを明示することを求めます。
- Tier 1、軽微: タイポ、コメント、設定値が対象です。完全なテストスイートと lint を実行します。新しいテストは追加せず、不要な理由を 1 文で説明します。
- Tier 2、通常: バグ修正または小規模な機能追加が対象です。完全な検証ループを実行します。バグ修正では、まずバグを再現する失敗テストから始めます。昨日のバグを明日の回帰テストにします。
- Tier 3、高リスク: 金銭、認証、データ損失、並行性、公開 API が対象です。まず、この変更によって発生し得る問題を列挙した失敗モデルを作成します。各モードに対して gauntlet 層を追加し、その後、property test、mutation testing、悪意のある入力で実装を攻撃する明示的な検証を 1 回含む完全な検証ループを実行します。
Tier 3 には実験的な手順も含まれます。別のエージェントに新しいコンテキストを与え、タスク契約、承認済みの SPEC、ソースの状態だけを見せます。そのエージェントは、EVIDENCE に署名する前に完成した成果物を壊そうとします。修正は行わず、報告だけを行い、人間がその内容を評価します。これにより、同じタスクコンテキストを共有することによる相関を減らせます。同じモデルを共有することによる相関は減りません。
この方式を採用するのではなく、同様のものを自分で作成したい場合は、独自のエージェントスキルを作成するで、ファイル構成とエージェントがスキルを読み込む条件を決める description フィールドを説明しています。
FAQ
ミューテーションテストは、コードカバレッジでは検出できない何を検出しますか?
カバレッジは、ある行が実行されたことを記録します。しかし、その行が誤っていた場合にアサーションが失敗するかどうかは記録できません。そのため、関数を呼び出すだけで何も検証しないテストでも、カバレッジは完全と報告されます。ミューテーションテストでは、コードを意図的に 1 箇所ずつ変更し、テストスイートを再実行します。ミュータントが生き残った場合、その行は実行されたものの、結果を検証する処理がなかったことを意味します。これが、このスキルで「カバレッジの数値を追いかけない」ことを絶対的なルールとして挙げ、テストの形骸化を検出する層としてミューテーションを位置付けている理由です。
エージェントが作成したコードを、今でも読む必要がありますか?
このワークフローでは、コーディング前に SPEC を読み、完了後に EVIDENCE レポートを読みます。さらに、レポートに記載されたコマンドを再実行して、内容を抜き取り確認します。diff の確認は任意になります。注意点は、判断のすべてが 1 つの文書にかかることです。誤った要件を含む SPEC からは、意図していないプログラムに対しても、すべて合格するテスト群が生成されるためです。節約できた時間は、SPEC の確認に使ってください。
Old Coder のテスト群を、自分のサーバー上の CI で実行できますか?
はい。そのほうが適しています。コンテナ内に requirements-dev.txt の固定バージョンのツールチェーンをインストールし、プロジェクトのテスト群のスクリプトを 1 つのジョブステップとして実行します。GitHub Actions では runs-on: self-hosted を設定し、VPS に runner を登録します。トリガーは、自分が管理するブランチへの push に限定してください。fork からの pull request をビルドする self-hosted runner は、runner の認証情報を使って信頼できないコードを実行するためです。
どのバージョンのスキルをインストールすべきですか?
1 つのバージョンに固定してください。リポジトリは現在も活発に開発されており、参照ファイルはすでに分割・移動されています。そのため、先月作成したレポートが異なるルールで評価されている可能性があります。リポジトリを clone し、特定の commit を check out して、skills/old-coder を ~/.claude/skills/ にコピーします。その commit hash を SPEC の横に記録してください。これにより、証拠の変化がコードの変化に対応するようになります。