Description
When a change's delta file writes the same delta header more than once — for
example two ## ADDED Requirements sections — only one of them is applied. The
requirements under the other are discarded with no error, no warning, and no
INFO note. openspec validate reports the change valid, openspec archive
exits 0 with "Specs updated successfully", and the change is moved into
openspec/changes/archive/, so the active change is gone while the live spec no
longer matches what was authored and reviewed.
The same happens when two copies differ only in casing
(## ADDED Requirements and ## Added Requirements), except the opposite copy
is the one dropped.
Reproduction
openspec init --tools claude
openspec new change add-billing
openspec/changes/add-billing/specs/billing/spec.md:
## ADDED Requirements
### Requirement: Invoice Generation
The system SHALL generate an invoice for every completed billing period.
#### Scenario: Period closes
- **WHEN** a billing period closes
- **THEN** an invoice is generated for the account
## ADDED Requirements
### Requirement: Dunning Notices
The system SHALL send a dunning notice when an invoice is 7 days overdue.
#### Scenario: Invoice overdue
- **WHEN** an invoice is 7 days overdue
- **THEN** a dunning notice is sent to the account owner
openspec validate add-billing --json # "valid": true, "issues": []
openspec archive add-billing --yes # exit 0, "+ 1 added"
Expected
Either both ## ADDED Requirements sections are merged, or the change is
rejected with an error naming the duplicated header. A delta operation the
author wrote should never be discarded without being reported.
Actual
Invoice Generation and its scenario are absent from
openspec/specs/billing/spec.md. Archive printed Totals: + 1, ~ 0, - 0, → 0
for a delta that declared two additions, and reported success.
The loss is not visible from any command:
openspec show add-billing --json --deltas-only — the inspection command
OpenSpec's own "no deltas found" error recommends — reports "deltaCount": 1
and lists only the surviving requirement.
Affects all four operations:
| Delta shape |
archive totals |
Result |
## ADDED Requirements ×2 |
+ 1 added |
first ADDED section dropped |
## ADDED Requirements + ## Added Requirements |
+ 1 added |
last ADDED section dropped |
## MODIFIED Requirements ×2 |
~ 1 modified |
a modification never applied; spec keeps stale text |
## REMOVED Requirements ×2 |
- 1 removed |
a removal never applied; requirement survives |
Impact
Silent loss of specification content in the primary write path, with a success
exit code. Delta files are the artifact OpenSpec asks an AI agent to author, and
emitting a delta header twice is an ordinary model output shape. A delta that
documents OpenSpec's own syntax in a fenced example can also produce the
duplicate without any authoring mistake, because an inner ``` closes the outer
fence and the ## ADDED Requirements line inside the example becomes a real
second section.
Environment
- OpenSpec 1.12.0 (reproduced on the published npm package and on a build of
main @ e062b95)
- Node v22.23.2, Linux
- Also reproduced in a project created entirely by
openspec init +
openspec new change, with the baseline main spec produced by archiving a
first change
Description
When a change's delta file writes the same delta header more than once — for
example two
## ADDED Requirementssections — only one of them is applied. Therequirements under the other are discarded with no error, no warning, and no
INFO note.
openspec validatereports the change valid,openspec archiveexits 0 with "Specs updated successfully", and the change is moved into
openspec/changes/archive/, so the active change is gone while the live spec nolonger matches what was authored and reviewed.
The same happens when two copies differ only in casing
(
## ADDED Requirementsand## Added Requirements), except the opposite copyis the one dropped.
Reproduction
openspec/changes/add-billing/specs/billing/spec.md:Expected
Either both
## ADDED Requirementssections are merged, or the change isrejected with an error naming the duplicated header. A delta operation the
author wrote should never be discarded without being reported.
Actual
Invoice Generationand its scenario are absent fromopenspec/specs/billing/spec.md. Archive printedTotals: + 1, ~ 0, - 0, → 0for a delta that declared two additions, and reported success.
The loss is not visible from any command:
openspec show add-billing --json --deltas-only— the inspection commandOpenSpec's own "no deltas found" error recommends — reports
"deltaCount": 1and lists only the surviving requirement.
Affects all four operations:
## ADDED Requirements×2+ 1 added## ADDED Requirements+## Added Requirements+ 1 added## MODIFIED Requirements×2~ 1 modified## REMOVED Requirements×2- 1 removedImpact
Silent loss of specification content in the primary write path, with a success
exit code. Delta files are the artifact OpenSpec asks an AI agent to author, and
emitting a delta header twice is an ordinary model output shape. A delta that
documents OpenSpec's own syntax in a fenced example can also produce the
duplicate without any authoring mistake, because an inner ``` closes the outer
fence and the
## ADDED Requirementsline inside the example becomes a realsecond section.
Environment
main@e062b95)openspec init+openspec new change, with the baseline main spec produced by archiving afirst change