tdd (Matt Pocock)
レッド・グリーン・リファクタリングと統合テストを使い、テストを先に書いて機能追加や修正を進めます。
mattpocock tdd:プロンプトがすでにテストファーストを求めている場合はリフトなし。トークンは約 1.7 倍。
Codex CLI · gpt-6-astra
22.7 / 30
スキルなしに対するリフト:+0.00 · 状況次第 · 18 実行
状況次第:リフト +0.00 が +1.0 未満。推奨には合計 22/30、出力品質 3.5、リフト +1.0 が必要です。
SOTA
合格Codex でテスト済み。
カバー対象 Codex, Claude Code
Codex CLI 0.166(SOTA ティア):採点対象 18 実行。当ティアのルールでは、ここでの合格は Claude Code もカバーします。第2ティアについては何も述べません。
スコア
各ディメンションは 0〜5 点で、6 つの合計が 30 点満点の合計スコアになります。すべてのディメンションで 5 が最良です。コスト 5 は低コストで高速、インストール 5 は簡単であることを意味します。
- トリガーの信頼性
- 5.0
- スキルなしに対するリフト
- 2.0
- 出力品質
- 4.7
- トークンと時間のコスト
- 2.0
- インストールと移植性
- 4.0
- 安全性
- 5.0
セキュリティレビュー
安全静的スキャン:CRITICAL 0 / HIGH 0 / MEDIUM 0。手動レビュー:ネットワーク無効のサンドボックスで実行しても安全(指摘なし)。クリーンなスキャン結果はスキルの安全性を証明しません。
インストール
npx skills@latest add mattpocock/skills --skill tdd
固定コミット: 4ae2317ddb22b046e26455e4f9c9b3aaaf62212d
テスト済み: 2026-11-02
テスト内容
- 正確な仕様からの関数実装 — TDD を使って slug.py(Python 3、標準ライブラリのみ)に slugify(text, max_length=None) を実装してください。仕様:テキストを小文字化する。ASCII の英字または数字ではない文字の連続を 1 つの '-' に置き換える。先頭と末尾の '-' を取り除く。結果が空の場合…
- 小さいステートフルクラスの実装 — ratelimiter.py(Python 3、標準ライブラリのみ)をテストファースト(red-green-refactor)で書いてください。class RateLimiter(limit, window_seconds, clock=time.monotonic):キーごとのスライディングウィンドウ制限です。allow(key) は、limit 未満の呼び出しであれば True を返して呼び出しを記録し…
- 丸めバグの修正 — 顧客によると、cart.py の cart_total が、各ステップ(各行の合計、割引後の小計、税)でセント単位に切り上げる会計システムと比べて 1 セントずれることがあるといいます。例:cart_total([(1.005, 1)]) は 1.09 を返すべきで…
長所
- スキルは名前を出さずとも採用されました:スキルあり群 6/6 実行でスキルファイルが読み込まれました。
- スキルありの絶対品質は高水準:5 点中 4.67。
短所
- スキルなしに対する有意なリフトなし(+0.00、ノイズの範囲内)。
- 一文プロンプトは 4.67、スキルありは 4.67(+0.00)。
- スキルなしの 1.7 倍のトークンを消費。
- テスト上の注意:一文プロンプト群(6 実行)は、対照群・スキルあり群とは別のマシンで後から追加されました。
- タスク・群ごとに n=2。1 ポイント未満の差はノイズです。
レビュー
Mac テストマシン上で Codex CLI 0.166(gpt-6-astra、低推論)、実行ごとに新規の一時プロジェクトと一時 HOME/TMPDIR/キャッシュを使用、スキルをインストール(スキルあり群)、スキルなし(対照群)、またはスキルの中核要件をプロンプト末尾に付けた一文に圧縮(一文プロンプト群)、タスクごとに各群 2 実行、ネットワーク無効、/tmp への書き込みはブロック。プロンプトはスキル名に一切言及しません(以下で注記する表現を除く)。このスキルの説明は、ユーザーが TDD/テストファーストを求めたときにしか発動しないため、プロンプトにはその旨を明記します(「TDD を使用」「テストファースト(red-green-refactor)」「テストファーストで行って」)。同じ文言は対照群にも与えられるため、このテストは、TDD の単純な依頼に対してスキルが上乗せできるかどうかを調べるものです。
3群の比較
| 群 | 平均スコア(0〜5) | 1実行あたりのトークン |
|---|---|---|
| スキルなし | 4.67 | 86k |
| 一文プロンプト | 4.67 | 178k |
| スキルあり | 4.67 | 146k |
Codex CLI で 18 実行を計測し、LLM 判定なしのルールベースのチェックで採点。合計 22.7 / 30、判定は 状況次第。
開示事項 一文プロンプト群(6 実行)は、対照群・スキルあり群とは別のマシンで後から追加しました。
判定:状況次第(22.7 / 30)。 ソース:mattpocock/skills、skills/engineering/tdd、コミット 4ae2317 に固定。インストール:npx skills@latest add mattpocock/skills --skill tdd。ライセンス: MIT(リポジトリの LICENSE)。
概要
Matt Pocock のスキルリポジトリ(engineering/tdd)の tdd スキルは、レッド・グリーンのループに加え、良いテストとは何か、いつモックするかについての参照ファイル 2 つを提供します。SKILL.md は約 570 語。純粋な Markdown で、スクリプトはありません。ユーザーがテストファーストの作業を望むときにトリガーされます。
テスト方法
Mac テストマシン上で Codex CLI 0.166(gpt-6-astra、低推論)、実行ごとに新規の一時プロジェクトと一時 HOME/TMPDIR/キャッシュを使用、スキルをインストール(スキルあり群)、スキルなし(対照群)、またはスキルの中核要件をプロンプト末尾に付けた一文に圧縮(一文プロンプト群)、タスクごとに各群 2 実行、ネットワーク無効、/tmp への書き込みはブロック。プロンプトはスキル名に一切言及しません(以下で注記する表現を除く)。
このスキルの説明は、ユーザーが TDD/テストファーストを要求したときにのみトリガーされるため、プロンプトにもそのように記しています("using TDD"、"test-first (red-green-refactor)"、"Do it test-first")。対照群にも同じ言葉を与えます。つまり、このテストはスキルが TDD の単なる要求を上回る改善をもたらすかを調べるものです。
- 精密な仕様からの関数実装 — TDD で slug.py に
slugify(text, max_length=None)を実装してください(Python 3、標準ライブラリのみ)。仕様:テキストを小文字化する。ASCII の英字または数字ではない文字の連続を 1 つの '-' に置き換える。先頭と末尾の '-' を取り除く。結果が空の場合は… - 小さなステートフルクラスの実装 — ratelimiter.py(Python 3、標準ライブラリのみ)をテストファースト(レッド・グリーン・リファクタリング)で書いてください。クラス
RateLimiter(limit, window_seconds, clock=time.monotonic):キーごとのスライディングウィンドウのリミッター。allow(key)は、limit未満なら True を返して呼び出しを記録… - 丸めのバグ修正 — 顧客によると、cart.py の
cart_totalは会計システムと比べて時々 1 セントずれます。会計システムは各ステップ(各行の合計、割引後の小計、税)でセント単位に四捨五入します。例えば cart_total([(1.005, 1)]) は 1.09 を返すべきで…
採点はプログラム的(隠しテスト/仕込み問題の検出/制約チェック/該当する場合はレンダリング+OCR)で、実行ごとに 0〜5 点。スコアはどの LLM 判定者も決めていません。
結果
第 3 の群に与えた一文プロンプト:"垂直スライスでテストファーストに作業してください:公開インターフェース経由で失敗するテストを 1 つ書き、失敗することを確認し、合格させる最小限のコードを書き、それを繰り返し、グリーンのときだけリファクタリングする。テストをすべて前もって書かないこと。"
| タスク | 対照群スコア | 一文プロンプト | スキルあり | 対照群トークン | 一文プロンプトトークン | スキルありトークン | スキルの読み込み |
|---|---|---|---|---|---|---|---|
| t1-slugify | 5.0 | 5.0 | 5.0 | 79k | 184k | 135k | 2/2 |
| t2-ratelimiter | 5.0 | 5.0 | 5.0 | 99k | 183k | 169k | 2/2 |
| t3-cart-bugfix | 4.0 | 4.0 | 4.0 | 79k | 164k | 133k | 2/2 |
平均スコア:スキルなし 4.67 に対しスキルあり 4.67(差 +0.0)。1実行あたりのトークン:86k に対し 146k(1.70 倍)。所要時間 50 秒に対し 67 秒。一文プロンプト群:4.67(一文プロンプトに対するスキルの上乗せ:+0.0、スキルなしに対する一文プロンプトの上乗せ:+0.0)。一文プロンプトのトークンは 1実行あたり 178k で、スキルは一文プロンプトの 0.82 倍のトークンを消費します。
何が起きたか
- プロンプトがすでにテストファーストを要求しているため、対照群もそれを行いました。2 つの実装タスクでは、両群のすべての実行が「実装前にテストを書く」を含むすべてのチェックに合格しました。スキルはそこに測定できる何も追加しませんでした。
- バグ修正タスク(カートの丸め)では、スキルの有無にかかわらず 4 実行すべてがバグを修正して隠しテストに合格し、4 実行すべてが同じプロセスチェックで不合格になりました:修正とテストが別々のステップではなく、同じステップに登場した(失敗するテストを先に書かなかった)。スキルはこれを変えませんでした。
- スキルはスキルあり群 6/6 実行で読み込まれました。同じ結果にもかかわらず、エージェントの作業量は大幅に増えました(実装タスクでコマンド 24〜26 に対し 8〜10)。トークンは約 1.7 倍です。
- 隠しテストの正解率は両群とも 100% でした。
- 一文版(垂直スライスでテストファースト、失敗するテストを 1 つずつ):平均スコア 4.67 で対照群・スキルありと同一。1実行あたり 178k トークンで、対照群 86k(2.1 倍)、スキルあり 146k に対応します。一文プロンプトのエージェントは平均 1実行あたり 27 コマンド実行しました(対照群 9、スキルあり 23)。テストループを繰り返し実行したためです。
- すべての群がすべての実行で独自のテストを書き、すべての隠しテストに合格しました。「実装前テスト」は 3 群すべてで 6 実行中 4 実行で満たされました。つまり、プロンプトレベルの「test first」という要求が、スキルの目的をすでに果たしていたのです。
ディメンション別スコア
| ディメンション | スコア | 理由 |
|---|---|---|
| 1_出力品質 | 4.67 | スキルありの平均スコア 4.67/5(プログラム的チェック。タスク別の表を 参照)。 |
| 2_スキルなしに対するリフト | 2.0 | スキルなし 4.67 に対して +0.00 ポイント(タスクごとに n=2。約 1 ポ イント未満の差はノイズ)。 |
| 3_トリガー | 5.0 | スキルあり群 6/6 実行でスキルファイルが読み込まれました。 |
| 4_指示の移植性 | 4 | |
| 5_効率性 | 2 | トークン 1.70 倍(1実行あたり 146k に対し 86k)、時間 1.34 倍(67 秒に対し 50 秒)。 |
| 6_安全性と衛生 | 5 |
本テストの限界
- 各テストプロンプト自体が TDD を要求しているため、対照群にも利益の大部分が与えられています。要求しないユーザーの場合、スキルの説明はまったくトリガーされないでしょう。
- 小さな Python タスクのみです。テストの品質は採点していません。
- タスク・群ごとに n=2。1 ポイント未満の差はノイズです。
- ルールベースの採点はチェックが検出できるものだけを測ります。センスや、結果が最善かどうかは判定できません。
セキュリティ
security.md を参照。スキャン:CRITICAL 0 / HIGH 0 / MEDIUM 0。判定:実行しても安全(ネットワーク無効のサンドボックス)。クリーンなスキャン結果はスキルの安全性を証明しません。
結論
テストファーストの要求に加えて、このスキルは小さなタスクで追加の利益をもたらしません。対照群、一文版、スキルありはいずれも 4.67 で、トークンはスキルが 1.7 倍、一文が 2.1 倍(対照群比)でした。長いセッションでテストを振る舞い重視に保つ助けになるかもしれませんが、それは測定していません。
トランスクリプト
実行ログ:tdd (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.) 列:タスク、群-回、スコア(0-5)、トークン、秒、合格したチェック、エージェントが開いたスキルファイル。 t1-slugify control-1: スコア 5.0, トークン 78904, 秒 50.3, チェック 3/3, 開いたファイルなし t1-slugify control-2: スコア 5.0, トークン 79205, 秒 47.1, チェック 3/3, 開いたファイルなし t1-slugify oneliner-1: スコア 5.0, トークン 199600, 秒 74.7, チェック 3/3, 開いたファイルなし t1-slugify oneliner-2: スコア 5.0, トークン 168650, 秒 68.2, チェック 3/3, 開いたファイルなし t1-slugify treatment-1: スコア 5.0, トークン 144538, 秒 57.6, チェック 3/3, 開いたファイル tdd t1-slugify treatment-2: スコア 5.0, トークン 126047, 秒 50.7, チェック 3/3, 開いたファイル tdd t2-ratelimiter control-1: スコア 5.0, トークン 99439, 秒 60.2, チェック 3/3, 開いたファイルなし t2-ratelimiter control-2: スコア 5.0, トークン 99611, 秒 54.3, チェック 3/3, 開いたファイルなし t2-ratelimiter oneliner-1: スコア 5.0, トークン 159969, 秒 70.9, チェック 3/3, 開いたファイルなし
18 実行、3 成果物
サンプル出力
SOTA · With the skill: test_slug.py (t1-slugify, run 1, Codex)
import unittest from slug import slugify class SlugifyTests(unittest.TestCase): def test_hard_cuts_first_word_when_it_exceeds_limit(self): self.assertEqual(slugify('Supercalifragilistic', 5), 'super') def test_truncates_at_word_boundary(self): self.assertEqual(slugify('Hello, World!', 8), 'hello') def test_normalizes_text(self): self.assertEqual(slugify('Hello, World!'), 'hello-world') def test_preserves_only_ascii_letters_and_digits(self): self.assertEqual(slugify(' -- CAFÉ_42\t中文 / Test!! '), 'caf-42-test') def test_lowercases_before_filtering(self): self.assertEqual(slugify('\u212aELVIN'), 'kelvin') def test_keeps_slug_that_fits_limit(self): for limit in (11, 20, None): with self.subTest(limit=limit): self.assertEqual(slugify('Hello, World!', limit), 'hello-world') def test_keeps_complete_words_without_trailing_separator(self): for limit, expected in ((3, 'one'), (4, 'one'), (6, 'one'), (7, 'one-two'), (8, 'one-two')): with self.subTest(limit=limit): self.assertEqual(slugify('one two three', limit), expected) def test_hard_cuts_long_first_word_even_with_later_words(self): self.assertEqual(slugify('Supercalifragilistic hello', 5), 'super') def test_applies_limit_to_fallback(self): self.assertEqual(slugify('', 2), 'n') def test_empty_normalized_text_has_fallback(self): for text in ('', ' !!! ', '中文'): with self.subTest(text=text): self.assertEqual(slugify(text), 'n-a') if __name__ == '__main__': unittest.main()