write-a-spec について
承認済みの提案を実装仕様書にまとめるスキルです。現行システムの把握とスコープ確認を必須とし、覆しにくい選択の記録や未解決事項の追跡を経て、実装タスクを計画します。
主な機能
- 01承認済み親提案の確認
- 02現行コードと文書の把握
- 03スコープ確認ゲート
- 04覆しにくい決定と根拠の記録
- 05未解決事項と検証の追跡
活用シーン
- 01承認済み提案の実装範囲の策定
- 02設計前の現行動作の記録
- 03変更コストの高い決定の根拠保存
- 04実装計画とタスクの準備
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ inkeep/ open-knowledge-skills --skill write-a-specインストール後、Claude Code で/write-a-specを入力して読み込まれたか確認します。
詳細
仕様書を書く — 機能の範囲を定め、実装仕様をコミットする
このスキルはパックのガイダンスです。プラットフォームの
/open-knowledgeスキル(読み取り/書き込み/プレビュー/リンク/グラウンディングのルール)は、すべての Markdown 操作を引き続き規定します。このスキルは、その上に仕様書作成の技法を重ねます。
仕様書は、「これを作ると決めた」ことと、「これがその作り方です」の間をつなぐ契約です。ACCEPTED の提案から導き出され、変更を提案する前に TODAY のシステムを対応付け、作成者がいなくなった後も判断の根拠が残るよう、一方通行の決定を Decision Log に記録します。現行システムの対応付けを省略したり、未解決の疑問を黙って解決したりする仕様書は、負債になります。根拠がないのに権威あるもののように読めてしまうからです。
specs/ は仕様書ごとにフォルダーを分ける形式です。specs/NNN-name/ に spec.md を置き、(実際に作業を実施する場合は)plan.md と tasks.md も格納します。このパックには3つのテンプレートが含まれています。spec.md の status は draft → ... → shipped と遷移します。
自律モード
| モード | 動作 | 開始方法 |
|---|---|---|
| 監督付き(デフォルト) | ステップ2のスコープ設定ゲートで停止し、Goals/Non-goals/変更の形についてユーザーの確認を求めます。未決定事項は対話形式で進めます。 | ユーザーがセッションを進める場合のデフォルトです。 |
| ヘッドレス | スコープ案を提示した後、自動で承認します。判断が必要なルーティングも自動で選択します。その他のすべてのゲート(親提案の確認、現行システムの対応付け、Decision Log、検証、グラウンディング)は引き続き適用されます。 | 「私を待たずに進めて」「そのまま進めて」「ヘッドレスで実行して」という明示的な指示、または非対話型のコンテナー環境。 |
ヘッドレスモードでは、スコープを提案したら直ちに作業を進めてください。ユーザーの承認を得ずにスコープを自動承認したことを Decision Log に記録します。
必須の実行順序
必須ゲート — 先に進んで飛ばしてはいけません。 変更対象の実際のコードを読む前に ## Design を書こうとしていることに気づいたら、そこで停止してください。ステップ1を飛ばしています。
- 親提案を見つけます。 仕様書の根拠となる ACCEPTED の提案がない場合は、要注意です。
- 現行システムを対応付けます。 実際のコード、過去の仕様書、ガイドを読みます。参照先を示しながら、現在あるものを記述します。
- スコープ設定の停止ゲート。 Goals/Non-goals/変更の形を提案します。監督付きモードでは、ここで待ちます。
specテンプレートを使って、仕様書のフォルダーとspec.mdを作成します。NNNは推測せず、一覧を確認して割り当てます。- Goals/Non-goals を記入します — Non-goals は期間を明記し、それぞれに理由を添えます。
- Design — 検討した選択肢、選んだもの、その理由を記述します。一方通行の決定は Decision Log に記録します。
- Decision Log — セクションを追加します。各項目は作成者がいなくなった後も役立つ内容にします。
- Open Questions — 常に更新されるバックログです。それぞれに、解決を示す根拠と決定者を明記します。
- Migration + Test plan — 実際の状態と階層を記述し、未対応部分を明示します。
plan.md+tasks.md— 仕様書に基づいて実装する場合に限ります。- リンク + 検証 — 相互リンクを追加し、デッドリンクをなくし、フロントマターを完成させます。
- 要約 — さらに、
draftから先に進むために必要なことを示します。
ステップ 0:親となる提案を探す
仕様書は、すでに決定された内容を実装するものです。範囲を検討する前に、そこから派生する承認済みの提案を見つけてください。
exec("ls -A proposals/")— フロントマターの情報を補足して、提案の一覧を表示します。exec("ls -A decisions/")— 承認済みの提案には、ここに対応する確定済みの記録があることがよくあります。search({ query: "<feature name or problem statement>" })— 名前がファイル名と異なる場合は、両方のフォルダーを意味的に検索します。- 有力な候補を 1~2 件選び、
exec("cat proposals/0004-x.md")を実行します — 承認済みの設計と未解決の質問をすべて読みます。未解決の質問は「未解決の質問」のたたき台になります。
見つかった内容に応じて進めてください:
- 承認済みの提案がある → パスを記録します。そのパスが仕様書のフロントマターの
parent_proposal:になります。ステップ 1 に進みます。 - 提案はあるが、承認されていない(
draft、fcp、rejected) → 停止してください。 承認されていない提案に基づく仕様書は、砂上の楼閣です。次のように伝えてください:「提案<path>のステータスはstatus: <x>で、承認されていません。仕様書は承認済みの提案に基づいて作成するべきです。先に承認を得るお手伝いをしましょうか。それとも、このまま進めますか?」 - 提案がまったくない → はっきり伝えてください:「この機能の背景となる承認済みの提案はありません。仕様書は提案から派生します。提案では 作るべきかどうか を議論し、仕様書では どのように作るか を定めます。まず提案をまとめることもできます(
/frame-a-proposalスキルを使います)。あるいは、リスクを承知のうえで、提案を省略したことを記録して仕様書を直接作成することもできます。」ユーザーが先に進むことを選んだ場合は、その選択と理由を決定ログ(ステップ 6)に記録してください。提案を黙って省略してはいけません。
必須ゲート: 親となる提案について、いずれかの方法で決着するまで spec.md を作成しないでください。省略する場合は、ユーザーがその旨を認識している必要があります。
ステップ 1:変更を設計する前に、現在のシステムを把握する
仕様書で最もよくある失敗は、実在しないシステムを前提に設計してしまうことです。現在のシステムの動作を把握しなければ、妥当な ## Design や実用的な ## Migration は書けません。
ネイティブのホストツールを使って、実在するコードを読みます。 ソースコード(.ts、.py、設定、スキーマ)はナレッジベースの外にあるため、OpenKnowledge の動詞ではなく、ホストのネイティブファイルツール(Read / Grep / Glob)で読みます。エントリーポイント、変更対象のモジュール、データの構造、境界をまたぐ呼び出しの流れを、最初から最後までたどってください。
OpenKnowledge の動詞を使って、ナレッジベースを読みます。 過去の仕様書やガイドは、対象となる Markdown です。
exec("ls -A specs/")— これまでに仕様書が作成された内容を確認します。同じサブシステムを扱う関連仕様書が、すでに対応範囲を示している場合があります。exec("cat specs/003-x/spec.md")— 最も近い過去の仕様書の「設計」と「移行」を読み、構成と参照先を把握します。exec("ls -A guides/")の後にexec("cat guides/<runbook>.md")を実行します — ランブックからは、コードだけではわからない運用の実態を把握できます。search({ query: "<subsystem name>" })— 設計上の制約となる関連する決定事項や事後分析を探します。
仕様書の「設計」セクションに、まず「現在のシステム」という小見出しを設け、現状を書きます。 ファイルやモジュールへの参照(src/foo/bar.ts、handleX 関数、widgets テーブルなど)も記載してください。具体的な参照先があれば、仕様書の内容を検証でき、レビュー担当者もコードと照らして確認できます。
必須ゲート: 現在のシステムを、実際の参照先とともに記載するまで、提案する ## Design の草案を作成してはいけません。現在の動作がどこに実装されているか示せないのであれば、変更するのに十分な理解には至っていません。
Step 2: スコープ設定の STOP ゲート
仕様本文を書く前に、スコープを提案して確認を得ます。Goals / Non-goals を誤ると、仕様の草案を作成した後で修正するのに大きなコストがかかります。
次の内容をユーザーに提示します。
## Proposed scope
**Parent proposal:** [path — or "none; proceeding without one per your acknowledgement"]
**Goals** (what this spec delivers):
1. [Goal — an observable capability or change]
2. ...
**Non-goals** (explicitly out of THIS spec, and why):
1. [Thing] — deferred because [reason]
2. ...
**Shape of the change:** [2-4 sentences: the approach at a high level, and which modules from the Step 1 map it touches]
**Open questions I already see:** [1-3 — the ones that will gate the design]
監督モード: STOP して確認を待ちます。ユーザーが確認または調整するまで、仕様フォルダーを作成しないでください。
ヘッドレスモード: スコープを提示して、そのまま進めます。Decision Log に自動確認の旨を記録します。
スコープ設定の指針:
- 機能が曖昧な場合は、Design ではなくここで範囲を絞ります。「どの領域ですか? 誰のためですか? 明確に延期するものは何ですか?」
- 各目標の受益者を明記します。恩恵を受ける人がいない目標は、スコープクリープです。
- 提案で定められていない要件を作り出してはいけません(このスキルの Non-goals を参照)。目標が親提案にたどれない場合は、それを指摘して確認を求めます。
Step 3: 仕様フォルダーと spec.md の作成
推測ではなく一覧表示によって、次の NNN を割り当てます。
exec("ls -A specs/")— 既存のNNN-name/フォルダーを確認します。最大のNNNに 1 を足し、既存の桁数に合わせてゼロ埋めします(003→004)。- 機能に基づいた短い kebab-case 名を選びます:
specs/004-payment-retries/。 - 仕様を作成します。
write({ document: { path: "specs/004-payment-retries/spec.md", template: "spec" } })
spec テンプレートには frontmatter と、次の H2 セクションがこの順番で用意されています:## Goals、## Non-goals、## Design、## Migration、## Test plan。ここで frontmatter を記入します。
type: spec
description: "<one line: what this spec delivers>"
status: draft
owner: <user>
target_release:
created: <today YYYY-MM-DD>
parent_proposal: ./proposals/0004-payment-retries.md # or note the skip
tags: [spec]
parent_proposal: には、Step 0 からの相対 Markdown パスを設定します。計画が存在するまでは、target_release: は空欄のままにします。
Step 4: Goals と Non-goals を記入する
edit({ ... }) を使って、## Goals セクションと ## Non-goals セクションを編集します。
Goals — 実装の詳細ではなく、観察可能な成果をそれぞれ記載します。「失敗した決済はバックオフを使って再試行され、最終ステータスが表示される」は目標ですが、「retryCount 列を追加する」は設計上の選択です。各目標を受益者に結び付け、可能であれば親提案の動機にも関連付けます。
Non-goals は恒久的なものではなく、時間的なものです。 それぞれ「この仕様では対象外」と、その延期理由を示す形で記述します。「今後も決して対応しない」としてはいけません。「複数通貨の再試行 — 延期。再試行エンジンは通貨に依存せず、複数通貨対応は独立した提案として扱うため」とすれば、将来の読者に、今は対象外である理由と、今後対応する余地があることが伝わります。「複数通貨には対応しません」では、この仕様に決定権のない恒久的な製品方針のように読めてしまいます。すべての non-goal に、その 理由 を添えます。
Step 5: Design
## Design を編集します。次の 2 つの層を、この順に記載します。
- 現行システム(Step 1 から)— ファイルやモジュールへの参照を添えて、現在存在するものを記述します。これを残すことで、提案する変更が理解しやすくなります。
- 提案する変更 — 検討した選択肢、選んだもの、その理由を記載します。
重要な設計上の分岐ごとに、検討した選択肢、選んだもの、その判断理由を記述します。受け入れたトレードオフも明記します。選んだ案だけを示す設計では、選択が行われた事実が隠れ、選択を厳しく検証する役割を担うレビュアーにも、その事実が伝わりません。
後戻りできない選択はすべて、Decision Log(Step 6)に記録します。 後戻りできない選択とは、その選択に基づくコードがリリースされた後に覆すのが困難、または不可能な決定のことです。ワイヤーフォーマット、データを削除するスキーマ移行、公開 API の形状、セキュリティ境界などが該当します。内部ヘルパーの名前やログレベルなど、変更可能な選択は記録する必要はありません。記録するのは、覆すのに実際のコストがかかる決定に限ります。
ステップ 6:決定ログ
spec テンプレートには決定ログは含まれていません。追加してください。 spec.md の ## Test plan の後に ## Decision Log セクションを追加します。これは仕様書の中で最も価値のある成果物です。新しい担当者が、システムが現在の形になっている理由を、最初から導き直したり、すでに退職した人に尋ねたりせずに理解するためのものです。
各エントリは短いブロックです。
### DL-1: <the question, as a question>
- **Options:** A [...], B [...], C [...]
- **Choice:** B
- **Rationale:** [why B beat A and C — the trade-off accepted, the evidence]
- **Date:** YYYY-MM-DD
- **Reverses if:** [what new fact or condition would force revisiting this]
運用ルール:
- ステップ 5 の一方向の選択ごとに 1 件、さらにプロセス上の決定(例:「 にユーザーの了承を得て、親提案なしで進めた」)ごとに 1 件記録します。
Reverses ifの行は必須です。取り消し条件が明記されていない決定は、正直に再検討できません。- このログの対象はこの仕様書に限られます。プロジェクトの決定記録ではありません。アーキテクチャ上の決定を
decisions/に記録するのは、姉妹スキル/record-a-decisionの役割です。ここに ADR を書かないでください。
ステップ 7:未解決の質問
仕様書では、未解決の質問に印を付けて公開します。黙って解決済みにしてはいけません。回答を推測して確定事項として書くと、後続の全員を誤解させる仕様書になります。
## Open Questions セクションを追加します(テンプレートには含まれていません)。未解決の質問を、随時更新するバックログとして管理します。
### OQ-1: <the unresolved question>
- **Blocks:** [which goal / design decision this gates, or "nothing — nice to know"]
- **Closed by:** [the evidence that would resolve it — a benchmark, a load test, a product call, a spike]
- **Decider:** [who has authority to close it — a role, not a name that will rot]
- **Status:** open
運用ルール:
- 質問が決定ログのエントリを妨げている場合は、両方にその旨を記載します。質問が解決するまでは、ログのエントリを暫定扱いにします。
- 自信ありげな推測よりも、「まだ分からない。調べる方法はこれです」を優先します。何が分かっているかの境界も情報です。でっち上げた回答は危険です。
- 仕様書の作成中に質問が解決したら、
Status: openをresolved → DL-Nに変更し、解決内容を決定ログに移します。質問を単に削除しないでください。
ステップ 8:移行とテスト計画
どちらも実質的な内容にし、飾りにしないでください。「データを移行する」とだけ書かれた移行セクションや、「テストを追加する」とだけ書かれたテスト計画は、何もないより悪いものです。存在しないカバレッジがあるかのように見せてしまいます。
## Migration — 稼働中のシステムが順に経る状態と、各状態で何が成り立つかを記載します。稼働中のシステムは、旧状態から新状態へ瞬時に移行するわけではありません。旧状態と新状態の両方が機能しなければならない中間状態を経ます。
- 状態の例:(1)新しいコードをデプロイし、機能フラグはオフ、旧経路は稼働中。(2)バックフィルを実行し、両方のスキーマを読み取り可能にする。(3)対象グループでフラグをオンにし、二重書き込みを行う。(4)全体でフラグをオンにする。(5)旧経路と旧カラムを削除する。
- 各状態で何を元に戻せるか、そしてどこが後戻りできない地点かを記載します(その地点を決定ログのエントリにリンクします)。
- 保存データについて、何をバックフィルするか、何を削除するか、削除したデータを復元できるかを記載します。
- 移行が不要な変更(新規追加であり、稼働中のデータもデプロイ済みの呼び出し元もない場合)は、その理由とともに明記します。セクションを空欄のままにしないでください。
## Test plan — 何をどの層で検証するか、検証できないものは何か、またその理由を明記します。
- 各目標について、それを証明する層を記載します。ユニット(純粋関数の境界ケース)、インテグレーション(実際の連携先と組み合わせたコンポーネント)、エンドツーエンド(ユーザーから見えるフロー)、コントラクト(双方が遵守すべき境界)。
- 正常系だけでなく、テストすべき障害モードも記載します。リトライの上限到達、途中まで完了した移行、同時書き込みなどです。
- リリース前に検証できないことと、その理由 — 本番環境でのみ確認できる負荷特性、ステージング環境では再現できないサードパーティの動作、実際のトラフィック下でのみ発生する競合状態などです。ギャップを明記すれば、レビュー担当者が許容できるかを判断できます。隠しておくと、そのギャップがインシデントにつながります。
ステップ 9:plan.md と tasks.md
仕様を実際に実装する場合に限ります。 status: draft の仕様が実装されない可能性があるなら、これらはまだ必要ありません。早い段階で作成すると、古くなった足場が残ります。仕様の実装が承認された時点で作成してください。
write({ document: { path: "specs/004-payment-retries/plan.md", template: "spec-plan" } })
write({ document: { path: "specs/004-payment-retries/tasks.md", template: "spec-tasks" } })
両方のフロントマターに parent_spec: ./spec.md を設定します。
plan.md(## Approach、## Phases、## Risks + unknowns、## Dependencies、## Rollout) — 構築方法をフェーズごとに順序立ててまとめます。Risks + unknowns には実装を妨げる未解決の質問を反映し、Rollout には移行の状態を反映します。
tasks.md(## Tasks チェックリスト、## Done when、## Out of scope) — 作業を細分化します。ルールは 2 つです。
- 依存関係の順に並べます。 あるタスクが別のタスクに依存する場合、後に配置します。読み手が上から順に作業できるようにします。
- 各タスクは個別に検証可能にします。 「決済ワーカーにリトライのバックオフを追加 — 枯渇時のインテグレーションテストで検証」はタスクですが、「信頼性を高める」はタスクではありません。タスクの確認方法を説明できないなら、まだ十分に細分化されていません。
## Done when では仕様の Goals を完了基準として再掲します。## Out of scope には Non-goals を反映します。
ステップ 10:リンクと検証
仕様が他からリンクされていないと、孤立してしまいます。仕様をグラフに組み込み、問題がないことを確認します。
- 親提案へのバックリンクを仕様本文にインラインで追加します:
derived from [Payment retries proposal](./proposals/0004-payment-retries.md)。通常の Markdown の相対リンクを使います。バッククォートで囲んだり、HTML アンカーを使ったりしないでください。 - 仕様が依存するすべての決定にリンクします。設計の根拠となる確定済みの記録が
decisions/にある場合は、依拠する箇所にリンクします。 - 親提案(およびハブがあればそのハブ)を更新し、この仕様へのリンクを追加します。これにより、バックリンク経由で仕様を見つけられるようになります。
links({ kind: "backlinks", ... })で逆向きのエッジが登録されていることを確認します。 audit({ path: "specs/004-payment-retries/spec.md" })の結果がクリーンであることを確認します(すべての lint 違反と内部リンク切れがない状態)。見つかった問題はすべて修正します。- フロントマターがすべてそろっていることを確認します:
type、description、status、owner、parent_proposal、created、tagsがすべて存在する必要があります。
ステップ 11:振り返り
ユーザーと最後まで確認します。
## Recap
- Spec at specs/004-payment-retries/spec.md, status: draft
- Derived from [proposal] (or: proceeding without one, logged as DL-N)
- N goals, M non-goals (all temporal, with reasons)
- Decision Log: K one-way-door choices recorded
- Open Questions: J open — [name the ones that block the design]
- Migration: [one line] · Test plan: [what's covered / what can't be]
**To move out of `draft`:** [the specific things — usually: close the design-blocking open questions, get the owner's sign-off, and create plan.md + tasks.md once greenlit for build]
未解決の点があれば、明確に伝えます。仕様が自らの不足を正直に示すことが、信頼性につながります。
対象外
- 提案で確立されていない要件を作り出さないでください。 仕様は「どのように」を定めるものであり、「実施するかどうか」を二度目に判断する場ではありません。親提案に根拠のない目標はスコープクリープです。紛れ込ませず、指摘してください。
decisions/に決定を記録しないでください。 これは兄弟スキルの/record-a-decisionの役割です。仕様の Decision Log は、この仕様における一方通行の決定に限られ、プロジェクトの ADR 記録ではありません。- 現在のシステムのマッピングを省略しないでください。 想像上のシステムを前提に設計することが、最もよくある失敗です。ステップ 1 で実際の参照先を含むマップを作成するまでは、
## Designを書かないでください。 - 作業がリリースされる前に
shippedにしないでください。statusは実態を反映する必要があります。仕様は構築されるまではdraftであり、コードが本番環境で稼働して初めてshippedになります。希望的な判断で変更しないでください。 - 事後分析を書かないでください。 リリースした機能に問題が発生した場合、インシデントの報告書を作成するのは
/write-a-postmortemスキルの役割であり、ここに追記するものではありません。 - 未解決の質問を黙って解消しないでください。 未解決として示された質問は情報です。作り上げた回答は危険を招きます。不明点を見える形で残したまま仕様をリリースしてください。