billing モジュールをモノリスから切り出す
billing は API routes、データベースモデル、バックグラウンドジョブ、リトライロジック、invoice ステータス、webhook ハンドラ、Admin UI に散在しています。タスクは一見シンプル——「billing を独立モジュールに切り出して、既存の挙動を保持する」。しかし agent が動き始めると疑問が浮かびます:
- 実際にスコープに含まれるファイルはどれか:API・ジョブ・DB・UI・webhook のどれ?
- 保持すべき挙動はどれか:リトライ・冪等性・invoice ステータス・支払い失敗?
- 既存テストはどこまでカバーしているか?
- このフェーズはマイグレーションやバックグラウンドジョブに暗黙的に依存していないか?
- 新しいコードがビルドを通っても境界ケースがズレていたら誰が検知するのか?
小さなタスクなら /ck:plan --fast や /ck:plan デフォルトで十分なことが多い。複雑だがアーキテクチャが明確なら --hard の方が合っていることも多い。大きなタスクでは、最初に context が足りないと後でコストがかかります:cook がスコープを推測し直し、フェーズファイルが曖昧になり、テストがコードの後に書かれ、リグレッションはリファクタリングが終わってから判明する。
ポイント
--deepは plan が影響範囲を十分に把握するのを助けます:ファイルインベントリ、テストのギャップ、フェーズ間の依存関係。--tddはリファクタリング前に既存の挙動を記録するフローを強制し、そのテストをリグレッションゲートとして使います。
この 2 つは「より強力なモード」ではありません。強引に有効化しても自動的に良くなるわけではない。2 つの具体的なリスクを解消するものです:スコープの把握不足と既存挙動の保護不足。
Mode と Flag、混同しがちな 2 つ
先に進む前に、混同しやすいポイントを整理します。/ck:plan は 2 種類のパラメータを受け取ります:
| 種類 | 例 | 役割 |
|---|---|---|
| Mode | --fast, --hard, --deep, --parallel, --two | パイプラインを選択する。1 コマンドにつき 1 つのみ。 |
| Composable flag | --tdd, --no-tasks | 追加オプション。どの mode にも組み合わせ可能。 |
重要な点:--deep は mode、つまり planning パイプラインを切り替えます。--tdd は flag、つまり選択した mode はそのままで、フェーズファイルと cook フローに tests-first の構造を追加します。
argument-hint: "[task] [--fast|--hard|--deep|--parallel|--two] [--tdd|--no-tasks]"
mode 間の | は「どれか 1 つを選ぶ」という意味。--tdd は別ブロックにあるので compose できます。だから --deep --tdd は有効で、--deep --hard は無効です。
--deep、フェーズごとの地図が欲しいとき
--deep が解決する課題
大きなタスクは複数のコード領域に影響します。billing モジュールの場合、スコープは次のように広がります:
- API routes:invoice 作成・支払いリトライ・返金・webhook コールバック
- Database:invoice テーブル・支払い試行テーブル・ステータス履歴
- Background jobs:リトライスケジューラ・照合ジョブ・webhook リプレイ
- Admin UI:invoice 詳細・手動リトライ・返金アクション
- 共有コントラクト:フロントエンド API レスポンス・webhook ペイロード・イベント名
このようなタスクにはステップのリストだけでなく、地図が必要です:
| 問い | billing 事例での答え |
|---|---|
| create/modify/delete するファイルは? | Route・service・job・migration・UI アクション |
| 既存の挙動をカバーしているテストは? | リトライ・冪等性・返金・webhook リプレイ |
| フェーズ間の依存関係は? | migration がサービスより先、サービスが UI より先 |
| テスト保護が必要なインターフェースは? | API レスポンス・webhook ペイロード・ジョブ入力 |
| リスクのある依存エッジは? | サービスが enum を変更した後もジョブが旧ステータスを読む |
--deep が適しているのは、plan を間違えたときのコストが、plan を丁寧に行うコストより大きい場合です。
Major refactor, 5+ areas, architectural debt -> use --deep
触るファイルが少なければ --fast か /ck:plan デフォルトで十分なことが多い。スコープは複雑でもアーキテクチャが明確なら --hard の方が合っていることも多い。--deep が価値を発揮するのは、plan ミスがフェーズ・テスト・オーナーシップの何時間にもわたる修正につながるケースです。
--deep のパイプライン
広いコンテキストから始め、徐々に各フェーズの詳細へ。
--no-tasks 未指定の場合)。/ck:cook {path}/plan.md を出力。--hard が通常 plan 全体を 1 周 scout するのに対して、--deep はさらにフェーズごとに 1 周追加します。コストは上がりますが、代わりにフェーズファイルが具体的になります:どのファイルを触るか、どのテストが足りないか、どの依存関係にリスクがあるか。
--deep を使うとフェーズファイルに追加されるもの
- ファイルインベントリテーブル:create/modify/delete アクション・おおまかなサイズ・テストへの影響
- テストシナリオマトリクス:critical/high/medium のパス
- 依存関係マップ:このフェーズが他のどのフェーズにリンクするか
- 関数/インターフェースチェックリスト:テストで保護すべき関数やインターフェース
これが主な違いです。フェーズは「billing サービスをリファクタリングする」と言うだけでは不十分。どのファイルか、どのテストか、どの境界ケースか、このフェーズはどのフェーズに依存しているかを明記する必要があります。
--tdd、リファクタが既存挙動を壊しそうなとき
--tdd が解決する課題
cook のデフォルトフローは見慣れたものです:plan を読み、タスクを順番に実装し、ファイルごとに型チェック、その後テストステップ。greenfield ならこれで十分。動いているコードのリファクタリングには、これだけでは安全ではありません。
billing 事例における既存の挙動は次のようなものです:
- リトライは最大 3 回だけ実行され、その後 invoice は
failedに遷移する - 同じ
event_idの webhook は 1 回だけ適用される - invoice が
voidedの場合は返金を実行できない - サービスを切り出した後も Admin UI が正しいステータス履歴を表示しなければならない
ハッピーパスは通るかもしれない。バグはたいてい境界ケースに潜んでいます。--tdd はコードを変更する前にリグレッションゲートを作ります。ここでの「TDD」は新機能のテストを書くことではなく、リファクタリング前に既存の挙動をスナップショットすることです。
--tdd が一番効くケース
| 状況 | 使うべきか | billing での例 |
|---|---|---|
| 完全な greenfield コード | 通常は不要 | スナップショットする invoice/retry の挙動がない |
| ユーザーが使っているモジュールのリファクタリング | 有効 | リトライ・冪等性・ステータス遷移を保持する必要がある |
| 支払いプロバイダを変更するが既存コントラクトを維持 | 有効 | API レスポンスや webhook 挙動がズレやすい |
| 原因が明確な 1-2 行の修正 | 通常は不要 | Admin UI のラベルのタイポ修正など |
| 使い捨てプロトタイプ | 不要 | テストゲートのコストが合わない |
--tdd が最も効くのは、非同期パターン・ステートフルなワークフロー・データベーストランザクション・パブリック API コントラクトを持つモジュールです。billing はこれらすべてを持ちます:バックグラウンドジョブ・ステータス遷移・トランザクション境界・webhook ペイロード・管理アクション。
plan のステップで
| フェーズファイルのセクション | 役割 |
|---|---|
| リファクタ前のテスト | コードを変更する前にリグレッションカバレッジを書き、既存の挙動を記録する。 |
| リファクタリング | 変更するコード部分を記述し、上記テストで保護される。 |
| 追加テスト | フェーズで追加する新しい挙動のテスト(あれば)。 |
| フェーズ末ゲート | リファクタ後に pass しなければならない compile + test コマンドを明記。 |
--tdd フェーズは通常この順で読みます:
1. 既存の挙動を守るテストを先に書く
2. 旧コードがテストしにくければ小さな依存関係を分離する
3. コードをリファクタリングする
4. compile + tests を再実行する
旧コードをテスト可能にするステップはよく見落とされます。旧コードが直接テストできないこともあります:内部でデータベースを呼ぶ長い関数、依存関係を注入できない、出力を分離できない。そのときはまず小さな依存関係を切り出す——挙動は変えずに、コードをテスト可能にする。それからが本当のリファクタリングです。
cook が走るとき
--tdd ありで cook が走るとき
シンプルに言うと:先に書いたテストは旧挙動の比較基準です。リファクタリング後にこの基準が fail したら、cook は次に進む前にズレた挙動を修正すべきです。
バージョン情報本記事は執筆時点の Engineer Kit
engineer@v2.19.1-beta.10を参照しており、ck -Vで確認できます。ck:planやck:cookのワークフローが将来変更された場合の混乱を避けるためのメモです。
なぜ --deep--tdd は一緒に使われることが多いか
--deep と --tdd は異なる 2 つのレイヤーのリスクを解決します。
タスクに両方のリスクがあれば、両方のオプションを有効にする。
--deep で地図を。--tdd で検証レイヤーを。大きなリファクタリングは通常その両方が必要。| リスク | 必要なもの | オプション |
|---|---|---|
| スコープの把握不足 | フェーズごとの scout・ファイルインベントリ・依存関係マップ | --deep |
| リファクタが既存挙動をズラす | リファクタ前のテスト・cook 時のリグレッションゲート | --tdd |
大きなリファクタリングは通常この両方のリスクを持ちます。例えば:
billing モジュールをモノリスから切り出す。
フロントエンド向け既存 API コントラクトを維持する。
retry・idempotency・invoice ステータスの旧挙動を維持する。
database schema・バックグラウンドジョブ・API routes・Admin UI に触れる。
この billing タスクには --deep が必要です。スコープが広く依存関係が多いから。--tdd も必要です。リトライ・冪等性・返金・webhook がすべて保持すべき既存挙動だから。
--deep だけ有効にすると、plan は非常に明確になるかもしれませんがリファクタリングにはリグレッションゲートが欠けます。--tdd だけ有効にすると、テストフローは正しいかもしれませんがフェーズファイルが曖昧になります:何をテストするのか・どこで・どのモジュールが影響を受けるか・どの依存関係が先に来るべきか。
ひと言スコープが広ければ
--deep。保持すべき既存挙動があれば--tddを追加。両方あれば両方セットで。
先に /ck:brainstorm や /ck:scout を使うべきとき
よくあるミス:アプローチがまだ曖昧なのに --deep --tdd を使うこと。plan モードは曖昧さを自動解消しません。すでに選択したアプローチをより具体的なステップに変えるだけです。
例えば、まだ決まっていない場合:
- 本当にサービスを切り出すのか、モノリス内でモジュール化するだけか?
- 新しいキューを使うのか、既存のバックグラウンドジョブを保持するのか?
- 現在の invoice ステータス enum を維持するのか、より明確なステートマシンに変えるのか?
- Admin UI フローを書き直すのか、部分的にリファクタリングするのか?
そのような場合はまず /ck:brainstorm でアプローチを固め、それから plan に進むべきです。
もう 1 つのレイヤーは /ck:scout です。これは context を集めるステップであり、意思決定のステップではありません。コードがどこにあるか・どのモジュールが関連するか・既存のテストが現在の挙動をどこまでカバーしているかがまだ不明なときに有効です。
ブリーフがまだ曖昧なら、いきなり plan に飛び込まない。
--deep --tdd を使う。/ck:scout "billing retry flow, invoice status, background jobs, admin UI"
/ck:brainstorm "billing モジュールを切り出すが既存 API コントラクトを維持する"
/ck:plan --deep --tdd "固まったアプローチで billing モジュールをリファクタリングする..."
関連するコード領域と主要なトレードオフを把握したら、/ck:scout の個別実行を省略できます。/ck:plan --deep はパイプライン内に scout ステップを含んでいます。ただしブリーフがまだ漠然としすぎている場合、先に scout しておくことで静かなミスを防げます:agent が聞こえの良い推論をするが、間違ったプロジェクトの地図に基づいている——というケースです。
| 状況 | 始め方 |
|---|---|
| プロジェクトの context がわからない / コードの場所がわからない | /ck:scout → /ck:brainstorm |
| 何をするか・どうやってするかが明確 | 直接 /ck:plan |
| 何をするかはわかるがどうやってするかが不明 | /ck:brainstorm → /ck:plan |
| そもそもやるべきかがわからない | /ck:brainstorm → 決定 → plan か中止 |
デシジョンマトリクス
/ck:scout/ck:brainstorm/ck:plan --deep --tdd| 状況 | コマンド |
|---|---|
| プロジェクトの context がまだ不明 | 先に /ck:scout、その後 brainstorm/plan |
| アプローチが不確か | 先に /ck:brainstorm |
| 1-2 ファイルの小さな修正 | /ck:plan --fast |
| 中規模の新機能 | /ck:plan auto |
| 複雑な機能・慣れないドメイン | /ck:plan --hard |
| 3 モジュール以上を並行して | /ck:plan --parallel |
| 2 つの具体的なアプローチで迷っている | /ck:plan --two |
| 5 領域以上のリファクタ・アーキテクチャ上の技術的負債 | /ck:plan --deep |
| 動いているコード / dogfood のリファクタ・リグレッションが心配 | Any mode + --tdd |
| 保持すべき挙動がある codebase の major refactor | /ck:plan --deep --tdd |
--deep --tdd の組み合わせは単一のメカニズムではなく、同時に動く 2 つの別々のものです。--deep は地図を。--tdd は検証レイヤーを。planning コストは増えますが、代わりに cook はスコープを推測する必要が減り、リグレッションゲートがより明確になります。
Inside、内部の仕組み
このセクションでは --deep と --tdd が plan 時・cook 時・実行コストにどう影響するかを詳しく見ます。
7.1 --deep が高コストな理由:researcher と各フェーズの scout
| Mode | Researcher | Red Team | Validation | フェーズごとの Scout |
|---|---|---|---|---|
--fast | 0 | 0 | 0 | No |
--hard | 2 | Yes | Optional | No |
--deep | 2-3 | Yes | Yes | Yes |
--parallel | 2 | Yes | Optional | No |
--two | 2+ | 選択後 | 選択後 | No |
--deep のコストは主に、高レベルアーキテクチャ分析のための researcher 2-3 つ・red-team レビュー・validation ステップ・フェーズごとの再確認から来ます。
シンプルに言うと、plan を確定させる前に各フェーズを個別に精査します:このフェーズはどのファイルに触れるか・どのフェーズに依存するか・まだ足りないテストは何か・見落としやすい境界ケースはないか。
誤解しやすいポイント:ここでの「scout」はフェーズごとに読み直して確認する作業を指しており、実行時に必ず別の agent を生成するという約束ではありません。重要なのは --deep が plan を複数の小さな確認ループに通すことで、全体を一度だけ俯瞰するのとは異なるということです。
7.2 --tdd は新しい agent を作らない
誤解しやすいポイント:--tdd は選択した mode を変えません。追加フラグです。--fast・--hard・--deep を通常通り実行し、tests-first の構造を追加するだけです。
このフラグが変えるのは 2 つだけ:
- plan 時のフェーズファイル。 通常フェーズは概要・要件・アーキテクチャ・関連ファイル・実装ステップ・成功基準・リスク評価を含みます。
--tddが有効になると、リファクタ前のテスト・リファクタ後のテスト・フェーズ末ゲートが追加されます。 - cook 時の実行順序。 旧挙動を守るテストを先に書き、リファクタリング後に compile/test ゲートを実行します。
--tdd のコストは --deep より大幅に低い。主にフェーズファイルへの構造追加と cook の実行順序変更です。
7.3 Regression Gate には具体的な command が必要
cook の仕様では Regression Gate は具体的な compile/test コマンドであることが求められます。Go プロジェクトの例:
Regression Gate: go test ./... && go vet ./...
フェーズファイルに「run tests to verify」と曖昧に書かれていると、cook が依拠する正確なコマンドがないため verify が弱くなります。--tdd フラグはすべてのリポジトリで正しいツールを自動推測しません。プロジェクト固有のコマンドがある場合は、タスク説明や plan ファイルに明記すべきです。
Project dùng go test ./..., frontend dùng npm test, E2E dùng Playwright.
タスクでツールを明記すると、plan は各フェーズの Regression Gate を正しく記述できます。
実行方法と汎用テンプレート
以下のテンプレートは ck:plan と ck:cook のソーススキルに準拠しています:plan はタスク + mode/flag を受け取り、cook は plan パスを受け取り、plan でフラグが有効になっていた場合は --tdd を維持します。
汎用テンプレート
/ck:plan [mode] [--tdd] "[やるべきこと].
スコープ: [触れるモジュール/ファイル/スタック].
維持するもの: [API コントラクト・既存挙動・互換性].
触れるもの: [database・job・route・UI・shared type].
Tooling: [具体的な compile/test コマンド].
Known bugs / out of scope: [あれば]."
注意:plan で --tdd を使った場合、cook でも --tdd を付けます:
/ck:cook /absolute/path/to/plan.md --tdd
よくある状況にテンプレートを当てはめてみます:
--deep で planning/inventory フェーズ
/ck:plan --deep "billing モジュールを独立パッケージに切り出すための inventory plan を作成する.
スコープ: API routes・invoice service・payment jobs・Admin UI・database schema.
必要な出力: ファイルインベントリ・依存関係マップ・フェーズオーナーシップ・リスクリスト.
今回のラウンドでは動いている挙動のリファクタリングはしない."
--hard --tdd で動いているコード / dogfood のリファクタリング
/ck:plan --hard --tdd "invoiceStatusService をリファクタリング:transition rules を
別ファイルに切り出し、retry・failed・refunded・voided の exact な挙動を維持する."
--deep --tdd で保持すべき挙動がある major refactor
/ck:plan --deep --tdd "billing モジュールをモノリスから切り出す.
フロントエンド向け API コントラクトを維持し、retry/idempotency/refund/webhook の挙動を保持する.
触れるもの: database schema・payment jobs・API routes・Admin UI. プロジェクトは
go test ./... を使用、フロントエンドは npm test."
良いタスク説明には通常これが揃っています:
- スコープ:どのファイル/モジュール/スタック
- 制約:壊してはいけないもの・維持すべき互換性
- Tooling:test コマンド・compile コマンド、
--tddにおいて重要 - 期待する結果:短くても具体的に
例えば "billing をリファクタリングする" のように入力が曖昧すぎると、plan も曖昧になります。具体的な anchor がなければ scout の出力が漠然としやすい。
ベストプラクティスと落とし穴
cook の前にやっておくこと
plan 前にクリーンな worktree を
working dir に別タスクの uncommitted changes があると、scout が現在のコードと作業中のコードを混同しやすくなります。大きな plan の前は:commit か stash。さらに良いのは /ck:worktree で専用 worktree を作ることです。
cook 前にフェーズファイルをレビュー
--deep --tdd の plan はかなり時間がかかります。plan が生成した plan.md と各フェーズファイルを読まずにすぐ cook しないでください。
フェーズレビューチェックリスト
cook を実行する前に plan.md と各フェーズファイルをこの 5 つの問いで確認してください:
タスクでテストコマンドを宣言する
--tdd を使う場合、リポジトリが npm の代わりに bun・asdf の代わりに mise・make の代わりに task などを使っているなら、明記してください。
Project dùng go test ./..., frontend dùng npm test, E2E dùng Playwright.
宣言しないと agent が推測します。コマンドを間違えると Regression Gate が弱くなります。
plan と cook の間に /clear
大きなタスクの plan context は非常に重くなります:research の出力・scout データ・red-team フィードバック。推奨フロー:plan 完了 → /clear → 再起動 → /ck:cook {absolute-path}/plan.md --tdd。cook は plan ファイルから再読み込みするので、planning context は不要です。
避けたいこと
/ck:cook で --tdd を忘れる
plan に --tdd があって cook にない場合 → cook はリファクタ前のテストセクションを読めますが、テストを先に、その後リファクタリングという順序を強制しません。テストは書かれるかもしれませんが、コードの後になります。plan の出力で提案された cook コマンドをそのまま使いましょう。
小さなタスクに --deep
5 ファイル未満なら通常は不要。--hard か --fast の方がコンパクトです。
greenfield に --tdd
新しいコードにはスナップショットすべき既存の挙動がありません。「リファクタ前のテスト」はほぼ空になり、agent が形式的なテストを書きやすくなります。真の TDD ではありません。greenfield は --hard を使い、テストを通常フローで書きましょう。
フェーズファイルを盲信する
--deep はより丁寧に scout しますが、暗黙的な依存関係を見落とす可能性は残ります。cook 前に自問してください:後のフェーズが前のフェーズでまだ作られていないものに暗黙的に依存していないか?
既存のバグがあるコードに --tdd でテストをスナップショット
旧 billing にバグが潜んでいる場合——例えば重複 webhook が稀に 2 つの支払い試行を作る——リファクタ前のテストがそのバグも「現在の挙動」として捉えてしまいます。リファクタリングがうっかりそのバグを修正するとゲートが fail し、agent が旧バグを再現しようとするかもしれません。解決策:タスクに known bugs を宣言するか、2 つの plan に分割——plan 1 でバグを修正、plan 2 でリファクタリング。
--deep と --parallel を無理に組み合わせる
--deep と --parallel はどちらも mode なので compose できません。複数の agent を並行して走らせる必要がある場合は --parallel --tdd を使い、オーナーシップとテストスコープを明確にしてください。オーナーシップとテスト分離が不確かなら、--parallel をやめて --deep --tdd を維持しましょう。
まとめ
--deep --tdd はすべてのタスクに向いているわけではありません。
次の 2 つのサインを持つタスク向けです:
- 通常の plan では地図が足りなくなりがちなほどスコープが大きい
- リファクタリング後も保持すべき挙動が既存コードにある
--deep は planning を遅くしますが、その代わりにフェーズファイルが明確になります:ファイルインベントリ・テストギャップ・依存関係マップ・関数/インターフェースチェックリスト。
--tdd は cook を遅くしますが、その代わりにリファクタリングにリグレッションゲートが付きます:テストを先に書いて、リファクタリングして、verify する。
PM/ファウンダー/プロダクトの立場なら、この 2 つのオプションが存在することを知っておけば十分です。チームが billing のような大きなモジュールのリファクタリングを準備しているとき、フェーズごとの scout とリグレッションゲートが plan に含まれているかを確認してください。
開発者なら、次の大きなタスクで試してみてください。最初は余計に時間がかかると感じるかもしれない。でも先に書いたテストが一度でも暗黙のリグレッションをキャッチしたなら、そのコストは十分に受け入れられるはずです。