create-spec について
研究ドキュメントを日付付きの実装仕様にまとめる Claude スキルです。要件の確認、設計案の比較、エントリーポイントの定義、テスト計画を支援し、変更自体は実装しません。
主な機能
- 01研究に基づく仕様書の作成
- 02曖昧な要件への確認質問
- 03互換性方針の評価
- 04エントリーポイントと契約の設計
- 05代替案の比較と RGR テスト計画
活用シーン
- 01research/ の文書を基にした機能仕様の作成
- 02仕様作成前の不明点の確認
- 03API の入口、契約、失敗動作の記録
- 04縦割りのテスト駆動実装の計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bastani-inc/ atomic --skill create-specインストール後、Claude Code で/create-specを入力して読み込まれたか確認します。
詳細
既存の調査資料を活用して、コードベースに新機能またはシステム変更を実装するための仕様書を作成してください。調査資料の場所は $ARGUMENTS です。調査資料の場所が指定されていない場合は、research/ ディレクトリ全体を使用してください。重要:調査資料は research/ ディレクトリにあります。調査資料を探すために specs/ ディレクトリを調べてはいけません。以下のテンプレートに従い、research/ にある関連する調査資料の知見を使って、包括的な仕様書を specs/ フォルダーに出力してください。仕様書のファイル名は、YYYY-MM-DD-topic.md 形式(例:specs/2026-03-26-my-feature.md)にしてください。日付には本日の日付を使用し、トピックはケバブケースで要約してください。ヒント:codebase-research-locator エージェントと codebase-research-analyzer エージェントを使うと、research/ ディレクトリ内の調査資料を見つけて分析するのに役立ちます。また、文脈を補足するため、仕様書全体で関連する調査資料を引用することを強く推奨します。
作成を始める前に確認の質問をする
- ユーザーの依頼が曖昧な場合、または必要な詳細が不足している場合は、仕様書の作成を始める前に確認の質問をして、より多くの情報を集めてください。これにより、仕様書が包括的で、ユーザーのニーズに合ったものになります。
互換性に関する方針を決める
- 仕様書作成の依頼を分解する前に、このプロジェクトが実際のダウンストリームユーザーとの後方互換性を維持する必要があるかを判断してください。
- ユーザーが破壊的変更、公開 API の変更、クリーンアップを明示的に許可している場合、または実際のユーザーやダウンストリーム依存関係がないと述べている場合は、破壊的変更を許可してください。
- ユーザーが本番環境のユーザー、公開 API、ダウンストリーム利用者、移行の安全性、または互換性要件に言及している場合は、破壊的変更を許可しないでください。
- 依頼から方針を判断できない場合は、続行する前に一度だけユーザーに確認してください。可能であれば、利用可能な構造化質問ツールを使ってください。
- この方針を仕様書作成の計画、最終仕様書のフロントマター、および最終仕様書の
## Backwards Compatibilityセクションに反映してください。 - 破壊的変更を許可する場合は、既存のレガシー動作、互換性用シム、任意のフラグ、公開 API を、現在の状態として記述してください。ユーザーが維持を明示的に求めない限り、将来の仕様書で保持すべき制約として扱ってはいけません。
- 破壊的変更を許可しない場合は、公開 API、互換性に影響する箇所、ダウンストリームの呼び出し元、移行上の制約、および今後の作業で維持すべき動作を記述してください。
草案を作成する前に進め方を決める
まず、すでに利用できる文脈を確認してください。会話、指定された調査資料の場所、research/ 内の文書、ローカルドキュメント、コードベースが対象です。実際に分かっている内容に合った進め方を選んでください。
- パス A — 文脈を仕様書にまとめる: 利用可能な会話、調査資料、ドキュメント、またはコードに、問題、制約、影響を受けるコード、受け入れ基準を説明するのに十分な背景情報がある場合は、この進め方を使ってください。
- パス B — まず確認する: 仕様書の作成を求められているものの、問題、制約、設計の方向性、影響を受けるコード、または受け入れ基準が明確でない場合は、この進め方を使ってください。アーキテクチャ上の決定を勝手に作り出してはいけません。
コードベースを調べれば答えが分かる場合は、ユーザーに質問するのではなく、コードベースを調べてください。パス B では、まだ仕様書全体を書かず、不足している文脈を説明したうえで、以下に定める既存の ask_user_question と対比的な確認質問のルールに従ってください(一度に一つの質問、または論理的にまとまった質問を行い、推奨する回答と具体的なトレードオフを提示してください)。回答とリポジトリの証拠によって十分な文脈が得られたら、パス A に進んでください。
パス A の作業方法
パス A を選択した場合は、次の順に作業し、結果を以下の番号付きドキュメント見出しに対応付けます。
- 標準とローカルのコンテキストを読み込みます。 ローカルの用語、モジュール構成、ドメイン概念、エラー、アダプター、オブザーバビリティ、ランタイムのパターン、テストのスタイルを調べます。パターン、ライブラリ、アダプター、スキーマのスタイル、テスト戦略を導入する前に、先例を確認してください。調査結果の根拠を §2.1 とドア名に示します。
- 設計上の問題を抽出します。 現状、ユーザーと呼び出し元、課題、目標、対象外、制約、不変条件、影響を受けるシステム、有力なドア、運用上の懸念、リスク、未解決の質問を §2、§3、§9 に記録します。不明点は未解決の質問として残します。
- 推奨案を確定する前に、実質的に異なる代替案を検討します。 名前だけでなく、インターフェースの形、接合部の配置、所有権、呼び出しスタック、ランタイムのトポロジー、モジュール境界を比較します。§6 はドキュメント内で推奨設計の後にありますが、比較結果は §6 に記録します。
- 型付きの契約を仕様化します。 ドアの評価基準を維持しながら、推奨するドア、型、API、名前付きの失敗、拒否を §5.1~§5.3 で定義します。
- 呼び出しスタックとデータフローを仕様化します。 到達可能な範囲で、現状と提案する経路、失敗時の動作、再試行、キャンセル、冪等性を、以下の視覚的な形式を使って §5.4 に記載します。
- ファイルとモジュールを対応付けます。 追加・変更・削除するファイル、テストファイル、設定ファイルと、それぞれが担う責務を §4 または §5 に列挙します。
- 垂直方向の RGR TDD スライスを計画します。 §8 では、重要な公開ドアまたは接合部ごとに、振る舞いを検証する失敗するテスト、最小限の動作する実装、その振る舞いを維持するリファクタリングを順に行います。すべてのテストを先に書く水平方向の計画にはしないでください。
- 設計のみの仕様書を作成します。
specs/YYYY-MM-DD-topic.mdを作成し、このスキルでは変更を実装しないでください。
設計哲学:仕様書とは、ドアについての理論です
プログラムのエントリーポイントをまとめて読むと、そのプログラムが自らの目的について持つ理論がわかります。境界の内側はすべて仕組み、つまり どのように実現するか です。コードが意味、つまり 何をするか と なぜそうするか を語るのは、境界においてのみです。したがって、この仕様書が定義する最も重要なものは、システム内部の仕組みではなく、システムが備えるドアの集合、すなわち信頼できない入力が到達し、取り消せない効果が発生する関数、ルート、RPC メソッドです。
この主張には二つの行為が含まれており、優れた仕様書はその両方を実行します。一つはドアを 見つける ことです。research/ の調査を用いて、そのソフトウェアが役立つ世界で実際に重要なことを学び、ドメインにすでに存在する接合部を発見します。もう一つはドアを 作り上げる ことです。各ドアの名前と形を定め、背後にあるものを正しく伝えるようにします。エントリーポイントの設計を仕様書の背骨として扱ってください。レビュー担当者がドアの集合だけを読めば、実装の詳細を一つも読む前に、そのシステムが何のためにあるのかを再構成できるべきです。
仕様書で導入または変更するすべてのエントリーポイントに、以下の五つの原則を適用し、それぞれに評価基準を用いてください。
5つの原則
-
ツールではなく、関節に名前を付けます。 ドメインには継ぎ目があります。現実がすでに意味のある単位に分かれている場所です(ユーザーを認証する、支払いを決済する、アクセス権を取り消す、下書きを公開する)。これらは、コードを書く前から存在しています。それぞれの入口には、そうした継ぎ目にちなんだ名前を付けます。背後にある仕組みの名前(
run the query、call the service、update the row)は付けません。ツールにちなんだ名前の入口では、読み手はどう動くかは学べても、何のためにあるのかはいつまでも分かりません。これは存在論的な不一致であり、どれほど整った仕組みでも埋め合わせることはできません。コードではなく、ドメイン(そして調査)に耳を傾けてください。 -
正直に凝縮するか、まったく凝縮しないかです。 入口の価値は、おおよそ隠された仕組みと外部に見せるインターフェースの比率で決まります。ただし、名前が本体の提供内容を正確に約束している場合に限ります。約束は少なすぎても(危険や不完全さを隠してしまうため)、多すぎてもいけません(守られない保証を暗示してしまうため)。ときどき黙って何もしない
save()、論理削除を行うdelete()、状態を変更するvalidate()、ユーザーを作成するgetUser()。いずれも境界での嘘であり、名前を頼りに考える呼び出し元が増えるたびに、その嘘は積み重なります。コストとリスクを語彙に反映してください(低コストの借用、割り当て、消費の違い、readとread_exactの違い、名前に含めるパニックのリスク)。 -
意図は、入口が拒むものに表れます。 境界は、何を許すかと同じくらい、何を禁じるかによっても伝わります。入口の集合の形、つまり何を容易にし、何を不可能にするかは、設計者が何を大切にしたかを直接示します。実行時にチェックするだけでなく、不正な状態を表現できないようにすることを優先してください。ルールをチェックする入口は呼び出し元を信頼する必要がありますが、ルールが構造上必須となる入口は、誰も信頼する必要がありません。プリミティブ型より新しい型(
AccountId、OrderId)を、独立したブール値より直和型を使います。また、その入口を通じてのみ生成できる、権限を保持する型(AdminSession、AuthorizedCharge)を使ってください。 -
時を経て出会う見知らぬ人のために書きます。 入口は、マシンではなく、何年も先に現れ、あなたと会うこともなく、変更に踏み切る前にシステムの目的を理解しなければならない、有能な見知らぬ人のために作ります。判断基準は次のとおりです。本体をひとつも読まずに、エントリーポイントだけからシステムの目的を再構成できるでしょうか? システムの意味を知るために実装を読まなければならないのなら、意図が入口から漏れ出し、仕組みの中に入り込んでいます。
-
危険な入口は少数に保ち、正直に名付けます。 システムの成熟度は、取り消せない作用を守る入口がいかに少ないか、そしてその入口がいかに正確な名前を持つかに表れます。お金が動く、アクセス権が付与される、データが破棄される、キーが発行される、メッセージが配信される。それぞれの作用は、正直な名前の単一の要所を通すことで、作用を守る約束がひとつの場所だけに存在するようにします。危険を、小さく無名の経路に散らばらせると(
chargeCardを実行できるすべてのハンドラー、DROP TABLEに至る広範な DB 権限、その場しのぎのos.system(...)、デフォルトで公開されるストレージなど)、作者自身も含め、誰もどこが責任を担っているのか言えなくなります。
仕様内のすべてのエントリーポイントで実行するルーブリック
仕様が導入または変更する自明でないエントリーポイントごとに、これらを順番に確認していきます。きれいに答えられなかった最初の項目で止まります — それが発見事項であり、仕様に記載すべきものです(多くの場合、制約として §5 に、または未解決の問いとして §9 に)。これを順方向に実行して、起草したドアを監査し、逆方向に — 各義務がどのドアに値するかを問いかける形で — 実行して、システムにまだ欠けているドアを見つけます。
- ツールではなくジョイント。 その名前は、非エンジニアでも認識できるドメイン意図の単位になっていますか — メカニズムの説明ではありませんか? 実装用語でしか名付けられないなら、それはステップであり、ドアではありません。
- 文が成立する。 その保証を、and を伴わない1つの宣言文で述べられますか? できないなら、それは融合している(分割してください)か、未定義です(最も危険なケース — 立ち止まり、それが実際に何を約束するのかを突き止めてください)。
- 名前が正直である。 本体が提供するものを正確に約束していますか — 危険を隠さず、守らない保証をほのめかしていませんか? その名前が嘘として読まれ得る方法を列挙してください。
- 義務が果たされている。 文から pre / invariant / post / never を読み取ってください。各義務は設計内の実際のステップに対応し、各ステップは義務に対応していますか?
- すべての出口が約束を守る。 エラーリターン、リトライ、タイムアウト、部分書き込み、同時呼び出し元、2番目のエントリーを確認してください。保証はハッピーパスだけでなく、それらすべてを生き延びなければなりません。
- 拒否が本物である。 このドアは何を不可能にしますか? 不正な状態は表現不可能になっていますか、それとも単にチェックされて信頼されているだけですか?
- 信頼の遷移が明示的かつ単一である。 信頼されていないものが信頼されるようになる、または権限が増加する場合、それはここで — そしてここだけで — 起こりますか?
- 不可逆的な効果は1つのチョークポイントを通過する。 これは、それが守る効果にとって唯一の支配的なドアですか? その効果に別の方法で到達できるなら、その別の方法がバグです。
- エアロックは境界にある。 検証、認可、変換、およびエラー境界はドアに存在し、内部は自身の不変条件を信頼して自由でいられます。深部にある防御的なコードは、境界が誤って配置されていることを意味します。
- 部外者が意図を再構築できる。 誰かがこのドアだけを — 名前とシグネチャを、本体ではなく — 読んで、それが何のためで何を負っているかを知ることができますか?
ジョイントはどの境界でも同じである
プロセス内の settle_payment、REST 上の POST /v1/payment_intents/{id}/capture、gRPC 上の Billing.SettlePayment は、1つのドア、3つのトランスポート、1つの名前です。関数、ルート、RPC メソッドがジョイントとは何かについて一致しない場合、それらの少なくとも1つはツールに名前を付けています — それをフラグしてください。ワイヤー上では、HTTP 動詞はプロトコルが無償で与えてくれる正直さであり(GET は安全、PUT/DELETE は冪等、POST はどちらでもない — まさにそれが、マネードアが Idempotency-Key を携える理由です)、ステータスコードはドアの正直な出口です(201 作成済み、204 完了、202 受理済み。409/412 は本物の拒否です)。最大の嘘は 200 OK が {"error": ...} を包むことです。認証はエッジの1つのゲートであり、その背後にあるすべてのハンドラーが、既知の呼び出し元と話していると信頼できます。
<EXTREMELY_IMPORTANT>
- ユーザーに質問するための、充実したインターフェースを提供する
ask_user_questionツールを使用してください。 - この段階では、実装は一切行わないでください。以下の説明に従って、包括的な仕様書を作成してください。
- 仕様書には、具体的な日付や期間(例:分、時間、日、週など)に関する情報を含めないでください。また、明示的なフェーズ(例:Phase 1、Phase 2 など)を優先してください。
- 仕様書では、エントリーポイントのセットを第一級の成果物として扱う必要があります。セクション 5 はドア(型付きシグネチャ、名前付きの失敗、型で表現される拒否)を中心に構成し、上記の評価基準を満たす必要があります。意図を仕組みに混在させないでください。レビュー担当者が §4.4 と §5.1 だけからシステムの目的を再構成できるようにしてください。
- 仕様書の対象が、レビュアーの承認ゲートと最終アクション(たとえば、PR/MR/レビューの作成、リリースタグの付与、デプロイ、公開)を含むワークフローである場合は、実装・レビューの承認と、それらの承認後に行う最終アクションを明確に分けてください。実装と検証の基準が満たされ、明示的に許可された最終アクションだけが残っている場合は、レビュアーとリデューサーが実装ループを承認して停止するよう、ワークフローの設計で定めてください。残りの作業は、次のアクション/最終アクションのフィールドに記録し、実装を繰り返してはなりません。
- 仕様書の対象がワークフローまたはワークフロープロンプトのリファクタリングである場合は、各ステージ、レビュアー、リデューサーに、ローカルでアクション指向のプロンプトを要求してください。モデルの各ステージが参照できるのは、そのステージ自身のプロンプト、成果物、ツール、読み込んだ情報です。明示的に提供されない限り、ワークフローグラフの名前や周辺の実装詳細は参照できません。そのため、名前がユーザーに表示される文脈であるか、動作に実質的な影響を与える場合を除き、「現在のコード差分をレビューする」や「レビュー依頼を作成/更新する」のような指示を優先し、実装固有のワークフロー上の呼称は避けてください。
- 仕様書の作成後、質問は一度に一つずつ、または論理的なグループに分けて行ってください。
- 「## 9. 未解決の質問/課題」のセクションを参照し、各質問を一つずつ確認してください。自由回答形式ではなく、対比型の確認(具体的なトレードオフを添えた2~3個の選択肢の提示)を行ってください。つまり、「X についてどう思いますか?」と尋ねるのではなく、「(A) 選択肢 X — トレードオフ Y」「(B) 選択肢 Z — トレードオフ W」のような解釈を提示します。質問を確認しながら、ユーザーの回答に基づいて仕様書を更新してください。
- ユーザーと共通理解に達するまで、この計画/仕様書のあらゆる側面について徹底的に質問してください。設計上の各分岐を掘り下げ、依存する判断を一つずつ解決してください。各質問では、推奨する回答を提示してください(つまり、対比型の確認を行ってください)。特にドアを重点的に確認してください。関節部とは何か、ドアが何を保証し何を拒否するのか、危険な作用をどこに集約するのかについて意見が分かれる場合は、いずれもユーザーに確認すべき質問です。
- コードベースを調べれば回答できる質問については、質問する代わりにコードベースを調査し、推測した意図がユーザーの意図と合っているか確認してください。
- 最後に、仕様書の作成と未解決の質問への回答が終わったら、
specs/ディレクトリに生成した仕様書のパスを含む要約をユーザーに提示してください。- 要約では、ドアの一覧を名前だけで示し、どのドアが不可逆な作用を防護するかを明記してください。最良の結果を得るために仕様書を確認し、フィードバックや追加の質問があれば伝えるよう、ユーザーに促してください。
形状を優先したビジュアル言語
仕様は Markdown で記述しますが、ビジュアルには要点を明確に伝えるために必要最小限の形式を使います。前置きは省き、説明文は簡潔にし、各ビジュアルはそれを補足する短い文章のすぐ隣に配置します。必要に応じて、以下から 1 つ以上を使用してください。毎回すべてを使う必要はありません。
- ロジックやアルゴリズムは、文章だけでなく、インデントした
textの擬似コードで示します。 - 実行時の制御フローは、インデントした呼び出しツリーで示します。
- UI 構造は、重要な状態とモジュールの境界を含め、
tsxのコンポーネントツリーで示します。 - ファイルの役割や大規模なリファクタリングは、浅い
textのファイルツリーで示します。 - コンポーネント間のやり取り、制御フロー、データフローは Mermaid で示します。
- 周囲の構造がすでに存在し、変更点を示すことが要点である場合は
diffを使います。トピックに合わせて、コンポーネントツリー、ファイルツリー、呼び出しツリー、または状態/制御フローの差分にします。 - 大部分が新規である場合、省略したコンテキストによって所有関係や順序が分かりにくくなる場合、または読者がコピー可能な構造を必要とする場合は、ブロック全体を示します。
ビジュアル UI、レイアウト、状態の比較、または Mermaid では複雑すぎる概念については、1 つの焦点を絞った show-me-{description}.html アーティファクト(図、インフォグラフィック、または短いスライドデッキ)を使用できます。製品の色、書体、間隔、コンポーネント、ラベル、データに合わせ、デスクトップとモバイルに対応させます。仕様は specs/ 内の Markdown のままにし、HTML はページ自体が主題である場合に限り、追加の任意ファイルとして使用します。Atomic の bash ツールと移植性のあるオープナーを使って開きます。
if [ "$(uname -s)" = "Darwin" ] && command -v open >/dev/null 2>&1; then
open "path/to/show-me-{description}.html"
elif [ "$(uname -s)" = "Linux" ] && command -v xdg-open >/dev/null 2>&1; then
xdg-open "path/to/show-me-{description}.html"
else
printf 'Open this file: %s\n' "path/to/show-me-{description}.html"
fi
[Project Name] 技術設計書/RFC
| ドキュメントのメタデータ | 詳細 |
|---|---|
| 作成者 | !git config user.name |
| ステータス | Draft (WIP) / In Review (RFC) / Approved / Implemented / Deprecated / Rejected |
| チーム/オーナー | |
| 作成日/最終更新日 |
1. エグゼクティブサマリー
指示:ドキュメントの「要約」です。読者は、2 分しか時間のない VP、または別チームのエンジニアだと想定してください。背景(問題)、解決策(提案)、影響(価値)を要約します。この変更の核心となる 1 つか 2 つの ドア を明記してください。200 語以内に収めます。
例: この RFC では、現在の夜間バッチ請求システムをイベント駆動型アーキテクチャに置き換えることを提案します。現在、請求の遅延によってカスタマーサポートへの問い合わせが 5% 増加しています。提案する解決策では、外部への送金を一元管理する単一の関門として、2 つの送金ドアである
authorize_charge(取り消し可能な与信枠の確保)とsettle_payment(取り消し不可能な決済確定)を導入します。これにより、請求の遅延を 24 時間から 5 分未満に短縮し、二重請求を構造上不可能にします。
2. 背景と動機
指示:なぜこれを行うのでしょうか。なぜ今なのでしょうか。製品要件定義書(PRD)へのリンクを記載し、関連する research/ ドキュメントを参照してください。
2.1 現状
指示:既存のアーキテクチャを説明し、欠点も正直に述べてください。ツールにちなんで名付けられた「漏れ」のあるドア、不誠実な圧縮、散在する危険なども含めます。現状を明確に示すために必要な最小限の図を選んでください。所有関係を示す浅いファイルツリー、実行時の流れを示す呼び出しツリー、UI 構造を示すコンポーネントツリー、またはインタラクションやデータフローを示す Mermaid を使います。簡潔な説明のすぐ近くに図を配置し、文章のほうが明確な場合は無理に図を使わないでください。
- アーキテクチャ: 現在、Service A は共有 SQL データベースを介して Service B と通信しています。
- 制限事項: これにより密結合が生じています。Service A がテーブルをロックすると、Service B はタイムアウトします。
- 現状の「漏れ」のあるドア: たとえば、
chargeCard(token, cents)はチェックアウト、再試行ジョブ、および管理パネルから呼び出せますが、「課金を厳密に一度だけ行う」責任を担うものはいません。processPayment(...) -> boolは、カードの決済拒否、ネットワーク障害、重複送信を同じfalseにまとめてしまいます。
2.2 問題
指示:具体的な問題点は何ですか?
- ユーザーへの影響: 夜間バッチ処理中に、顧客が領収書をダウンロードできません。
- ビジネスへの影響: 請求エラーによる解約で、月あたり $X の損失が発生しています。
- 技術的負債: 危険が散在しており、境界の位置も適切ではありません。防御コードがドアではなく、コアの奥深くにあります。
3. 目標と対象外
指示:これは契約、つまり成功の定義です。正確に記述してください。
3.1 機能目標
- ユーザーがデータを CSV 形式でエクスポートできるようにする必要があります。
- システムはマルチテナントのデータ分離をサポートする必要があります。
3.2 対象外
指示:実施しないことを明確に記載してください。「意図は、ドアが拒むものに表れます」。意図的に設けないドアも、設けるドアと同じくらい目的を明確に示します。これにより、スコープの拡大を防ぎます。
- このバージョンでは PDF エクスポートをサポートしません(CSV のみ)。
- 3 年を超えるデータは移行しません。
- 資金を移動するための 2 つ目の経路は公開しません。
settle_paymentを唯一のチョークポイントとして維持します。
4. 提案するソリューション(ハイレベル設計)
指示:「全体像」を示します。前述の形状優先のビジュアル表現から、目的に合う最小限の形式を選んでください。それぞれが異なる問いに答える場合に限り、複数を使います。型、ツリー、差分、または Mermaid で形状を定義し、簡潔な文章で理由を説明してください。より簡単な図で境界を伝えられる場合は、装飾の多い図を使わないでください。
4.1 システムアーキテクチャ図
指示:システム境界を示し、信頼できない入力が信頼できるリクエストになる単一の境界(エアロック)を明示してください。コンポーネント間のインタラクションやデータフローには Mermaid、モジュールの責務には浅いファイルツリー、実行時の制御フローには呼び出しツリー、UI 構造にはコンポーネントツリーを使います。全体の大部分が新しい場合や、省略したコンテキストによって所有関係や順序が分からなくなる場合は、ブロック全体を示してください。
flowchart TB
User((User)) -->|untrusted request| Gateway["Gateway<br/>auth · validate · authorize<br/>airlock"]
Gateway -->|trusted request| API["Core service<br/>trusts its invariants"]
API --> DB[(Primary DB)]
API -.-> Worker[Worker]
Worker -.-> Ext["External provider<br/>irreversible effect"]
4.2 アーキテクチャパターン
指示:パターン名を記載してください(例:「イベントソーシング」、「BFF — フロントエンド向けバックエンド」、「パブリッシャー・サブスクライバー」)。
- Order Service が
OrderCreatedイベントを発行し、Billing Service がそれを非同期に消費する、パブリッシャー・サブスクライバーパターンを採用します。
4.3 主要コンポーネント
| コンポーネント | 責務 | 技術スタック | 根拠 |
|---|---|---|---|
| 取り込みサービス | 受信した Webhook を検証する | Go、Gin フレームワーク | 高い並行処理性能が必要。 |
| イベントバス | サービスを疎結合にする | Kafka | 耐久性のあるログ、リプレイ機能。 |
| プロジェクション DB | 読み取り最適化されたビュー | MongoDB | 多様なレシート形式に対応する柔軟なスキーマ。 |
指示: 設計を実装するファイルとモジュールを対応付けてください。追加、変更、削除、テスト、設定のすべてのファイルを列挙し、それぞれが担う責務を明記してください。レイアウトが要点である場合は浅いファイルツリーを使用し、それ以外の場合はこのコンパクトなマップを使用してください。
| パス | アクション | 担当 |
|---|---|---|
src/feature/door.ts | 変更 | 公開ドアと境界コントラクト |
test/feature/door.test.ts | 追加 | ドアを通した垂直方向の動作スライス |
4.4 ドアセットの概要 (Stranger-Across-Time View)
指示: エントリポイントの名前のみを列挙してください — シグネチャも本体も不要です。有能な第三者がこのリストからシステムの目的を再構築できるべきです。できない場合、意図がメカニズムに漏れています。§5 に戻り、再構築できるようになるまで名前を変更してください。不可逆的な影響を防ぐすべてのドアには ⚠ を付けてください。
例:
register_account、authenticate、authorize_charge、settle_payment⚠、grant_access⚠、revoke_access、publish_draft。これらを読むだけで、システムが誰を入れるのか、資金がちょうど 2 ステップでのみ、そしてその 2 ステップだけで移動すること、誰がアクセスを付与できるのか、そして作業が公開されるとはどういうことかが分かります。
5. 詳細設計
指示: ドキュメントの「中身」です。エンジニアがコーディングを開始するのに十分な詳細を記載してください。まずドアから始めてください — これらは仕様の主要な部分です — その後、その背後にあるメカニズムを説明してください。
5.1 ドア (エントリポイントコントラクト)
指示: 自明でない各エントリポイントについて、型付きシグネチャ (型付き擬似コードで構いません — 構文ではなく型を読んでください)、一文での保証 (「and」を使わない)、名前付き失敗セット、および型システムで強制する拒否を記載してください。その後、ルーブリックの結果を記録してください。不正な状態を単にチェックするのではなく、表現不可能にしてください。各ジョイントを確立する research/ ドキュメントを引用してください。ドアがほぼ新規である場合、または読者がコピー可能な目標形状を必要とする場合はブロック全体を使用し、既存のドアが変更される場合はトピックに合わせた diff を使用してください。ドアへの、およびドアからの実行時パスをコールツリーとして示し、重要であれば UI/モジュール境界をコンポーネントツリーとして含めてください。
// — Money. Two doors, and there is no third way to move a cent. —
authorize_charge(
account: AccountId, // newtype: cannot be confused with any other id
amount: Money, // currency-typed: USD and JPY will not add
idempotency_key: IdempotencyKey,
): Result<AuthorizedCharge, ChargeError>
// Guarantee: places a reversible hold and returns proof an authorization exists.
// ChargeError = InsufficientFunds | CardDeclined | NetworkError | DuplicateKey
settle_payment(
authorized: AuthorizedCharge, // ← can ONLY be produced by authorize_charge
idempotency_key: IdempotencyKey,
): Result<Settlement, SettlementError>
// Guarantee: captures the held funds. IRREVERSIBLE. The single chokepoint for outbound money.
// You cannot settle a charge you did not authorize — not because a check forbids it,
// but because there is no way to CONSTRUCT an AuthorizedCharge except by calling
// authorize_charge. The illegal state is unrepresentable. The idempotency key makes
// the retry, the double-click, and the at-least-once queue converge on ONE settlement.
ドアごとの監査 (ルーブリックを実行):
| ドア | (1) ジョイント | (2) 一文、「and」なし | (3) 正直な名前 | (5) すべての出口 | (6) 実際の拒否 | (7) 信頼の遷移 | (8) 単一のチョークポイント |
|---|---|---|---|---|---|---|---|
authorize_charge | ✅ ビジネス動詞 | ✅「可逆的なホールドを置く」 | ✅ | リトライ → DuplicateKey、タイムアウト → NetworkError | 通貨の不一致は表現不可能 | 該当なし | 可逆的、チョークポイントではない |
settle_payment ⚠ | ✅ ビジネス動詞 | ✅「ホールドされた資金をキャプチャする」 | ✅ ドキュメントと型に不可逆性を明記 | リプレイはキーによって収束 | 未承認の課金を決済できない (型) | 該当なし | ✅ 唯一の資金流出ドア |
5.2 API インターフェース — ワイヤー上の同じドア
指示:Web サービスの実際の境界は、トランスポート層の表面です。URL は接合部を示し、HTTP 動詞はその安全性の分類を宣言し、ステータスコードはドアの正直な出口です。エラーを包んだ 200 OK は決して返さないでください。ワイヤー上のドアには、プロセス内の対になるドアと同じ名前を必ず付けてください(§5.1)。
# Identity — the one trust transition, at the edge
POST /v1/sessions 201 Created # = authenticate; 401 on bad credentials
DELETE /v1/sessions/current 204 No Content # = log out
# Money — two doors, one chokepoint, idempotent under retry
POST /v1/payment_intents 201 Idempotency-Key: <key> # = authorize_charge (reversible)
POST /v1/payment_intents/{id}/capture 200 Idempotency-Key: <key> # = settle_payment (IRREVERSIBLE)
# 409 Conflict if the key is replayed with a different body
# 422 Unprocessable if the intent was never authorized
# Access — authority demanded by the route, destructive door made idempotent
POST /v1/accounts/{id}/grants 201 (admin scope required) # = grant_access
DELETE /v1/grants/{id} 204 (204 even if already revoked) # = revoke_access
# Publishing — the domain's own verb, refusing to clobber a concurrent edit
POST /v1/drafts/{id}/publish 200 If-Match: <etag> # = publish_draft
# 412 Precondition Failed if the draft moved under you — the wire's --force-with-lease
gRPC を使用する場合は、同じ接合部を .proto で定義してください。型付きリクエストメッセージが、構造上エアロックの役割を果たします。正直なステータスコード(INVALID_ARGUMENT、PERMISSION_DENIED、NOT_FOUND、ALREADY_EXISTS、FAILED_PRECONDITION、再試行可能な ABORTED/UNAVAILABLE)を使用してください。エラーフィールドを含んだ単独の OK は決して返さないでください。
5.3 データモデル/スキーマ
指示:ER 図または JSON スキーマを提示してください。正規化と非正規化について説明してください。不正な状態を表現できないスキーマを優先してください(独立したブール値フラグではなく、合計型のステータス列を使用します)。
テーブル: invoices(PostgreSQL)
| 列 | 型 | 制約 | 説明 |
|---|---|---|---|
id | UUID | PK | |
user_id | UUID | FK -> Users | パーティションキー |
status | ENUM | 'DRAFT','LOCKED','PROCESSING','PAID' | 3 つのブール値ではなく合計型です |
5.4 アルゴリズムと状態管理
指示:複雑なロジック、状態マシン、または一貫性モデルを説明してください。各状態遷移を、それを実行するドアに結び付け、動作を明確にする最小限のビューを選んでください。アルゴリズムにはインデントした text の疑似コード、ランタイムの制御フローには呼び出しツリー、インタラクションまたはデータフローには Mermaid、既存の状態や制御フローの形状を変更する場合は対象のトピックに合った diff を使用してください。到達可能な場合は、失敗、再試行、キャンセル、冪等性、並行性の動作を含めてください。不明点は作り上げず、§9 に記載してください。
on(settle_payment)
if request is a replay
return the recorded settlement
validate the authorized charge
if provider call fails
return named retryable error
persist settlement
return settlement
publishDraft
authenticate
loadDraft
checkVersion
persistPublication
notifySubscribers
- 状態マシン: 請求書は
DRAFT→LOCKED→PROCESSING→PAIDと遷移します。PROCESSING → PAIDの遷移は、settle_paymentを通じてのみ発生します。 - 並行性:
version列で楽観的ロックを行います。ワイヤー上ではIf-Match/412として表れます。 - データフロー: それらの段階に到達できる場合は、境界入力、解析、正規化されたドメイン入力、サービスドア、アダプター呼び出し、型付きの結果/エラー、射影、シリアライズされた出力を Mermaid 図または簡潔な矢印の連鎖で示してください。
6. 検討した代替案
指示:代替となるドアの構成(例:1つの万能エンドポイントと、個別のジョイント)も含め、トレードオフを検討したことを示してください。推奨案を確定する前に、実質的に異なる代替案を検討し、選択した境界のほうが優れている理由を記録してください。インターフェースの形状、継ぎ目の配置、所有権、呼び出しスタック、ランタイムトポロジー、モジュール境界を比較してください。名前だけを比較してはいけません。
| オプション | メリット | デメリット | 却下理由 |
|---|---|---|---|
オプション A:単一の POST /execute {action} | ルートが1つで、柔軟です | 万能ドアです。意図がペイロードに隠れ、危険な操作が集約されません | 「ツールではなくジョイント」と「危険なドアは少なくする」に反します。 |
オプション B:ワンステップの chargeCard() | 呼び出し回数が最少です | 取り消し可能な保留がなく、リトライすると二重請求になります | 二重請求を表現不可能にできません。 |
オプション C:authorize + settle(選択) | 取り消し可能な保留が可能で、単一のチョークポイントがあり、冪等です | 呼び出しが1回ではなく2回になります | 選択: 2つの実際のジョイントを設け、取り消し不能な効果を1か所に集約できます。 |
7. 横断的な懸念事項
7.1 セキュリティとプライバシー
指示:「危険なドアは少なく、正直なものにする」と「境界にエアロックを設ける」を具体化する箇所です。
- 信頼の移行は1か所です: 信頼されていない呼び出し元が信頼されるようになるのは、
POST /v1/sessions/ゲートウェイだけです。匿名の呼び出し元を昇格させるドアはほかにありません。(ルーブリック #7。) - 権限は型で保持します: 破壊的な操作や特権を要するドアは、
authenticateだけが発行できる機能(AdminSession)を要求します。呼び出し元では権限チェックを忘れようがありません。権限チェックなしで呼び出せる箇所が存在しないためです。(ルーブリック #6。) - 取り消し不能な効果は1つのチョークポイントを通過します: 金銭は
settle_paymentを、削除は唯一のガード付きドアを通過します。壊滅的な処理は明示的に要求する必要があります。(ルーブリック #8。) - データ保護: PII(氏名、メールアドレス)は保存時に暗号化します(AES-256)。
Passwordはニュータイプで、誤ってログ記録、出力、比較できないようにします。 - 脅威モデル: 主な脅威は API キーの侵害です。対策として、迅速なローテーションとレート制限を行います。
8. テスト計画
指示:各ドアについて、想定どおりに動作する場合だけでなく、拒否する場合もテストしてください。ルーブリック #5 のすべての出口をテストする必要があります。公開ドアと継ぎ目を通る垂直方向のレッド・グリーン・リファクタリング(RGR)スライスを計画してください。各スライスでは、まず動作テストを1つ失敗させ、それをグリーンにする最小限の実装を追加し、その後、動作を変えずにリファクタリングします。すべてのテストを先に水平にまとめて書かないでください。インタラクティブ検証があれば、人間や別のエージェントは実装の中身を読まずに機能が正しいことを確認できます。時を越えた見知らぬ人によるテストを、実行可能な形にしたものです。
- ユニットテスト: 各ドアに名前付きで定義された失敗パターン、および拒否(例:
settle_paymentがAuthorizedCharge以外を受け付けないことを証明する型/構築テスト)。 - エンドツーエンドテスト: 実際の §5.2 のワイヤードアを通して実行する、ジョイントで定義された完全なドメインフロー(register → authenticate → authorize → settle)。
- 統合テスト: 再試行時の冪等性(同じキー → 決済は1回だけ);並行編集時の
412;信頼状態の遷移(authenticate以外のドアは、匿名の呼び出し元を昇格させない)。 - ファズ/プロパティテスト: 不正な入力や敵対的な入力をドア(エアロック)に与えます。境界は型が禁止するすべての入力を拒否し、コアを決してクラッシュさせないようにします。ランダムな入力に対する不変条件を検証します(例:
settle_paymentは再試行がどのような順序で行われても、1回の決済に収束する;金銭の移動に至る入力シーケンスは、必ずチョークポイントを通る)。 - インタラクティブ検証: 人間または別のエージェントが実行して、機能が正しく実装されたことを確認できるチェックリストまたはスクリプト。各ステップでドアを指定し、入力を与え、想定される正直な出口(ステータスコード/名前付きエラー/結果の状態)を示して、境界だけから正しさを確認できるようにします。実行する正確なコマンドまたはリクエストと、それぞれの合格/不合格条件を含めてください。
9. 未解決の質問/課題
指示:判明している未確定事項を列挙してください。ドキュメントを「承認済み」とする前に、これらを解決する必要があります。明確に回答できないルーブリックを持つドア、特に未定義の保証(ルーブリック #2、最も危険なケース)や、単一のチョークポイントにまだ集約されていない不可逆な影響(ルーブリック #8)をすべて含めてください。対比を用いた確認を通じて、これらをユーザーと解決してください。
-
publish_draftはドラフトを公開状態にする唯一のドアですか、それとも管理パネルからも公開できますか?(後者の場合、その影響はまだ集約されていません — ルーブリック #8。) - 部分的なプロバイダー障害時に
authorize_chargeが具体的に保証することは何ですか。保証は定義されていますか?(ルーブリック #2。) - PDF 生成に使うサードパーティー製ライブラリを法務チームは承認しますか?
- 現在の VPC ピアリングで、レガシーなメインフレームに接続できますか?