はじめに

初めまして。 『DApps開発入門』という本や色々記事を書いているかるでねです。

LLMを使った仕様駆動開発を扱う書籍『LLMと進める仕様駆動開発』を発売予定です。ハンズオンや、LLMによって開発がどう変わるのか、実践のTipsをまとめています。

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!

商品検索APIへ価格帯フィルターを追加する時、現在提供している検索機能と今回だけの変更案を同じ文書へ書くと、どこまで実装済みなのか分かりにくくなります。 金額の下限だけを指定できるのか、境界値を検索結果へ含めるのか、下限が上限を超えた時に何を返すのかも、コードを書く前に決めます。

OpenSpecは、現在有効な仕様と作業中の変更内容をリポジトリ内で分け、確認を終えた変更内容を現行仕様へ反映するためのツールです。 価格帯フィルターの追加を例に、ファイル構成、変更の進め方、実装後の検証、アーカイブまでを説明します。

OpenSpecとは

OpenSpecは、現在有効な仕様と作業中の変更案をMarkdownファイルで分けて管理するツールです。 AIコーディングエージェントは同じ記録を読み、計画と実装を進めます。 利用者は、変更理由と外部から確認できる動作を確認します。 技術判断と作業一覧にも目を通し、実装後にどの差分を現行仕様へ反映するか決めます。

OpenSpecが現行仕様と変更内容を分ける範囲

作業中の変更はopenspec/changes/に残り、現在有効な仕様はopenspec/specs/に残ります。 新しい案を作った段階では、現行仕様は書き換わりません。 実装と確認を終え、利用者が現行仕様への反映を承認した時に、変更内容が現行仕様へ反映されます。

AIエージェントとの接続

OpenSpecのCLIは、対象プロジェクトへopenspec/ディレクトリとAIツール向けの操作手順を配置します。 Codexを選ぶと、OpenSpecの操作を記したスキルが.agents/skills/に作られます。

OpenSpec CLIとAIコーディングエージェントの役割

OpenSpec v1.14.2をCodex向けに初期化するコマンドは以下です。

bash
npx --yes @fission-ai/openspec@1.14.2 init \
  --tools codex \
  --language ja \
  --no-animation \
  <project-root>

このバージョンはNode.js 20.19.0以上を必要とします。 初期化に成功すると、openspec/config.yamlとCodex向けの6個のスキルが作られます。 Codex CLIやIDEでは$openspec-proposeのように呼び出し、Codex DesktopではSkillsサイドバーから選びます。

OpenSpecは操作手順と記録形式を管理します。 ソースコードの読取り、ファイルの編集、コマンド実行に対する許可は、Codexと実行環境が管理します。 差分仕様を現行仕様へ反映する時は、AIエージェントが差分仕様と現行仕様を読み、内容を判断して現行仕様を編集します。 ArchiveスキルもAIエージェントが同期の確認と履歴への移動を進め、その途中で状態確認や検証などのCLIコマンドを呼び出します。

ファイル構成

OpenSpecは、設定、現行仕様、進行中の変更、完了した変更履歴を別の場所へ保存します。 1つの変更を開始すると、変更名のディレクトリ内に計画用のファイルが増えます。

OpenSpecを初期化して変更を作成した後のファイル構成

価格帯フィルターをadd-price-filterという変更名で進める場合、主要な構成は以下です。

text
project/
├── openspec/
│   ├── config.yaml
│   ├── specs/
│   │   └── product-search/
│   │       └── spec.md
│   └── changes/
│       ├── add-price-filter/
│       │   ├── .openspec.yaml
│       │   ├── proposal.md
│       │   ├── specs/
│       │   │   └── product-search/
│       │   │       └── spec.md
│       │   ├── design.md
│       │   └── tasks.md
│       └── archive/
└── src/

openspec/config.yamlには、プロジェクトへ追加する規則や使用するスキーマを設定します。 一方、openspec/changes/add-price-filter/には価格帯フィルターの変更だけを保存するため、別の機能変更と判断が混ざりません。

現行仕様と変更内容

openspec/specs/は、OpenSpecへ記録済みの現行動作を機能単位で保存します。 既存プロジェクトへ導入しても、OpenSpecがコードベース全体を自動で仕様へ変換するわけではありません。 実際に変更した範囲から仕様が増えていきます。

現行仕様と変更内容の責任

openspec/changes/<変更名>/specs/に書くのは、今回追加、変更、削除、名称変更する要求です。 例えば、既存の商品検索に価格条件を加えるなら、現行仕様全体を複製せず、価格指定によって変わる動作だけを差分として記録します。

アーカイブ時に現行仕様への反映を承認すると、この変更内容がopenspec/specs/へ反映されます。 同期を承認せずにアーカイブした場合、変更ディレクトリは履歴へ移りますが、現行仕様は更新されません。

変更フォルダーの記録

変更ディレクトリでは、なぜ変えるのか、利用者から何が見えるのか、どう実装するのか、何を確認するのかを別々のファイルへ保存します。

変更フォルダーに保存する4種類の記録

ファイル 保存する内容 読み直す場面
proposal.md 変更理由、対象範囲、影響する機能 変更の目的や範囲が変わった時
差分仕様 外部から確認できる動作と受入条件 利用者に見える動作が変わった時
design.md 技術判断、データの流れ、採用した方法 実装方針や構成を変える時
tasks.md 実装と確認を進める順序 作業の不足や依存関係が見つかった時

design.mdは、すべての変更で必須ではありません。 技術判断を別に残す必要がある時に作成します。 また、利用者に見える動作を変えないリファクタリングや文書修正では、skip_specsを指定して不要な要求を作らない方法もあります。

基本フロー

OpenSpecでは、作業中の変更ディレクトリを1つ選び、以下の順で変更を進めます。 確認で不足が見つかった場合は、問題を保存するファイルへ戻って修正します。

OpenSpecで変更を進める基本フロー

  1. 変更する動作を整理する

    $openspec-exploreで問題、制約、未決事項を確認する。 コードは変更せず、仕様へ固定する動作と追加調査が必要な内容を説明できる状態にする。

  2. 変更の計画を作る

    $openspec-proposeで変更名を決め、proposal.md、差分仕様、必要なdesign.md、tasks.mdを作る。 ファイルが依存順にそろい、各作業に確認方法があることを確かめる。

  3. 利用者が計画を確認する

    変更理由、受入条件、技術判断、作業漏れを確認する。 問題があればMarkdownを直接編集するか、$openspec-update-changeで関連ファイルを更新する。

  4. 作業一覧から実装する

    $openspec-apply-changeでtasks.mdの未完了項目を上から実装する。 完了した項目のチェックボックスがtasks.mdへ反映されていることを確認する。

  5. 記録と実装を別々に検証する

    openspec validate <変更名> --strictで記録形式を確認し、必要ならVerifyスキルで仕様と実装の対応を調べる。 記録形式の確認後に、プロジェクト固有のテストとコードレビューも実行する。

  6. 現行仕様へ差分を反映する

    先に反映する場合はSyncスキルを使い、アーカイブ時に反映する場合は同期確認を承認する。 openspec/specs/が実装済みの動作へ更新されたことを確認する。

  7. 変更を履歴へ移す

    $openspec-archive-changeで変更ディレクトリを日付付きの履歴へ移す。 現行仕様の同期結果とアーカイブ先を別々に確認する。

計画を作って確認する

Proposeスキルは、標準スキーマの依存順に計画ファイルを作ります。 この段階ではソースコードを変更しないため、利用者は実装前に要求と作業範囲を確認できます。

計画ファイルごとの確認対象と修正先

proposal.mdでは問題と変更範囲を確認し、差分仕様では入力条件と期待結果を確認します。 技術判断が必要な変更ではdesign.mdを読み、最後にtasks.mdから実装漏れと確認漏れを探します。 要求が間違っている時は差分仕様へ戻り、実装方法だけを変える時はdesign.mdへ戻せるため、コードだけを直して仕様を置き去りにせずに済みます。

実装して再開する

Applyスキルはtasks.mdのチェックボックスを進捗記録として更新します。 途中でセッションが終了しても、新しいAIコーディングエージェントは変更ディレクトリを読み、最初の未完了項目から再開できます。

tasks.mdを使った中断と再開

再開時は会話履歴を復元する代わりに、tasks.mdと関連ファイルを読みます。 完了済みと未完了の作業は、チェックボックスから判断できます。 計画の誤りが原因で止まった場合は、利用者またはAIエージェントが停止理由を関連する計画ファイルへ明示的に記録し、差分仕様または設計を修正してからApplyスキルを再実行します。

価格帯フィルターの実例

商品検索APIへmin_priceとmax_priceを追加し、価格帯で検索結果を絞り込む変更を考えます。 この変更では、金額の下限だけ、金額の上限だけ、両方の価格を指定する3通りを扱います。 不正な範囲と未指定時の互換性も、同じ変更ディレクトリで追います。

価格帯フィルターの要求から現行仕様への反映

変更を提案する

proposal.mdには、価格帯で商品を絞り込めない問題と、変更する商品検索APIの範囲を書きます。 既存のproduct-search仕様がある場合は、変更対象として同じ機能パスを指定します。

markdown
## Why

利用者が予算内の商品だけを検索できない。

## What Changes

- 商品検索APIへ min_price と max_price を追加する
- 2つの価格入力の片方だけを指定できるようにする
- min_price が max_price を超える入力を拒否する

## Capabilities

### Modified Capabilities

- product-search: 価格帯による絞り込みを追加する

product-searchがopenspec/specs/にまだ存在しない場合は、変更対象ではなく新しい機能として登録します。 既存機能か新規機能かによって差分の反映先が変わるため、Propose後にproposal.mdの機能パスと差分仕様のディレクトリ名を照合します。

要求文を提案と検証可能な場面へ分ける流れ

差分仕様には、外部から確認できる動作を要求と具体的な場面に分けて書きます。 SQLやクラス名は利用者に見える動作ではないため、差分仕様には置きません。

markdown
## ADDED Requirements

### Requirement: 商品を価格帯で絞り込める
検索APIは、指定された価格条件を満たす商品だけを返さなければならない(MUST)。

#### Scenario: 金額の下限だけを指定する
- **WHEN** min_price に 3000 を指定する
- **THEN** 価格が 3000 以上の商品だけを返す

#### Scenario: 金額の上限だけを指定する
- **WHEN** max_price に 10000 を指定する
- **THEN** 価格が 10000 以下の商品だけを返す

#### Scenario: 価格範囲が逆転している
- **WHEN** min_price に 10000、max_price に 3000 を指定する
- **THEN** 検索を実行せず入力エラーを返す

#### Scenario: 価格条件を指定しない
- **WHEN** min_price と max_price を指定しない
- **THEN** 既存の検索結果を維持する

min_priceとmax_priceの境界値を検索結果へ含めることを明記すると、実装者は>と>=のどちらを使うか推測せずに済みます。 また、逆転した範囲で入力エラーを返すと決めれば、検索結果0件として扱う実装との違いもテストできます。

設計と作業へ分ける

受入条件が決まったら、design.mdで入力の型、検証する場所、検索条件の組み立て方を決めます。 例えば、HTTP層で文字列を金額へ変換し、検索サービスで範囲の前後関係を検証し、検索条件ビルダーが下限と上限を個別に追加する構成を選べます。

差分仕様を設計と確認可能な作業へ接続する流れ

tasks.mdでは、実装と確認を依存順に分けます。

markdown
## 1. API入力

- [ ] 1.1 min_price と max_price を任意項目として受け取る
- [ ] 1.2 数値へ変換できない入力を拒否するテストを追加する
- [ ] 1.3 min_price > max_price を拒否するテストを追加する

## 2. 検索処理

- [ ] 2.1 下限条件を検索クエリへ追加する
- [ ] 2.2 上限条件を検索クエリへ追加する
- [ ] 2.3 境界値を含むintegration testを追加する
- [ ] 2.4 価格条件を指定しない既存テストが通ることを確認する

各項目には、変更する処理と確認方法の両方を置きます。 「価格帯フィルターを実装する」という1項目だけでは、金額の下限と上限の実装範囲を判断できません。 不正入力と既存動作の互換性を確認したかどうかも残りません。

検証して現行仕様へ統合する

実装後は、OpenSpecの記録形式とプロジェクトの実行結果を別々に確認します。 変更名を指定して厳格な検証を実行するコマンドは以下です。

bash
npx --yes @fission-ai/openspec@1.14.2 \
  validate add-price-filter --strict

このコマンドが確認するのは、差分仕様の構造や参照関係です。 商品検索APIが境界値を正しく返すことは、プロジェクトの単体テストと結合テストで確認します。 Verifyスキルを利用できるプロファイルでは、仕様、作業一覧、実装の対応も確認します。

現行仕様への反映を承認した場合と承認しない場合のアーカイブ

現行仕様へ先に反映したい場合はSyncスキルを実行し、変更ディレクトリを進行中のまま残せます。 Archiveスキルから同期する場合は、未反映の差分を現行仕様へ適用するか確認されます。 同期を承認すると、追加・変更・削除・名称変更の差分がopenspec/specs/へ反映され、その後に変更ディレクトリが日付付きの履歴へ移ります。

同期を承認しなくても、変更ディレクトリはアーカイブできます。 アーカイブ先の存在と現行仕様の更新を同じ完了条件にせず、現行仕様へ反映したかも確認します。

検証と完了の境界

OpenSpecのValidate、Verify、Archiveと、プロジェクトのテストやコードレビューは確認する対象が異なります。 1つの結果だけで完了と判断せず、変更の種類に必要な確認を組み合わせます。

OpenSpecの確認とプロジェクト固有の品質確認

確認 主に確認する対象 確認できないこと
Validate OpenSpecの記録形式と参照関係 APIが実行時に正しく動くこと
Verify 計画、要求、実装の対応 負荷や本番データでの動作すべて
プロジェクトのテスト 実行時の入力と出力 保守性や設計判断の妥当性すべて
コードレビュー 変更内容、保守性、安全性 すべての実行経路の自動確認
Archive 差分の同期と変更履歴への移動 上記の確認がすべて成功したこと

Archiveは未完了の作業を警告しても、利用者が続行を選べば処理を進められます。 --no-validateを明示すれば、Validationも省略できます。 このため、openspec/changes/archive/に移動済みという事実だけでは、作業一覧、テスト、レビューまで完了した証拠になりません。

仕様の書込みまたは変更ディレクトリの移動が完了前に失敗した場合、OpenSpecは変更前の仕様と進行中の変更を復元します。 再実行する前に、openspec/specs/とopenspec/changes/<変更名>/が元の位置へ戻っているか確認します。 一方、履歴へのコピーが完了した後で元ディレクトリの削除だけに失敗した場合は、完成した履歴と反映済みの仕様が残ります。 この場合は同じ差分を再び同期せず、CLIが報告した履歴パスと残った進行中ディレクトリを確認します。

OpenSpecはGitのコミット、push、マージを実行しません。 チームは、PRをマージしてから現行仕様への反映とアーカイブを行うか、コードとアーカイブを同じPRへ含めるかを決めます。 どちらを選ぶ場合も、採用されたコードとopenspec/specs/の内容が一致する時点をチーム内で固定します。

並行変更の衝突

複数の変更が同じ機能または同じ要求を書き換えると、先に同期した変更によって、後から同期する差分の前提が変わります。 例えば、変更Aが既存要求の名前を変えた後に、変更Bが古い名前を変更しようとすると、変更対象を見つけられません。

同じ要求へ触れる並行変更と修正の流れ

同名の要求を2つの変更が追加する場合も衝突します。 後から同期する変更では、最新のopenspec/specs/を読み直し、差分仕様を現行仕様へ合わせて更新します。 その後、Validate、プロジェクトのテスト、レビューを再実行します。

Bulk Archiveは同じ仕様へ触れる変更を検出し、実装済みの差分を古い順に同期します。 自動で選ばれた順序を承認の代わりにせず、各変更を反映した後の現行仕様を確認します。

向く条件と注意点

OpenSpecは、現在の仕様を維持しながら複数の変更案を分離し、実装済みの差分だけを現行仕様へ反映したいプロジェクトに向きます。 変更ごとのMarkdownをGitで共有できるため、AIコーディングエージェントやセッションが変わっても、同じ変更ディレクトリから再開できます。

OpenSpecを使う条件と工程を減らす条件

以下の条件では、標準フローの効果を得やすくなります。

  • 外部から確認できる動作が変わる

    受入条件を差分仕様として残し、実装後に同じ条件へ戻れます。

  • 複数の変更を並行して進める

    変更ごとにディレクトリを分けられますが、同じ要求へ触れる場合は同期順と衝突の解消が必要です。

  • 仕様の変更履歴をGitで確認する

    現行仕様と日付付きのアーカイブから、採用済みの動作と過去の変更理由を分けて読めます。

  • 仕様を保守する担当を決められる

    コード変更時に差分仕様と現行仕様を更新する運用を継続できます。

数行の明白な修正や、利用者に見える動作を変えない作業では、Issue、テスト、レビューだけで進めると記録を減らせます。 OpenSpec内で変更を追う場合も、リファクタリングや文書修正にはskip_specsを使い、実在しない動作変更を要求として残しません。

標準スキーマは要求と具体的な場面の書式を検証します。 一方、対応できる変更数と仕様数について、公式の保証値は公開されていません。 ファイル容量と実行性能にも保証値はありません。 大きなリポジトリへ導入する場合は、実際の変更数と仕様量でCLIの実行時間を測定します。

最後に

OpenSpecは、現在有効な仕様をopenspec/specs/へ保存し、作業中の変更内容をopenspec/changes/へ分けます。 変更理由と受入条件を別々の記録へ置き、技術判断と作業一覧も分けて保存します。 AIコーディングエージェントはこれらの記録を読み、未完了の作業から再開できます。

完了を判断する時は、アーカイブ済みかどうかだけでなく、現行仕様へ反映したかと作業一覧の状態も確認します。 Validate、必要なVerify、プロジェクトのテスト、コードレビューの結果も個別に確かめます。 この区別ができれば、現在提供している動作と次に取り込む変更を混ぜずに管理できます。

OpenSpecの公式ドキュメントと公式リポジトリは以下です。

他でも色々記事を書いているのでぜひよろしければ読んでいってください!