OpenSpec マージ は何をしますか?
openspec/specs/ 配下にある複数の関連する機能フォルダを、ステージングと承認の2段階ワークフローで1つのメイン仕様に統合します。人間が明示的に承認するまで、openspec/specs/<capability>/ 配下のファイルは一切変更されません。
適用タイミング
OpenSpec のアーカイブワークフローは、変更の差分を openspec/specs/<capability>/spec.md に同期します。関連する変更が異なる機能名でアーカイブされると(多くの場合、段階的に構築されたため、例:user-show、user-detail、user-permissions)、機能的には1つの機能であるにもかかわらず、複数の仕様フォルダが存在することになります。このスキルは、それらを1つに統合します。ただし、実際のファイルを置き換える前に、人間の承認を必要とするステージング領域を経由します。
2つのフェーズ
- フェーズ A — ステージング: ソースの仕様を読み込み、統合ドラフトを
openspec/specs/.merged-specs/<feature>/spec.mdに書き込みます。フロントマターにはApproved-by-human: noを設定します。それ以外の変更は行われません。 - フェーズ B — 適用: ユーザーがフロントマターを
Approved-by-human: yesに変更したときにトリガーされます。承認された内容でソースフォルダを置き換え、ステージングファイルを削除します。
常に適用するフェーズを検出します。openspec/specs/.merged-specs/<feature>/ が既に存在する場合は、最初に Approved-by-human フィールドを確認し、再ステージングは行わないようにします。
フェーズ A — ステージング
1. 候補フォルダを特定する
openspec/specs/* をリストアップし、以下の条件に合致するフォルダを探します:
- 共通の名前プレフィックスまたはドメイン語句を共有している(
user-show、user-detail、user-permissions) - 要件内で同じエンティティ/リソースを参照している(同じモデル、同じ API エンドポイント、同じ UI ページ)
- アーカイブされた日時が近い(
openspec/changes/archive/の日付を確認)
ユーザーが明示的にフォルダ名を指定した場合は、推測ではなくそれらを使用します。
複数の妥当なグループ化が存在する場合は、停止してユーザーにどのフォルダを統合するか尋ねます。
2. 各候補の spec.md を完全に読み込む
各フォルダについて、見出しだけでなく spec.md 全体を読み込みます。要件名、シナリオ、非推奨マーカー、および別の候補フォルダの要件を変更/置き換えるように見えるものをすべて記録します。
3. ターゲットの機能名を推測する
候補フォルダにわたる共通プレフィックスまたは共有ドメイン名から導出します(例:user-show + user-detail + user-permissions → user、または user-management の方が自然な場合)。
- 明確な共通プレフィックス/ドメインがある → それを使用し、尋ねる必要はありません。
- 明白な共有名がない場合(例:
checkout-flowとinvoice-exportが曖昧な「請求」テーマでのみ関連している)→ 続行する前にユーザーにターゲットの機能名を尋ねます。
推測した名前を出力で明示的に示し、ユーザーが上書きできるようにします。
4. 要件をマージする
ルール(優先順位順):
- ADDED 要件の和集合: すべてのソースフォルダのすべての個別の要件を保持します。
- 競合/重複要件(同じ名前、または同じ動作が2回記述されている場合): 最も完全で最新のバージョンを保持し、競合しない詳細をマージします。黙ってどちらかを削除しないでください。
- REMOVED/非推奨要件: マージ対象から除外します。
- 要件を発明しないでください。 あいまいさや矛盾はインラインでフラグを立ててください(例:
<!-- CONFLICT: ソース A は X、ソース B は Y としている — 人間の判断が必要 -->)。黙って選択しないでください。 - 可能な限り元のシナリオテキストを保持し、2つの重複する要件を1つにマージする場合にのみ散文を書き換えます。
5. ステージングファイルを書き込む
以下に書き込みます:
openspec/specs/.merged-specs/<推測した名前>/spec.md
先頭に以下のフロントマターを付けます:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
その後にマージされた仕様の内容を続けます。
6. ユーザーに簡潔に伝える
- どのソースフォルダがマージされたか、および推測したターゲット名
- インラインでフラグが立てられた、判断が必要な競合があるかどうか
- 次に行うべきこと:
openspec/specs/.merged-specs/<推測した名前>/spec.mdを開き、レビューし、必要に応じて編集し、満足したらフロントマターのApproved-by-human: yesを設定すること openspec/specs/<capability>/配下のファイルはまだ一切変更されていないこと
フェーズ B — 適用
トリガー: ユーザーがマージを適用/確定するよう依頼した場合、または openspec/specs/.merged-specs/<feature>/spec.md の Approved-by-human が yes になっているのを検出した場合。
- ステージングファイルを再読み込みし、
Approved-by-human: yesを確認します。まだnoの場合は、停止してユーザーに先に承認が必要であることを伝えます。適用しないでください。 - フロントマターから
merged-fromを読み取り、置き換えるソースフォルダを把握します。 - ステージングされた内容からフロントマター(両方のフィールド)を削除し、フロントマターのないクリーンな
spec.mdを残します。 - ソースフォルダをマージ結果で置き換えます:
- フロントマターを削除した内容で
openspec/specs/<推測した名前>/spec.mdを作成/上書きします。 merged-fromにリストされている、ターゲット名自体ではない他のソースフォルダを削除します。
- フロントマターを削除した内容で
- ステージングフォルダ
openspec/specs/.merged-specs/<推測した名前>/を削除します。 - 何が置き換えられ、何が削除されたかをユーザーに確認します。これにより、コミット前に git diff で確認しやすくなります。
ファイルに明示的な Approved-by-human: yes が既に存在しない限り、ステップ4を決して実行しないでください。チャットで口頭で承認を求めないでください。承認は、フロントマターフィールドそのものが変更される必要があります。
例
フェーズ A — 入力: openspec/specs/user-show/、openspec/specs/user-detail/、openspec/specs/user-permissions/ — すべて同じユーザープロファイル機能の異なる側面を記述しています。
出力: openspec/specs/.merged-specs/user/spec.md:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
# User
## Requirement: ...
...
ユーザーへのメッセージ: user-show、user-detail、user-permissions から推測された機能 user にマージされました(user-management の方が良ければフォルダ名を変更してください)。競合は見つかりませんでした。openspec/specs/.merged-specs/user/spec.md をレビューし、準備ができたら Approved-by-human: yes を設定してください。
フェーズ B — ユーザーが Approved-by-human: yes を設定し、適用を依頼すると:openspec/specs/user/spec.md がフロントマターなしのマージ内容で作成/上書きされ、user-show/、user-detail/、user-permissions/ が削除され、openspec/specs/.merged-specs/user/ が削除されます。