OpenSpec Merge
Merges several related capability folders in openspec/specs/ into one main
feature spec, through a two-phase staged/approved workflow. Nothing under
openspec/specs/<capability>/ is ever touched until a human has explicitly
approved the merge.
When this applies
OpenSpec's archive workflow syncs deltas into openspec/specs/<capability>/spec.md
per change. When related changes are archived under different capability names
(often because they were built in stages, e.g. user-show, user-detail,
user-permissions), you end up with several spec folders describing what is
functionally one feature. This skill merges those into one, via a staging
area that requires human sign-off before anything real gets replaced.
The two phases
- Phase A — Stage: read source specs, write a merged draft to
openspec/specs/.merged-specs/<feature>/spec.mdwithApproved-by-human: no. Nothing else changes. - Phase B — Apply: triggered once the user has flipped the frontmatter to
Approved-by-human: yes. Replaces the source folders with the approved content and removes the staging file.
Always detect which phase applies: if openspec/specs/.merged-specs/<feature>/
already exists, check its Approved-by-human field first instead of
re-staging from scratch.
Phase A — Stage
1. Identify candidate folders
List openspec/specs/* and look for folders that:
- Share a common name prefix or domain word (
user-show,user-detail,user-permissions) - Reference the same entity/resource in their requirements (same model, same API endpoints, same UI page)
- Were archived close together in time (check
openspec/changes/archive/dates)
If the user named specific folders explicitly, use those instead of guessing.
If more than one plausible grouping exists, stop and ask the user which folders belong together before staging anything.
2. Read every candidate spec.md in full
For each folder, read the entire spec.md, not just headings. Note requirement
names, scenarios, deprecation markers, and anything that looks like it
modifies/supersedes a requirement in another candidate folder.
3. Infer the target capability name
Derive it from the common prefix or shared domain noun across the candidate
folders (e.g. user-show + user-detail + user-permissions → user, or
user-management if that reads more naturally).
- Clear common prefix/domain → use it, no need to ask.
- No obvious shared name (e.g.
checkout-flowandinvoice-exportrelated only by a vague "billing" theme) → ask the user to name the target capability before proceeding.
State the inferred name explicitly in your output so the user can override it.
4. Merge the requirements
Rules, in order:
- Union of ADDED requirements: every distinct requirement from every source folder is kept.
- Conflicting/duplicate requirements (same name, or same behavior described twice): keep the most complete/recent version, merging non-conflicting details rather than dropping either silently.
- REMOVED/deprecated requirements: exclude from the merged spec.
- Never invent requirements. Flag ambiguity or contradictions inline
(e.g.
<!-- CONFLICT: source A says X, source B says Y — needs human decision -->) instead of picking silently. - Preserve original scenario text where possible; only rewrite prose when merging two overlapping requirements into one.
5. Write the staged file
Write to:
openspec/specs/.merged-specs/<inferred-name>/spec.md
With this frontmatter at the top:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
Followed by the merged spec content.
6. Tell the user, briefly
- Which source folders were merged, and the inferred target name
- Any conflicts flagged inline that need a decision
- Exactly what to do next: open
openspec/specs/.merged-specs/<inferred-name>/spec.md, review it, edit if needed, and setApproved-by-human: yesin the frontmatter once satisfied - That nothing under
openspec/specs/<capability>/has been touched yet
Phase B — Apply
Trigger: the user asks to apply/finalize a merge, or you notice
openspec/specs/.merged-specs/<feature>/spec.md has Approved-by-human: yes.
- Re-read the staged file and confirm
Approved-by-human: yes. If it's stillno, stop and tell the user it needs approval first — do not apply. - Read
merged-fromfrom the frontmatter to know which source folders to replace. - Strip the frontmatter from the staged content (both fields), leaving a
clean
spec.mdwith no frontmatter. - Replace the source folders with the merged result:
- Create/overwrite
openspec/specs/<inferred-name>/spec.mdwith the frontmatter-stripped content. - Remove the other source folders listed in
merged-fromthat are not the target name itself.
- Create/overwrite
- Remove the staging folder
openspec/specs/.merged-specs/<inferred-name>/. - Confirm to the user what was replaced/removed, so it's easy to check against git diff before committing.
Never perform step 4 without an explicit Approved-by-human: yes already
present in the file — do not ask the user to approve verbally in chat as a
substitute; the approval must be the flipped frontmatter field itself.
Example
Phase A — Input: openspec/specs/user-show/, openspec/specs/user-detail/,
openspec/specs/user-permissions/ — all describing different aspects of the
same user profile feature.
Output: openspec/specs/.merged-specs/user/spec.md:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
# User
## Requirement: ...
...
Message to the user: merged from user-show, user-detail, user-permissions
into inferred capability user (rename the folder if you'd prefer
user-management); no conflicts found; review
openspec/specs/.merged-specs/user/spec.md and set
Approved-by-human: yes when ready.
Phase B — Once the user sets Approved-by-human: yes and asks to apply:
openspec/specs/user/spec.md is created/overwritten with the frontmatter-free
merged content, user-show/, user-detail/, user-permissions/ are removed,
and openspec/specs/.merged-specs/user/ is deleted.