This is an open question, not a proposal ready to implement. Please don't open a PR from this issue yet. It needs more careful thought and discussion first, and we'd like to hear from the community.
The question
openspec archive merges delta specs into main specs by matching requirement headers. We're starting to think a deterministic merge isn't realistic to support well, and that the merge belongs with an agent instead. We don't yet know what should replace it, or what that would break.
What we're seeing
The merge matches on names. When an agent edits a delta, its header can drift from the base spec and the match breaks. We ran into this before (feat(instructions): add runtime context and operation guidance #1062 : "heading matching was too brittle").
Some failures show up late. A bad MODIFIED header passes validate and only fails at archive, sometimes weeks later (validate: MODIFIED/REMOVED/RENAMED-from headers that don't exist in base spec aren't caught until archive (proposal: opt-in cross-change MODIFIED) #1112 ).
Some failures are silent. Two changes that modified the same requirement lost scenarios (Archiving two changes that MODIFY the same requirement silently drops scenarios (distinct from #1112) #1246 ). A renamed scenario reads as a deleted one (Cannot rename a scenario — MODIFIED reads a rename as a dropped scenario and blocks archive #1697 ).
The fixes keep coming. Since June, the merge code (archive.ts, specs-apply.ts, requirement-blocks.ts) has had 46 fix commits. For example, parser grammar (REMOVED and RENAMED entries written with * or + bullets are silently ignored #1799 , Repeated delta section headers silently discard requirements #1801 , A requirement written outside a delta section is dropped with no diagnostic #1803 , Malformed RENAMED pairs silently skip a rename, or rename the wrong requirement #1805 , show --json --deltas-only reports an invented MODIFIED for a bullet-form REMOVED, while archive deletes the requirement #1855 , A REMOVED heading with a CommonMark closing # run is skipped, with a false "already removed" warning #1859 , ADDED and RENAMED accept a name that differs only in case or spacing, leaving two copies of one requirement #1863 , A delta at specs/<capability>.md is green-lit by status/apply, rejected by validate, then archived without being merged #1869 ) and lost formatting (archive: rebuilt specs end with an extra blank line at EOF #1527 , openspec archive does not preserve blank lines around ## Requirements in the target spec #1625 , archive rewrites the inside of fenced code blocks #1797 , archive: define blank-line normalization policy for fenced code #1711 , archive: applying a delta rewrites a CRLF spec to LF, turning a one-requirement change into a whole-file diff #1935 , Feature: preserve specodelic frontmatter and four-layer tables when openspec archive deploys a delta #2017 ).
The skills already work another way. /opsx:archive and /opsx:bulk-archive have the agent do the merge, then move the folder. So there are two merge paths, and users notice the difference (question: why achive skill or command relies on llm to manually merge specs why it does not use openspec archive cli command? #656 , AI commands and skills for /opsx:archive do not invoke openspec archive CLI, unlike official docs and tutorials #863 ).
Directions we're thinking about
None of these are decided. Each one has trade-offs we haven't worked through.
Agent merges, CLI checks. Drop the deterministic merge. The agent updates the specs, and the CLI keeps checks that must hold. For example: specs parse, no leftover delta sections, tasks are done. Close to Proposal: prepare → agent work → validate → confirm → finalize flow for archive #1460 .
Sync as you go, no archive step. Spec updates become part of the task list. Each task group updates the specs it implemented, and a final task checks that everything was updated. Specs land in the same PR as the code, so there's no separate archive PR. See Exploration: OpenSpec without archive #1968 , and feat(cli): archive a completed change without a separate step #1831 for a related angle.
Keep the deterministic merge, fix what it matches on. For example, stable requirement IDs instead of names. This is the main option that keeps openspec archive as it is.
Open questions
What breaks for people who run openspec archive from CI or scripts?
If the CLI no longer merges, what can it still guarantee? Any "specs are in sync" check still has to match requirements somehow.
How does this work with several changes in progress at once, or specs in a separate repo?
What happens to bulk archive, and to hooks that run around archive (Feat : Add Extensible Hook Capability to OpenSpec Archive Operation #682 , Feature Request: Support on_archive (post-archive hooks) in schema for custom artifact moves/copies #704 , Add optional lifecycle hooks for sync and archive #1910 )?
Should the command be removed, kept as a plain "move to archive" step, or kept as it is?
How you can help
Tell us how you archive today, and whether the CLI merge has worked for you or against you.
Share workflows where removing it would hurt.
Suggest directions we've missed.
Related
Direction: Exploration: OpenSpec without archive #1968 , Proposal: prepare → agent work → validate → confirm → finalize flow for archive #1460 , feat(instructions): add runtime context and operation guidance #1062 , feat(cli): archive a completed change without a separate step #1831 , Proposal: lifecycle: status — record change state as data, not directory position (experimental) #1683 , feat(sync): fold delta specs without archiving the change #1813
Failures: validate: MODIFIED/REMOVED/RENAMED-from headers that don't exist in base spec aren't caught until archive (proposal: opt-in cross-change MODIFIED) #1112 , Archiving two changes that MODIFY the same requirement silently drops scenarios (distinct from #1112) #1246 , Cannot rename a scenario — MODIFIED reads a rename as a dropped scenario and blocks archive #1697 , feat(validator): detect stray delta headers that silently truncate main specs #954 , Feature: preserve specodelic frontmatter and four-layer tables when openspec archive deploys a delta #2017 , and the parser and formatting issues above
Guardrails: archive --yes proceeds on self-reported task status — should archive be able to require evidence? #1652 , archive: --yes skips the incomplete-task stop, and the refusal tells callers to rerun with --yes #2006 , Harden archive workflow against incomplete or invalid spec synchronization #1890
Hooks: Feat : Add Extensible Hook Capability to OpenSpec Archive Operation #682 , Feature Request: Support on_archive (post-archive hooks) in schema for custom artifact moves/copies #704 , Add optional lifecycle hooks for sync and archive #1910 , Proposal: preserve additional artifacts other than specs during sync/archive #1706
Parallel changes: Overlap between open changes is invisible until one archives (parallel-merge plan, Phase 1) #1669 , Pre-archive drift and overlap checking for parallel changes: working tool, offer to contribute #1387 , Feedback: Stores + git worktrees: per-checkout resolution, decoupled specs/changes roots, and archive locking #1714
The question
openspec archivemerges delta specs into main specs by matching requirement headers. We're starting to think a deterministic merge isn't realistic to support well, and that the merge belongs with an agent instead. We don't yet know what should replace it, or what that would break.What we're seeing
validateand only fails at archive, sometimes weeks later (validate: MODIFIED/REMOVED/RENAMED-from headers that don't exist in base spec aren't caught until archive (proposal: opt-in cross-change MODIFIED) #1112).MODIFIEDreads a rename as a dropped scenario and blocks archive #1697).archive.ts,specs-apply.ts,requirement-blocks.ts) has had 46 fix commits. For example, parser grammar (REMOVED and RENAMED entries written with*or+bullets are silently ignored #1799, Repeated delta section headers silently discard requirements #1801, A requirement written outside a delta section is dropped with no diagnostic #1803, Malformed RENAMED pairs silently skip a rename, or rename the wrong requirement #1805,show --json --deltas-onlyreports an invented MODIFIED for a bullet-form REMOVED, while archive deletes the requirement #1855, A REMOVED heading with a CommonMark closing#run is skipped, with a false "already removed" warning #1859, ADDED and RENAMED accept a name that differs only in case or spacing, leaving two copies of one requirement #1863, A delta atspecs/<capability>.mdis green-lit bystatus/apply, rejected byvalidate, then archived without being merged #1869) and lost formatting (archive: rebuilt specs end with an extra blank line at EOF #1527, openspec archive does not preserve blank lines around ## Requirements in the target spec #1625,archiverewrites the inside of fenced code blocks #1797, archive: define blank-line normalization policy for fenced code #1711, archive: applying a delta rewrites a CRLF spec to LF, turning a one-requirement change into a whole-file diff #1935, Feature: preserve specodelic frontmatter and four-layer tables when openspec archive deploys a delta #2017)./opsx:archiveand/opsx:bulk-archivehave the agent do the merge, then move the folder. So there are two merge paths, and users notice the difference (question: why achive skill or command relies on llm to manually merge specs why it does not use openspec archive cli command? #656, AI commands and skills for/opsx:archivedo not invokeopenspec archiveCLI, unlike official docs and tutorials #863).Directions we're thinking about
None of these are decided. Each one has trade-offs we haven't worked through.
openspec archiveas it is.Open questions
openspec archivefrom CI or scripts?How you can help
Related
lifecycle: status— record change state as data, not directory position (experimental) #1683, feat(sync): fold delta specs without archiving the change #1813MODIFIEDreads a rename as a dropped scenario and blocks archive #1697, feat(validator): detect stray delta headers that silently truncate main specs #954, Feature: preserve specodelic frontmatter and four-layer tables when openspec archive deploys a delta #2017, and the parser and formatting issues above--yesskips the incomplete-task stop, and the refusal tells callers to rerun with--yes#2006, Harden archive workflow against incomplete or invalid spec synchronization #1890