Related to #1112, but a distinct (opposite) failure mode. #1112 covers MODIFIED against a header that does not exist in the base spec → openspec archive aborts loudly (not found). This issue is the inverse: the header does exist and is MODIFIED by two changes, so archive succeeds but silently overwrites the earlier change's scenarios. #1112's validate-time fix does not address this case.
Summary
When two active changes both list the same ### Requirement: under ## MODIFIED Requirements and are archived sequentially, the second archive overwrites the first — the earlier change's scenarios are silently dropped from the canonical spec. No warning, no diff, no conflict marker; archive reports success.
Root cause
buildUpdatedSpec in src/core/specs-apply.ts (around L296) applies MODIFIED as a whole-block replace keyed by requirement name (nameToBlock.set(key, mod)). The delta carries the change author's view of the requirement at authoring time; when applied, it replaces the entire requirement block, discarding any sibling scenarios another change added in between. validateChangeDeltaSpecs checks only intra-change consistency — there is no base-fingerprint / cross-change drift detection, and archive.ts has no conflict gate.
Reproduction
- Change A:
MODIFIED requirement R, adds scenario S_A.
- Change B (branched from the pre-A spec):
MODIFIED the same R, adds scenario S_B.
- Archive A → canonical spec has
S_A (plus existing scenarios).
- Archive B →
buildUpdatedSpec whole-block replaces R with B's version → S_A is gone, silently.
Change B's delta never knew S_A existed, so replacing the whole block drops it.
Fix options (RFC drafted)
Happy to open a PR in whichever direction you prefer. Full RFC available on request.
Related to #1112, but a distinct (opposite) failure mode. #1112 covers
MODIFIEDagainst a header that does not exist in the base spec →openspec archiveaborts loudly (not found). This issue is the inverse: the header does exist and isMODIFIEDby two changes, so archive succeeds but silently overwrites the earlier change's scenarios. #1112's validate-time fix does not address this case.Summary
When two active changes both list the same
### Requirement:under## MODIFIED Requirementsand are archived sequentially, the second archive overwrites the first — the earlier change's scenarios are silently dropped from the canonical spec. No warning, no diff, no conflict marker; archive reports success.Root cause
buildUpdatedSpecinsrc/core/specs-apply.ts(around L296) appliesMODIFIEDas a whole-block replace keyed by requirement name (nameToBlock.set(key, mod)). The delta carries the change author's view of the requirement at authoring time; when applied, it replaces the entire requirement block, discarding any sibling scenarios another change added in between.validateChangeDeltaSpecschecks only intra-change consistency — there is no base-fingerprint / cross-change drift detection, andarchive.tshas no conflict gate.Reproduction
MODIFIEDrequirement R, adds scenarioS_A.MODIFIEDthe same R, adds scenarioS_B.S_A(plus existing scenarios).buildUpdatedSpecwhole-block replaces R with B's version →S_Ais gone, silently.Change B's delta never knew
S_Aexisted, so replacing the whole block drops it.Fix options (RFC drafted)
buildUpdatedSpecto merge scenario lists rather than whole-block replace, so non-overlapping scenario additions commute. Root fix, larger scope + migration.Happy to open a PR in whichever direction you prefer. Full RFC available on request.