mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-02 05:24:34 +08:00
Merged and archived four concurrent changes that modified the same specs: - add-cline-support: Added Cline AI tool configuration - add-crush-support: Added Crush AI tool configuration - add-factory-slash-commands: Added Factory Droid slash commands - add-archive-command-arguments: Added archive command argument support Changes to cli-init/spec.md: - Added Cline and CodeBuddy Code configuration scenarios - Added slash command scenarios for CodeBuddy Code, Cline, Crush, Factory Droid Changes to cli-update/spec.md: - Added update scenarios for CodeBuddy Code, Cline, Crush, Factory Droid - Added Archive Command Argument Support requirement - Modified OpenCode scenario to support $ARGUMENTS placeholder This required manual intervention to prevent data loss from OpenSpec's requirement-level replacement during archiving. The archive operations initially overwrote scenarios from earlier changes, requiring restoration of all missing content to preserve complete spec state. All four changes successfully archived to openspec/changes/archive/.
8.3 KiB
8.3 KiB
OpenSpec Parallel Delta Remediation Plan
Problem Summary
- Active changes apply requirement-level replacements when archiving. When two changes touch the same requirement, the second archive overwrites the first and silently drops scenarios (e.g., Windsurf vs. Kilo Code slash command updates).
- The archive workflow (
src/core/archive.ts:191andsrc/core/archive.ts:501) rebuilds main specs by replacing entire requirement blocks with the content contained in the change delta. The delta format (src/core/parsers/requirement-blocks.ts:113) has no notion of base versions or scenario-level operations. - The tooling cannot detect divergence between the change author’s starting point and the live spec, so parallel development corrupts the source of truth without warning.
Observed Failure Mode
- Change A (
add-windsurf-workflows) adds a Windsurf scenario underSlash Command Configuration. - Change B (
add-kilocode-workflows) adds a Kilo Code scenario to the same requirement, starting from the pre-Windsurf spec. - After Change A archives, the main spec contains both scenarios.
- When Change B archives,
buildUpdatedSpecsees aMODIFIEDblock forSlash Command Configurationand replaces the requirement with the four-scenario variant shipped in that change. Because that file never learned about Windsurf, the Windsurf scenario disappears. - There is no warning, diff, or conflict indicator—the archive completes successfully, and the source-of-truth spec now omits a shipped scenario.
Root Causes
- Replace-only semantics.
buildUpdatedSpecperforms hash-map substitution of requirement blocks and cannot merge or compare individual scenarios (src/core/archive.ts:455-src/core/archive.ts:526). - Missing base fingerprint. Changes do not persist the requirement content they were authored against, so the archive step cannot tell if the live spec diverged.
- Single-level granularity. The delta language only understands requirements. Even if we introduced scenario-level parsing, we would still lose sibling edits without an accompanying merge strategy.
- Lack of conflict UX. The CLI never forces contributors to reconcile parallel updates. There is no equivalent of
git merge,git rebase, or conflict markers.
Design Objectives
- Preserve every approved scenario regardless of archive order.
- Detect and block speculative archives when the live spec diverges from the author’s base.
- Provide a deterministic, reviewable conflict resolution flow that mirrors source-control best practices.
- Keep the authoring experience ergonomic: deltas should remain human-editable markdown.
- Support incremental adoption so existing repositories can roll forward without breaking active work.
Proposed Fix: Layered Remediation
Phase 0 – Stop the Bleeding (Detection & Guardrails)
- Persist requirement fingerprints alongside each change.
- When scaffolding or validating a change, capture the current requirement body for every
MODIFIED/REMOVED/RENAMEDentry and write it tochanges/<id>/meta.json. - Store a stable hash (e.g., SHA-256) of the base requirement content and the raw text itself for later merges.
- When scaffolding or validating a change, capture the current requirement body for every
- Validate fingerprints during archive.
- Before
buildUpdatedSpecmutates specs, recompute the requirement hash from the live spec. - If the hash differs from the stored base, abort and instruct the user to rebase. This makes the destructive path impossible.
- Before
- Surface intent in CLI output.
- Show which requirements are stale, when they diverged, and which change last touched them.
- Document interim manual mitigation.
- Update
openspec/AGENTS.mdand docs so contributors know to rerunopenspec change sync(see Phase 1) whenever another change lands.
- Update
Outcome: We prevent data loss immediately while we work on a richer merge story.
Phase 1 – Add a Rebase Workflow (Author-Side Merge)
- Introduce
openspec change sync <id>(orrebase).- Reads the stored base snapshot, the current spec, and the author’s delta.
- Performs a 3-way merge per requirement. A naive diff3 on markdown lines is acceptable initially because we already operate on requirement-sized chunks.
- If the merge is clean, rewrite the
MODIFIEDblock with the merged text and refresh the stored fingerprint. - On conflict, write conflict markers inside the change delta (similar to Git) and require the author to hand-edit before re-running validation.
- Enrich validator messages.
openspec validateshould flag unresolved conflict markers or fingerprint mismatches so errors appear early in the workflow.
- Optional: Offer a
--rewrite-scenarioshelper that merges bullet lists of scenarios to reduce manual editing noise.
Outcome: Contributors can safely reconcile their work with the latest spec before archiving, restoring true parallel development.
Phase 2 – Increase Delta Granularity
- Extend the delta language with scenario-level directives.
- Allow
## MODIFIED Requirements+## ADDED Scenarios/## MODIFIED Scenariossections nested under the requirement header. - Backed by stable scenario identifiers (explicit IDs or generated hashes) stored in
meta.json. This lets the system reason about individual scenarios.
- Allow
- Teach the parser to understand nested operations.
- Update
parseDeltaSpecto emit scenario-level operations in addition to requirement blocks. - Update
buildUpdatedSpec(or its replacement) to merge scenario lists, preserving order while inserting new entries in a deterministic fashion.
- Update
- Automate migration.
- Provide a one-time command that inspects each existing spec, injects scenario IDs, and rewrites in-flight change deltas into the richer format.
- Continue to rely on the Phase 1 rebase flow for conflicts when two changes edit the same scenario body or description.
Outcome: Most concurrent updates become commutative, drastically reducing the odds of human merges.
Phase 3 – Structured Spec Graph (Long-Term)
- Define stable requirement IDs.
- Embed
Requirement ID: <uuid>markers in specs so renames and moves are trackable. - This enables future features like cross-capability references and better diff visualizations.
- Embed
- Model spec edits as operations over an AST.
- Build an intermediate representation (IR) for requirements/scenarios/metadata.
- Use operational transforms or CRDT-like techniques to guarantee merge associativity.
- Integrate with Git directly.
- Offer optional
openspec branchscaffolding that aligns spec changes with Git branches, letting teams leverage Git’s conflict editor for the markdown IR.
- Offer optional
Outcome: OpenSpec graduates from replace-based updates to a resilient, intent-preserving spec management platform.
Migration & Product Impacts
- Backfill metadata: add hashes for all active changes and the current main specs during the initial rollout.
- CLI UX: new commands (
change sync, enhancedarchive) require documentation, help text, and release notes. - Docs & AGENTS updates: reinforce the rebase workflow and explain conflict resolution to AI assistants.
- Testing: introduce fixtures covering divergent requirement fingerprints and merge resolution logic.
- Telemetry (optional): log fingerprint mismatches so we can see how often teams hit conflicts after the rollout.
Open Questions / Risks
- How should we order scenarios when multiple changes insert at different points? (Consider optional
positionmetadata or deterministic alphabetical fallbacks.) - What is the graceful failure mode if contributors delete the
meta.jsonfile? (CLI should recreate fingerprints on demand.) - Do we need to support offline authors who cannot easily re-run the sync command before archiving? (Potential
--accept-outdatedescape hatch for emergencies.) - How will archived historical changes be handled? We may need a migration script to embed fingerprints retroactively so re-validation succeeds.
Immediate Next Steps
- Prototype fingerprint capture during
openspec change validateand block archive on mismatches. - Ship
openspec change syncwith line-based diff3 merging and conflict markers. - Update contributor docs and AI instructions to mandate running
syncbefore archiving. - Plan the scenario-level delta extension and migration path as a follow-up RFC.