Skip to content

feat(engine): propose deterministic spec-merge engine (part 2) (#1279) #1288

Closed
clay-good wants to merge 1 commit into
Fission-AI:mainfrom
clay-good:add-spec-tooling-suite
Closed

feat(engine): propose deterministic spec-merge engine (part 2) (#1279) #1288
clay-good wants to merge 1 commit into
Fission-AI:mainfrom
clay-good:add-spec-tooling-suite

Conversation

@clay-good

@clay-good clay-good commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Proposal PR (OpenSpec change, no code). Dogfoods OpenSpec: the deliverable is a change folder under openspec/changes/add-spec-tooling-suite/ (proposal + design + tasks + 5 spec deltas), validated with openspec validate --strict. Follow-up to #1279.

Status

Open, proposal-only (proposal + design + tasks + 5 spec deltas). Validates clean under openspec validate add-spec-tooling-suite --strict.

This is the follow-up @alfred-openspec asked for on #1279 ("narrow the first slice… then split unarchive, format, and diff into follow-ups once the primitive is proven"). #1279 was narrowed to the deterministic merge engine + applied-delta baseline + sync; this PR carries the commands that build on that primitive.

Prerequisite

Depends on #1279 (add-deterministic-spec-sync): the merge engine, the applied-delta baseline (pre-image + digest + provenance), the shared src/core/spec-canonical.ts canonicalizer, and openspec sync --check. This change adds no new merge semantics — it builds commands on top. Merge #1279 first.

What it does

Four commands, all reusing #1279's baseline + canonicalizer:

  • openspec unarchive (+ /opsx:unarchive) — the deterministic inverse of archive: resolve the archived folder (prefix-tolerant, never auto-picking), reverse the spec merge from the baseline pre-image under a drift guard, move the folder back, atomically (--keep-specs escape hatch; pre-baseline archives degrade all-or-nothing). Gives archive the rollback Feat : Add Extensible Hook Capability to OpenSpec Archive Operation #682 noted it lacks.
  • openspec format — a deterministic, behavior-preserving spec formatter (--check gate) sharing one canonicalizer with the merge engine, so synced/archived specs pass format --check by construction. parse-before == parse-after; pure text, no skill.
  • openspec diff — a spec-aware diff that splices the provenance feat(engine): propose deterministic spec-merge engine (part 1) (#1288) #1279 records + the change's proposal.md rationale inline; opt-in git diff driver. No sidecar reasoning store, no inference.
  • openspec check — the unified deterministic linter (format --check + sync --check + validate) behind one verdict, plus an opt-in hook installer and CI step — the same binary for pre-commit and CI, no model, no API keys.

Capabilities (5 spec deltas)

cli-unarchive (new), opsx-unarchive-skill (new), cli-format (new), cli-diff (new), cli-check (new). Design Decisions 6–10 (unarchive) and 14–16 (format/diff/check) live in this change's design.md; the model-shaping rejections (keep archive; keep deltas) stay in #1279's Decisions 5/11/13.

Proof (for a proposal)

Notes / scope

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Added a new spec tooling suite plan and requirements docs covering upcoming CLI workflows for unarchiving changes, formatting specs, generating deterministic diffs, and running a unified check command.
    • Clarified expected behavior for interactive prompts, drift handling, JSON output, and atomic, safe file updates.
    • Added rollout, migration, and verification guidance for consistent use in local development and CI.

…AI#1279)

The follow-up @alfred-openspec asked for: split unarchive, format, diff, and
the unified check gate out of the deterministic-sync spine (Fission-AI#1279) into their
own change, to land once the merge engine + applied-delta baseline is proven.

Adds five capabilities, all built on Fission-AI#1279's baseline (pre-image + digest +
provenance) and shared canonicalizer — no new merge semantics:
- cli-unarchive + opsx-unarchive-skill: deterministic inverse of archive
  (byte-exact reverse from the baseline, drift-guarded, atomic).
- cli-format: behavior-preserving formatter sharing Fission-AI#1279's canonicalizer.
- cli-diff: spec-aware diff splicing provenance + rationale inline.
- cli-check: unified format/sync/validate gate, same binary for pre-commit
  and CI, with an opt-in hook installer + CI template.

Design Decisions 6-10 (unarchive) and 14-16 (format/diff/check); the model-
shaping rejections (keep archive; keep deltas) stay in Fission-AI#1279 Decisions 5/11/13.
Prerequisite: Fission-AI#1279. Validates clean under
`openspec validate add-spec-tooling-suite --strict`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@clay-good clay-good requested a review from TabishB as a code owner July 1, 2026 15:14
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new OpenSpec change proposal ("add-spec-tooling-suite") consisting entirely of documentation: metadata, a design document, a proposal document, four capability spec files (unarchive, format, diff, check), an opsx-unarchive-skill spec, and a tasks plan, all describing tooling built on the existing merge baseline.

Changes

Spec tooling suite proposal

Layer / File(s) Summary
Proposal metadata and overview
.openspec.yaml, design.md (intro/risks/migration), proposal.md, tasks.md (intro)
Adds change metadata and documents purpose, goals, risks, migration notes, and the four target commands built atop the #1279 merge baseline.
Unarchive command and skill spec
design.md (unarchive sections), specs/cli-unarchive/spec.md, specs/opsx-unarchive-skill/spec.md, tasks.md (unarchive tasks)
Defines deterministic reversal of archived changes with resolution, drift guard, --keep-specs, atomicity, backward compatibility, and CLI-delegating skill behavior.
Format command spec
design.md (format section), specs/cli-format/spec.md, tasks.md (format tasks)
Defines deterministic, canonicalizer-shared presentation formatting with --check/--fix and incremental digest support.
Diff command spec
design.md (diff section), specs/cli-diff/spec.md, tasks.md (diff tasks)
Defines deterministic, provenance-annotated requirement diffs with optional git diff-driver integration and --json output.
Check command spec
design.md (check section), specs/cli-check/spec.md, tasks.md (check tasks)
Defines a unified gate combining format/sync/validate checks for pre-commit and CI parity, --fix, incremental checks, hook install, and --json.
Skill wiring, docs, and e2e verification tasks
tasks.md (skill/docs and verification sections)
Adds tasks for skill export, snapshot tests, docs updates, and end-to-end verification across all four commands.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • Fission-AI/OpenSpec#443: Introduces the .openspec.yaml per-change schema metadata contract that this PR's new .openspec.yaml file follows.
  • Fission-AI/OpenSpec#455: Defines the archive skill spec that this PR's unarchive//opsx:unarchive specs are the explicit inverse of.

Suggested reviewers: TabishB, alfred-openspec

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is related to the follow-up on the deterministic merge foundation, but it undersells the main change: a proposal-only spec tooling suite for four new commands.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openspec/changes/add-spec-tooling-suite/specs/cli-check/spec.md`:
- Around line 61-75: The incremental checking requirement is too narrow because
it only keys off per-file digests, which can miss verdict changes when `--all`
enables cross-change checks. Update the check command’s skip/cache logic to
include the active gate set plus the current change-set or revision, so
unchanged file content is only skipped when the full active context matches;
otherwise force a full re-check. Use the existing `Incremental Checking`
requirement and the `openspec check` behavior to locate and adjust the cache
validity rules.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-diff/spec.md`:
- Around line 19-23: Update the “No inference” scenario wording in the CLI diff
spec so it says the renderer computes the change set from repository contents in
code, not from git diff output. In the spec entry for the diff renderer, keep
the deterministic contract explicit and note that git is only used for the
opt-in driver integration, while the renderer itself should rely on recorded
provenance/rationale and repository state.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-format/spec.md`:
- Around line 116-129: The incremental check in the CLI format spec currently
keys skips only on content digests, which can miss formatter/canonicalizer
changes. Update the Requirement: Incremental Checking and its scenarios so the
cache key in `format --check` also includes a formatter/canonicalizer
fingerprint, or explicitly invalidates recorded digests when the logic changes.
Make sure the guidance in `spec.md` ties the skip decision to both the file
digest and the formatter version/fingerprint so unchanged content is only
skipped when the full check result would remain identical.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-unarchive/spec.md`:
- Around line 123-145: The reversal flow in the CLI unarchive spec is not atomic
across interruption because `openspec/specs/` is swapped before the archive
folder move completes. Update the `validate -> stage -> commit` sequence so
`spec swap` and `folder move` are protected by a durable recovery marker or
journal in `.openspec/`, allowing `unarchive` to resume or roll back after a
crash. If you cannot add that persistence, tighten the wording around the
`Abort. No files were changed.` guarantee to only cover in-process failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e2b51ac0-98a1-4331-afb2-59c8f289d6f4

📥 Commits

Reviewing files that changed from the base of the PR and between 546224e and 52a0762.

📒 Files selected for processing (9)
  • openspec/changes/add-spec-tooling-suite/.openspec.yaml
  • openspec/changes/add-spec-tooling-suite/design.md
  • openspec/changes/add-spec-tooling-suite/proposal.md
  • openspec/changes/add-spec-tooling-suite/specs/cli-check/spec.md
  • openspec/changes/add-spec-tooling-suite/specs/cli-diff/spec.md
  • openspec/changes/add-spec-tooling-suite/specs/cli-format/spec.md
  • openspec/changes/add-spec-tooling-suite/specs/cli-unarchive/spec.md
  • openspec/changes/add-spec-tooling-suite/specs/opsx-unarchive-skill/spec.md
  • openspec/changes/add-spec-tooling-suite/tasks.md

Comment on lines +61 to +75
### Requirement: Incremental Checking

The check command MAY use recorded content digests to skip specs and files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the verdict versus a full check.

#### Scenario: Unchanged inputs skipped

- **WHEN** `openspec check` runs and an input's digest matches its recorded digest
- **THEN** the command may skip re-checking it
- **AND** the overall verdict is identical to a full check

#### Scenario: Changed or unknown inputs fully checked

- **WHEN** an input's digest does not match, is missing, or uses an unrecognized scheme
- **THEN** the command performs the full check for it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Make incremental checks aware of the active change set.

A per-file digest is not enough once --all can add cross-change checks: the file contents may stay unchanged while the active change graph changes, so the verdict changes too. Key the cache by the gate set plus the change-set/revision, or invalidate it when cross-change inputs change.

🧩 Suggested wording
- A skip SHALL be permitted only when it cannot change the verdict versus a full check.
+ A skip SHALL be permitted only when it cannot change the verdict versus a full check, and the recorded digest MUST be tied to the exact gate set and active change-set/revision that produced it.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Requirement: Incremental Checking
The check command MAY use recorded content digests to skip specs and files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the verdict versus a full check.
#### Scenario: Unchanged inputs skipped
- **WHEN** `openspec check` runs and an input's digest matches its recorded digest
- **THEN** the command may skip re-checking it
- **AND** the overall verdict is identical to a full check
#### Scenario: Changed or unknown inputs fully checked
- **WHEN** an input's digest does not match, is missing, or uses an unrecognized scheme
- **THEN** the command performs the full check for it
### Requirement: Incremental Checking
The check command MAY use recorded content digests to skip specs and files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the verdict versus a full check, and the recorded digest MUST be tied to the exact gate set and active change-set/revision that produced it.
#### Scenario: Unchanged inputs skipped
- **WHEN** `openspec check` runs and an input's digest matches its recorded digest
- **THEN** the command may skip re-checking it
- **AND** the overall verdict is identical to a full check
#### Scenario: Changed or unknown inputs fully checked
- **WHEN** an input's digest does not match, is missing, or uses an unrecognized scheme
- **THEN** the command performs the full check for it
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-check/spec.md` around lines
61 - 75, The incremental checking requirement is too narrow because it only keys
off per-file digests, which can miss verdict changes when `--all` enables
cross-change checks. Update the check command’s skip/cache logic to include the
active gate set plus the current change-set or revision, so unchanged file
content is only skipped when the full active context matches; otherwise force a
full re-check. Use the existing `Incremental Checking` requirement and the
`openspec check` behavior to locate and adjust the cache validity rules.

Comment on lines +19 to +23
#### Scenario: No inference

- **WHEN** the command renders a diff
- **THEN** it composes the result from the git diff and the recorded provenance/rationale in code
- **AND** it does not call a language model

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Don't derive the renderer from git diff output.

The deterministic contract is stronger than this wording: git diff can vary with user config and external drivers, so the renderer should compute the change set from repository contents in code and reserve git only for the opt-in driver integration.

🛠️ Suggested wording
- THEN it composes the result from the git diff and the recorded provenance/rationale in code
+ THEN it computes the result from repository contents and the recorded provenance/rationale in code
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#### Scenario: No inference
- **WHEN** the command renders a diff
- **THEN** it composes the result from the git diff and the recorded provenance/rationale in code
- **AND** it does not call a language model
#### Scenario: No inference
- **WHEN** the command renders a diff
- **THEN** it computes the result from repository contents and the recorded provenance/rationale in code
- **AND** it does not call a language model
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-diff/spec.md` around lines
19 - 23, Update the “No inference” scenario wording in the CLI diff spec so it
says the renderer computes the change set from repository contents in code, not
from git diff output. In the spec entry for the diff renderer, keep the
deterministic contract explicit and note that git is only used for the opt-in
driver integration, while the renderer itself should rely on recorded
provenance/rationale and repository state.

Comment on lines +116 to +129
### Requirement: Incremental Checking

The format check MAY use recorded content digests to skip files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the result versus a full check.

#### Scenario: Unchanged file skipped

- **WHEN** `--check` runs and a file's current content digest matches the recorded digest
- **THEN** the command may skip re-checking that file
- **AND** the overall result is identical to checking it fully

#### Scenario: Changed or unknown file fully checked

- **WHEN** a file's digest does not match, no digest is recorded, or the recorded digest uses an unrecognized scheme
- **THEN** the command performs the full check for that file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Tie the digest skip to the formatter version.

A content-only digest is not enough here: if the canonicalizer changes, an unchanged file can still need a full pass, and format --check can return a stale pass. Please include a formatter/canonicalizer fingerprint in the cache key or invalidate the cache when that logic changes.

🔧 Suggested wording
- A skip SHALL be permitted only when it cannot change the result versus a full check.
+ A skip SHALL be permitted only when it cannot change the result versus a full check, and the recorded digest MUST be tied to the formatter/canonicalizer version (or invalidated when that version changes).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Requirement: Incremental Checking
The format check MAY use recorded content digests to skip files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the result versus a full check.
#### Scenario: Unchanged file skipped
- **WHEN** `--check` runs and a file's current content digest matches the recorded digest
- **THEN** the command may skip re-checking that file
- **AND** the overall result is identical to checking it fully
#### Scenario: Changed or unknown file fully checked
- **WHEN** a file's digest does not match, no digest is recorded, or the recorded digest uses an unrecognized scheme
- **THEN** the command performs the full check for that file
### Requirement: Incremental Checking
The format check MAY use recorded content digests to skip files whose content is unchanged since they were last checked, re-checking only what changed. A skip SHALL be permitted only when it cannot change the result versus a full check, and the recorded digest MUST be tied to the formatter/canonicalizer version (or invalidated when that version changes).
#### Scenario: Unchanged file skipped
- **WHEN** `--check` runs and a file's current content digest matches the recorded digest
- **THEN** the command may skip re-checking that file
- **AND** the overall result is identical to checking it fully
#### Scenario: Changed or unknown file fully checked
- **WHEN** a file's digest does not match, no digest is recorded, or the recorded digest uses an unrecognized scheme
- **THEN** the command performs the full check for that file
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-format/spec.md` around
lines 116 - 129, The incremental check in the CLI format spec currently keys
skips only on content digests, which can miss formatter/canonicalizer changes.
Update the Requirement: Incremental Checking and its scenarios so the cache key
in `format --check` also includes a formatter/canonicalizer fingerprint, or
explicitly invalidates recorded digests when the logic changes. Make sure the
guidance in `spec.md` ties the skip decision to both the file digest and the
formatter version/fingerprint so unchanged content is only skipped when the full
check result would remain identical.

Comment on lines +123 to +145
The command SHALL apply the reversal atomically using a defined sequence: validate, then stage, then commit, so that any failure before the final commit step leaves the filesystem unchanged.

#### Scenario: Defined sequence

- **WHEN** the command performs a reversal that touches specs
- **THEN** it executes in this order:
1. validate that the destination `openspec/changes/<name>/` does not exist and that every affected spec is present and drift-free against the baseline;
2. compute the reversed spec content and write it to a temporary staging area inside `.openspec/` (no change yet to `openspec/specs/`);
3. swap the staged specs into `openspec/specs/`;
4. move the change folder from archive back to active.

#### Scenario: Failure before the folder move leaves specs untouched

- **WHEN** validation or staging (steps 1–2) fails
- **THEN** the command reports `Abort. No files were changed.`
- **AND** `openspec/specs/` and the archive folder are exactly as they were

#### Scenario: Failure of the final move rolls specs back

- **WHEN** the spec swap (step 3) has occurred but the folder move (step 4) fails
- **THEN** the command restores `openspec/specs/` from the still-present baseline
- **AND** reports `Abort. No files were changed.`
- **AND** if rollback itself cannot complete, it reports the partial state and the exact recovery steps rather than leaving a silent inconsistency

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

This isn’t atomic across interruption.

The sequence updates openspec/specs/ before the folder move completes, so a crash or kill between steps 3 and 4 can still leave the repo split even though the text promises “Abort. No files were changed.” Either narrow the guarantee to in-process failures or add a durable recovery marker/journal so the operation can be resumed or rolled back after interruption.

Suggested wording
- The command SHALL apply the reversal atomically using a defined sequence: validate, then stage, then commit, so that any failure before the final commit step leaves the filesystem unchanged.
+ The command SHALL use a crash-recoverable sequence: validate, then stage, then swap specs, then move the folder. Any failure before the swap leaves the filesystem unchanged; a failure after the swap must be recoverable via a durable journal or recovery marker.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The command SHALL apply the reversal atomically using a defined sequence: validate, then stage, then commit, so that any failure before the final commit step leaves the filesystem unchanged.
#### Scenario: Defined sequence
- **WHEN** the command performs a reversal that touches specs
- **THEN** it executes in this order:
1. validate that the destination `openspec/changes/<name>/` does not exist and that every affected spec is present and drift-free against the baseline;
2. compute the reversed spec content and write it to a temporary staging area inside `.openspec/` (no change yet to `openspec/specs/`);
3. swap the staged specs into `openspec/specs/`;
4. move the change folder from archive back to active.
#### Scenario: Failure before the folder move leaves specs untouched
- **WHEN** validation or staging (steps 1–2) fails
- **THEN** the command reports `Abort. No files were changed.`
- **AND** `openspec/specs/` and the archive folder are exactly as they were
#### Scenario: Failure of the final move rolls specs back
- **WHEN** the spec swap (step 3) has occurred but the folder move (step 4) fails
- **THEN** the command restores `openspec/specs/` from the still-present baseline
- **AND** reports `Abort. No files were changed.`
- **AND** if rollback itself cannot complete, it reports the partial state and the exact recovery steps rather than leaving a silent inconsistency
The command SHALL use a crash-recoverable sequence: validate, then stage, then swap specs, then move the folder. Any failure before the swap leaves the filesystem unchanged; a failure after the swap must be recoverable via a durable journal or recovery marker.
#### Scenario: Defined sequence
- **WHEN** the command performs a reversal that touches specs
- **THEN** it executes in this order:
1. validate that the destination `openspec/changes/<name>/` does not exist and that every affected spec is present and drift-free against the baseline;
2. compute the reversed spec content and write it to a temporary staging area inside `.openspec/` (no change yet to `openspec/specs/`);
3. swap the staged specs into `openspec/specs/`;
4. move the change folder from archive back to active.
#### Scenario: Failure before the folder move leaves specs untouched
- **WHEN** validation or staging (steps 1–2) fails
- **THEN** the command reports `Abort. No files were changed.`
- **AND** `openspec/specs/` and the archive folder are exactly as they were
#### Scenario: Failure of the final move rolls specs back
- **WHEN** the spec swap (step 3) has occurred but the folder move (step 4) fails
- **THEN** the command restores `openspec/specs/` from the still-present baseline
- **AND** reports `Abort. No files were changed.`
- **AND** if rollback itself cannot complete, it reports the partial state and the exact recovery steps rather than leaving a silent inconsistency
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openspec/changes/add-spec-tooling-suite/specs/cli-unarchive/spec.md` around
lines 123 - 145, The reversal flow in the CLI unarchive spec is not atomic
across interruption because `openspec/specs/` is swapped before the archive
folder move completes. Update the `validate -> stage -> commit` sequence so
`spec swap` and `folder move` are protected by a durable recovery marker or
journal in `.openspec/`, allowing `unarchive` to resume or roll back after a
crash. If you cannot add that persistence, tighten the wording around the
`Abort. No files were changed.` guarantee to only cover in-process failures.

@clay-good clay-good marked this pull request as ready for review July 1, 2026 20:55
@clay-good clay-good changed the title [Feature] Propose spec tooling on the merge baseline — unarchive · format · diff · check (follow-up to #1279) feat(engine): propose deterministic spec-merge engine (part 2) (#1279) Jul 2, 2026
@TabishB

TabishB commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

@clay-good Are we still going ahead with this? I'm a little confused by the status of this

@clay-good clay-good closed this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants