はじめに
こんにちは鷹野です。
私は4月から仕様駆動開発(SDD)を取り入れるための新チームにジョインしました。
Claude Code もその時から触り始めた初心者です。そんな中、約2ヶ月の仕様駆動開発を経験して感じたのは、1人でやるのとチームでやるのは全然違うということです。
1人で開発していたときは、前提が自分の頭の中で揃っているので、特に不満なく思いついた仕様をそのまま AI に渡してどんどん書かせることができました。ところがチームになると、メンバーや AI で「何を作るか」の認識が少しずつズレています。そのせいで、あとから「ここが足りない」「ここが認識と違う」「そもそもここは考えられていなかった」という抜けや食い違いが次々と出てきて、そのたびに手戻りすることが増えました。
そこで試したのが、OpenSpec と grill-with-docs という2つのツールの組み合わせです。提案書を書く前に AI のほうから質問を飛ばしてもらって、認識のズレを入口で潰してしまう、というやり方です。
この記事では、2つのツールの導入とつなぎ込みのセットアップ手順を一通り紹介したうえで、grill-with-docs の「あり・なし」で生成される仕様がどう変わるかを実際に比べてみます。仕様駆動開発をチームで回し始めた人や、これから試そうとしている人、そして「何を仕様に書けばいいか分からない」と仕様作成でつまずいている人に対して、何かしら参考になればうれしいです。
登場するツール
最初に、今回の主役である2つのツールを紹介します。
OpenSpec
OpenSpecはコードを書く前に「何を作るか」を仕様として固めておくための、軽量な SDD フレームワークです。新規プロジェクトだけではなく、既存プロジェクトにも馴染みやすいのが売りです。
「現行の仕様」を置く specs/ と「今回の変更点」を置く changes/ をきっちり分けて管理できます。
ざっくりした流れは以下です。
- やりたい変更を
/opsx:propose(新しい変更なら/opsx:new)で提案する - AI が
changes/以下に提案書・仕様・設計・タスク(proposal.md・specs/・design.md・tasks.md)を生成する - 内容をレビューして、
/opsx:applyで実装に入る - 完了したら
/opsx:archiveでアーカイブし、その変更を現行仕様の側に取り込む
grill-with-docs
grill-with-docs は、一時期 SNS でも話題になった「AI に質問攻めさせるスキル」 grill-me の後継・発展形です。
両者の違いをざっくり言うと、こうなります。
-
grill-meは、設計の決定を一問ずつ詰めて、依存関係を解きほぐしていく。前提の共有が少なくても回せる。 -
grill-with-docsは、CONTEXT.md(その文脈の共有言語・用語をまとめたファイル)があればそれを土台に質問し、無ければ対話の中で作りながら進める。
違いを生んだのは「grill-me だと毎回ドメインの前提を説明し直すのが大変だった」という作者の課題感です。そこで grill-with-docs は、既存の CONTEXT.md があればそれを土台に質問します。曖昧な用語は用語集(Glossary)と突き合わせて指摘し、対話の中で固まった決定はその場で CONTEXT.md に書き足し(無ければ自動で作り)、非自明な判断は ADR(設計判断の記録)に残していきます。
つまり、単発の「要件定義の壁打ち」から、ドメイン用語のズレを見つけて揃えてくれる存在へ進化した、というのが自分での理解です。個人で小さく回すなら grill-me で十分だと思いますが、用語やドキュメントが積み上がったチーム開発ほど、この性質が効いてくると思います。
なぜ OpenSpec + grill-with-docs の組み合わせにハマったのか
「AI に先に質問させて要件を詰め、そこから仕様・設計・タスクへ落とす」という流れは、実は Superpowers でもほぼ同じことができます。
ブレインストーミングで質問を投げ、承認した設計から実装計画まで一気通貫で進めることができる方法です。grill-with-docs の「質問で前提を揃える」発想とも近いです。
それでも OpenSpec と grill-with-docs を組み合わせたのは、チーム開発で見据えたい 2 点があったためです。
ひとつは、仕様の履歴を Git でどう残すかです。OpenSpec は現行仕様を置く specs/ と、今回の変更点を置く changes/ を明確に切り分ける思想です。「今いじるのはここだけ」という差分がそのまま成果物として Git に残るので、新規でも既存でも、変更の単位がそのまま履歴として積み上がっていきます。仕様を少しずつ更新しながら進めるチーム開発にとても合致する方法だと思いました。
もうひとつは、ワークフローそのものに手を入れられるかです。Superpowers も writing-skills というメタスキルでスキルを自作・改変でき、拡張性は高いです。ただ拡張は基本「スキルを新しく書く」方向で、全体の進め方は方法論としてある程度決まっています。一方 OpenSpec は、ワークフローの定義そのものが schema.yaml というファイルになっていて、フォークすれば各ステップのテンプレートや AI への指示を直接書き換えられます。今回やったカスタマイズも、proposal ステップの指示に「提案書を書く前に grill-with-docs を走らせる」という一文を足しただけです。設定ファイルを編集する感覚でワークフローに手を入れられる。この手軽さがチーム開発に合っていると思いました。
OpenSpec のセットアップとスキーマのカスタマイズ
ここからは実際に手を動かしていきます。OpenSpec をインストールして、spec-driven スキーマをフォークし、最後は grill-with-docs を提案フローに組み込むところまで一気に進めます。
OpenSpec のインストール
OpenSpec は npm パッケージとして配布されています。グローバルに入れます。なお Node.js は 20.19.0 以上が必要です。
npm install -g @fission-ai/openspec@latest
プロジェクトへの初期化
機能追加したい既存プロジェクトのルートに移動して、初期化します。
cd your-project
openspec init
openspec init を実行すると、対話形式で「どの AI ツール向けに設定を作るか」を聞かれます。Claude Code を使うなら claude を選びます(cursor や github-copilot など 25 種類以上に対応しています)。
完了すると、プロジェクト直下に openspec/ ディレクトリが作られます。中身は現行仕様を置く specs/ 、変更点を置く changes/ 、そして設定ファイルの config.yaml などです。
匿名利用統計をオフにする
OpenSpec は匿名の利用統計を送信します。チームのポリシー上オフにしたいこともあると思うので、最初に止め方をおさえておきます。
OPENSPEC_TELEMETRY=0 または DO_NOT_TRACK=1 を環境変数に設定すれば無効化できます。
Claude Code から openspec を実行している場合は、.claude/settings.json の env ブロックに書いておくのが手軽です。Claude Code が Bash 経由でコマンドを実行するときに、この環境変数が渡されます。
{
"env": {
"OPENSPEC_TELEMETRY": "0"
}
}
注意点として、この書き方が効くのは Claude Code セッション内から実行したときだけです。
ターミナルで直接 openspec を叩くときにも常にオフにしたいなら、~/.zshrc に export OPENSPEC_TELEMETRY=0 を足すか、openspec config 配下のグローバル設定を併用してください。
ワークフローのプロファイル選択
OpenSpec には作業の進め方を決める「プロファイル」があります。/opsx:new や /opsx:continue といったスラッシュコマンドは、選んでいるプロファイルによって使えたり使えなかったりします。以前に設定済みで最初から使える人はこの節を読み飛ばして大丈夫です。
既定の core プロファイルで使えるのは /opsx:propose などの基本コマンドです。/opsx:new や /opsx:continue が見当たらない人は、拡張側のプロファイルに切り替えると /opsx:verify ・ /opsx:bulk-archive まで含めて使えるようになります。
切り替えは openspec config profile を引数なしで実行し、対話形式で拡張側を選びます。
openspec config profile
openspec update
続く openspec update は、プロファイルの変更を AI 向けの指示ファイル( .claude/commands/opsx/ 配下のコマンド定義)に反映するコマンドです。これを忘れるとコマンドが生えてこないので注意してください。
spec-driven スキーマをフォークする
ここが今回の肝です。OpenSpec の標準スキーマ spec-driven をそのまま使うこともできますが、チーム固有のルールを入れたいので、自分のプロジェクトにコピー(フォーク)して編集できる状態にします。
openspec schema fork spec-driven spec-driven-custom
これで、編集自由なスキーマ一式が次のように作られます。
openspec/schemas/spec-driven-custom/
├── schema.yaml
└── templates/
├── proposal.md
├── spec.md
├── design.md
└── tasks.md
schema.yaml がワークフローの定義本体です。proposal → specs → design → tasks という生成の流れや、各アーティファクトの依存関係(どれを作るには何が先に必要か)が書かれています。
templates/ 以下は、AI が各ドキュメントを生成するときの雛形です。提案書に必須セクションを足したり、用語の統一ルールを書き込んだりするのは、ここを編集して仕込みます。
フォークしたスキーマを使う設定にする
最後に、プロジェクトの openspec/config.yaml で、使うスキーマを今フォークしたものに切り替えます。
# openspec/config.yaml
schema: spec-driven-custom
これで毎回 --schema spec-driven-custom を付ける必要がなくなり、コマンドがスッキリします。
ちなみに、フォークしたスキーマがちゃんと認識されているかは次のコマンドで確認できます。編集後に構造が壊れていないかのチェックにも便利です。
openspec schema which spec-driven-custom
openspec schema validate spec-driven-custom
ここまでで OpenSpec 側の土台ができました。schema.yaml や templates/ を編集すればチームのルールを書き込めます。次は、もう一方の主役である grill-with-docs を入れて、この土台とつないでいきます。
grill-with-docs のインストール
もう一方の主役、grill-with-docs を入れます。プロジェクトのルートで次を実行します。
npx skills add https://github.com/mattpocock/skills --skill grill-with-docs
これで .claude/skills/ 配下にスキルが追加され、Claude Code から grill-with-docs を呼べるようになります。
このスキルは、ドメイン用語や共有言語をまとめた CONTEXT.md を土台に質問してきます。といっても事前に用意しておく必要はありません。CONTEXT.md が無ければ、対話の中で用語がひとつ固まった時点で自動で作られ、そのまま少しずつ育っていきます。既存プロジェクトですでに CONTEXT.md がある場合は、それを読み込んで突き合わせてくれます。
grill-with-docs を提案フローに組み込む
両方そろったので、最後に 2 つをつなぎます。さきほどフォークした schema.yaml には、各アーティファクトへの AI 指示( instruction )がデフォルトで入っています。今回は proposal の instruction の先頭に、grill-with-docs を呼ぶ一文を足すだけです。
- id: proposal
generates: proposal.md
description: Initial proposal document outlining the change
template: proposal.md
instruction: >
BEFORE writing the proposal, run a grilling session to lock down
requirements and terminology. Invoke the `grill-with-docs` skill (use the
Skill tool with skill name `grill-with-docs`): interview the user one
question at a time, challenge vague or overloaded terms against
`CONTEXT.md`, cross-reference the codebase for contradictions, and update
`CONTEXT.md` / ADRs inline as decisions crystallise. Only once you have a
sharpened, shared understanding should you draft the proposal — and write
it FROM the outcome of that grilling session.
Create the proposal document that establishes WHY this change is needed.
ポイントは、Create the proposal document... から始まる元の指示はそのまま残し、その前に「提案書を書く前に grill-with-docs で要件と用語を固めろ」という前段を差し込んでいるところです。
これだけで、OpenSpec が提案書を生成しようとするたびに、まず grill-with-docs が走って要件と用語を詰め、その結果を踏まえてから proposal.md を書く、という流れが固定されます。
以上でツールの導入から提案フローへの組み込みまでが完了しました。
次は、この仕込みの効果を実際に確かめてみます。
【検証】grill-with-docs「あり・なし」で成果物はどう変わるか
ここで確かめたいのは、いきなり OpenSpec で提案書を作るのではなく、先に grill-with-docs でドメインの認識を揃えてから渡す、という順番の効果です。この順番にするだけで、生成される仕様の精度が一段変わります。なかでも一番違いが出てほしいのは、最初に出力される proposal.md の質です。
この検証では同じお題で、grill-with-docs のあり・なしを比べてみました。お題はあえて「タスク管理アプリ」というありふれた簡単なものにしています。
実際に叩いたコマンド
手順はたった 2 ステップです。Claude Code 上で次を順に実行しました。
/opsx:new 通知機能付きのタスク管理アプリを実装したい
/opsx:new で新しい変更(change)を起こします。お題はこのときに一緒に渡しています。あり・なしのどちらもお題はこの一文で固定です。
/opsx:continue
/opsx:continue で次のアーティファクト、つまり proposal.md の生成に進みます。ここからが grill-with-docs のあり・なしの分かれ道です。さきほど schema.yaml の proposal ステップに仕込んであるので、あり版ではこの /opsx:continue のタイミングで質問攻めが走り、その結果を踏まえてから提案書が書かれます。なし版は、仕込んでいないスキーマでまったく同じ手順を実行しただけです。
実際に走らせると、こんな感じで進みます。
提案書をいきなり書き始めるのではなく、まず既存のコードベースと CONTEXT.md ・ ADR を確認しにいっているのがわかります。今回はグリーンフィールドで何も無かったので、「このプロジェクトはグリーンフィールドなので CONTEXT.md に書き起こしていく」と判断して、対話の中でドメインモデルを作り始めました。
そこから 1 問ずつ詰めていきます。画面は「通知のトリガーをどう定義するか」を複数選択で聞いてきている場面で、期限ベースのリマインダー・オーバーデュー通知・繰り返し通知といった選択肢が並んでいます。ここで「期限ベース」を選んだことが、grill-with-docs あり版の提案書にある相対オフセットのリマインダー仕様につながっています。
こうして決まったことが CONTEXT.md に書き足され、非自明な判断は ADR に残る。その積み上がった前提を土台にして、ようやく提案書が書かれる、という流れです。
grill-with-docs なしの場合
体裁は整っています。ただ、こちらが渡したのは一行のお題だけなので、足りない前提を AI が推測で補いながら書いた提案書になりました。
たとえば、こちらからは触れていないブラウザのプッシュ通知やスケジューラが、スコープに含まれています。その実現方法は「バックグラウンドジョブ/タイマー」とだけ書かれていて、常時起動サーバーが必要かどうかまでは決まっていません。通知まわりも task-notifications という一つのまとまりに収められていて、取りこぼした通知の扱いや繰り返しといった細かい挙動は触れられていないままです。前提を渡していないので、AI が埋められる範囲で埋めてくれた、という結果ですね。あえて簡単なお題にしたぶん、この「どう補ったか」の違いが素直に見えてきます。
grill-with-docs なしの場合に生成された proposal.md(クリックで展開)
## Why
ユーザーはタスクを一元的に登録・整理・追跡できる場所を必要としており、締め切りを逃さないために事前のリマインドも欠かせません。通知機能のないタスク管理ツールでは、ユーザーが自分でリストを見に行かない限り期限切れに気づけません。本変更では、リマインドを後付けではなく中核機能として備えたタスク管理アプリを導入します。
## What Changes
- タスクの作成・閲覧・編集・完了・削除を行えるようにする。
- 各タスクはタイトル、任意の説明、ステータス(未着手 / 進行中 / 完了)、優先度、任意の期限を持つ。
- タスクをステータス・優先度・期限で一覧表示・絞り込みできる。
- 期限が近いタスクや期限切れのタスクをユーザーに知らせる通知機能を追加する。
- ユーザーはリマインドの発火タイミング(例:期限時刻、および期限前のリードタイム)と通知チャネルを設定できる。
- 通知はアプリ内通知とブラウザのプッシュ通知で配信する。配信方式はプラガブルにし、後からメールなどのチャネルを追加できるようにする。
- スケジューラが期限到来タスクを評価し、ユーザーがリロードしなくても通知を送出する。
## Capabilities
### New Capabilities
- `task-management`: タスクのライフサイクル(作成・取得・更新・完了・削除)の中核機能。フィールド(タイトル、説明、ステータス、優先度、期限)と一覧・絞り込みの挙動を含む。
- `task-notifications`: リマインドルール、期限到来/期限切れタスクの通知スケジューリング、ユーザーの通知設定、チャネル(アプリ内・ブラウザプッシュ)をまたぐ配信。
### Modified Capabilities
<!-- なし — 既存のスペックが存在しないグリーンフィールドの変更です。 -->
## Impact
- **新規コード**: タスクのドメインモデルと永続化、タスク CRUD の API/サービス層、通知のスケジューリング・送出サービス、通知設定の保存、タスク一覧とリマインド設定の UI。
- **API**: タスク CRUD、タスク絞り込み、通知設定のための新規エンドポイント。
- **依存関係**: 期限到来タスクを評価するためのスケジューリング機構(バックグラウンドジョブ/タイマー)。配信のためのブラウザプッシュ通知サポート(Service Worker + Push API)。
- **データ**: タスクおよびユーザーごとの通知設定のための新規永続ストア。
grill-with-docs ありの場合
どこまで作ってどこから作らないか、その線引きまで先回りして考えられた提案書になりました。
「アプリ内通知のみで Web Push は採用しない」「バックエンド・認証・同期は持たない」とスコープを決め切り、その判断は ADR(設計判断の記録)として根拠ごと残しています。タブを閉じている間に発火すべきだった通知を次回起動時にキャッチアップ生成する、といったエッジケースまで詰めてあり、機能も責務ごとに 5 つの Capability へ分かれています。同じ一行のお題から始めたのに、こちらの推測任せにならず、聞かれた質問に答えていくだけで前提が埋まっていきました。
grill-with-docs ありの場合に生成された proposal.md(クリックで展開)
## Why
個人が自分のタスクを管理し、期限やリマインダーに基づいて「やるべきこと」に気づける仕組みが欲しい。既存の汎用ToDoではなく、リマインダー・繰り返し・オーバーデュー検知を一体で扱い、かつサーバー運用・アカウント不要で即使える、軽量な単一ユーザー向けWebアプリを初版として立ち上げる。
## What Changes
- **タスク管理**: タスクの作成・編集・削除・完了切り替え。属性はタイトル(必須)・期限(任意)・優先度(任意)・メモ(任意)・完了状態。
- **オーバーデュー検知**: 期限を過ぎ未完了のタスクを自動的にオーバーデュー状態として識別・表示する。
- **リマインダー**: タスクに発火トリガーを複数付与できる。発火時刻は「期限からの相対オフセット(例: 10分前)」または「絶対時刻」で指定する。
- **繰り返し**: 繰り返し規則(Recurrence Rule)が個々のタスク実体を生成する。各回は独立して完了/オーバーデュー状態を持つ。
- **分類**: タスクを高々1つのリスト(多対一)にまとめ、横断的なタグ(多対多)で分類・絞り込みできる。
- **アプリ内通知**: リマインダー発火を記録した通知を、既読/未読を持つインボックスとして提示する。タブを閉じている間に発火すべきだった分は、次回起動時に取り逃し分を全件キャッチアップ生成する。
- 配信はアプリ内通知のみとし、Web Push は採用しない(ADR-0001)。
- データはブラウザ内に永続化し、バックエンド・認証・デバイス間同期を持たない(ADR-0002)。
- **スコープ外(将来)**: Web Push通知、複数デバイス同期、複数ユーザー/共有。
## Capabilities
### New Capabilities
- `task-management`: タスクの作成・編集・削除・完了切り替えと、期限・優先度・メモの管理、オーバーデュー状態の自動判定。
- `task-organization`: タスクのリスト所属(多対一)とタグ付け(多対多)、およびそれらによる分類・絞り込み。
- `reminders`: タスクへのリマインダー付与(相対オフセット/絶対時刻)と発火時刻の評価。
- `recurrence`: 繰り返し規則の定義と、そこからのタスク実体生成。
- `notification-inbox`: 発火通知の永続化・既読/未読管理・キャッチアップ生成・インボックス表示。
### Modified Capabilities
<!-- 既存 spec なし(グリーンフィールド)。要件変更による delta spec は不要。 -->
## Impact
- 新規グリーンフィールドプロジェクト。既存コードへの影響なし。
- アーキテクチャは完全クライアントサイド(静的ホスティングで配信可能)。ブラウザ内永続化(IndexedDB/localStorage)を採用。
- リマインダー発火の評価はアプリ起動中にクライアントで行う(常時起動サーバー・Service Worker不要)。
- ドメインモデル(Task / Reminder / Notification / Recurrence Rule)は永続化手段・配信経路から分離し、将来の同期・Web Push 追加に備える。
どこに差が出たか
パッと見の分量は、あり・なしでそれほど変わりません。ですが同じお題なのに、中身を読み比べるとかなりの差を感じました。今回とくに気になった 4 つの観点で並べてみます。
| 観点 | なし | あり |
|---|---|---|
| スコープ | 「後から追加」と曖昧。Web Push もスケジューラも勝手に盛り込む | アプリ内通知のみ・バックエンドなしと決め切り、スコープ外も明記 |
| 判断の根拠 | 決め切らず保留したまま | ADR に理由ごと記録され、後から追える |
| 機能の粒度 | 2 つ。task-notifications に詰め込みすぎ |
責務ごとに 5 つ。後続の spec.md に落としやすい |
| 整合性 | 「リロード不要で通知」と言うが実現手段が未定 | 起動中に評価し、閉じていた分は次回キャッチアップ、と一貫 |
チーム開発こそ、AI に先回りして質問してもらうと効く
ネットでは「grill 系は AI の行動を縛りすぎるから不要、もっと自由に書かせるべき」という意見を見かけました。
個人の爆速開発という文脈なら、その通りだと思います。制限なしが最速な場面は確かにあると思います。
しかし、チーム開発では話が変わるのでは?と考えるようになりました。
まだ数ヶ月ですが、チーム開発においての仕様駆動開発で一番コストが大きいのは、あとから決め切れていない部分に気づくたびに、AI へ「ここが違う」「ここが足りない」と伝え直す往復だと思っています。さっきの検証でも見たとおり、その部分が多いほどこの往復は増えます。そして往復が増えるほど、こちらの見落としや AI の取りこぼしが重なって、抜け漏れが手戻りになって返ってきます。
どうせあとから不足や間違いをその都度指摘するなら、その前に潰せるものは AI のほうから質問してもらって、先になくしておくほうがいい。grill-with-docs はまさにそれをやってくれます。提案書を書く前に AI から質問が飛んでくるので、こちらがあとで気づくはずだった抜けを、入口で先回りして埋められます。個人開発なら前提は自分の頭の中で揃っているので、この質問はただのノイズかもしれません。でも、メンバーや AI で認識がバラつくチーム開発ほど、この「先に質問してもらって潰しておく」やり方が効いてくるのではないかなと強く実感しています。
まとめ
OpenSpec でワークフローの土台を作り、その提案フローに grill-with-docs を差し込む、という組み合わせを試してみました。やったことは schema.yaml の proposal ステップに一文足すだけで、提案書を書く前に必ず AI から質問が飛んでくる流れを固定できました。
同じ一行のお題で grill-with-docs のあり・なしを比べると、なしは「読めるけれど決め切れていない」、ありは「迷いそうなところが先に固めてある」という差がはっきり出ました。スコープの線引きや判断の根拠(ADR)まで入口で埋まる分、あとからの手戻りが減るのことが大きいです。
正直、1人で開発していた頃は AI に質問されること自体が面倒に感じていました。でもチームに入ってからは、認識のズレを入口で潰しておけるこのやり方が、自分には合っていると感じています。仕様をゼロから書き出すのが苦手でも、質問に答えるだけで骨組みが埋まっていくので、これから仕様駆動を始める人の最初の一歩にもいいと思います。
まだ試し始めて数ヶ月なので、これからやり方はどんどん変わっていくかもしれませんが、いろいろ模索していきます。
最後まで読んでいただきありがとうございました!
