Skip to content

fix(archive): stop failing on specs that were already synced before archiving#1376

Merged
clay-good merged 3 commits into
mainfrom
fix/archive-added-idempotent-after-sync
Jul 17, 2026
Merged

fix(archive): stop failing on specs that were already synced before archiving#1376
clay-good merged 3 commits into
mainfrom
fix/archive-added-idempotent-after-sync

Conversation

@clay-good

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

Copy link
Copy Markdown
Collaborator

Fixes #1332

What was wrong

Syncing a change's specs to the main specs early (to unblock dependent changes) and archiving later is a pattern OpenSpec's own sync workflow supports — but openspec archive failed on it:

core-layer ADDED failed for header "### Requirement: ..." - already exists

buildUpdatedSpec threw whenever an ADDED requirement already existed in the target spec, even when the existing content was identical — i.e., when re-applying it would change nothing. The only way through was discovering the --skip-specs workaround.

How it was fixed

In the ADDED pass of buildUpdatedSpec (src/core/specs-apply.ts), a requirement that already exists in the target with identical content (line-ending-normalized, trimmed) is now skipped as a no-op. A same-named requirement with different content still aborts the archive — the genuine-conflict safety check is unchanged. Reported counts now reflect what was actually applied, so an already-synced change shows + 0 added instead of claiming to add requirements it didn't touch.

Replication / proof

Repro from the issue: sync a change's ADDED requirement into openspec/specs/, then openspec archive <change> --yes.

  • Before: archive aborts with ADDED failed ... - already exists, exit code 1, change stays active.
  • After (built CLI): archive succeeds, the requirement appears exactly once in the main spec, and the change lands in archive/:
Specs to update:
  core-layer: update
Applying changes to openspec/specs/core-layer/spec.md:
Totals: + 0, ~ 0, - 0, → 0
Specs updated successfully.
Change 'core-abstraction' archived as '2026-07-16-core-abstraction'.

Tests in test/core/archive.test.ts:

  • new repro test (fails on main, passes here): already-synced ADDED archives cleanly with no duplicate requirement
  • new safety test: same header with different content still aborts with the original error and leaves all files untouched

Full suite: 1,860 passed; the only failures are the 17 known environment-only zsh-installer ones (#1321, unrelated, green in CI).

Notes

Scoped to ADDED only, matching the issue. After an early sync, MODIFIED is naturally idempotent (replacing a block with itself), but REMOVED/RENAMED deltas referencing already-synced state would still error — those lack a content-identity check to distinguish "already done" from a typo, so loosening them trades away real safety. Can follow up if that pattern shows up in practice.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed openspec archive failures for ADDED requirements that were already synced to main before archiving.
    • Identical ADDED blocks are treated as a no-op to avoid duplicate requirements.
    • Archives still stop on conflicts when a same-named ADDED requirement exists with different content.
    • Archiving outcomes now more accurately reflect when changes were actually added.
  • Tests
    • Added regression coverage for the success and conflict scenarios involving ADDED requirements during archiving.

Archiving a change whose specs were synced to the baseline first (the
early-sync pattern from the sync workflow) failed with 'ADDED failed -
already exists'. An ADDED requirement that already exists in the target
spec with identical content is now skipped; differing content still
aborts as a genuine conflict. Fixes #1332.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 288badf7-0532-449c-ada6-2bb4d13b7077

📥 Commits

Reviewing files that changed from the base of the PR and between c352ec4 and fbee80f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • src/core/specs-apply.ts
  • test/core/archive.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/core/specs-apply.ts

📝 Walkthrough

Walkthrough

openspec archive now treats identical pre-existing ADDED requirements as no-ops, while conflicting content still aborts archiving. Applied counts reflect only inserted requirements, with regression tests and a patch changeset documenting the behavior.

Changes

Archive idempotency

Layer / File(s) Summary
Idempotent ADDED application
src/core/specs-apply.ts
Normalizes existing and incoming requirement blocks, skips identical ADDED requirements, preserves errors for differing content, and counts only newly inserted requirements.
Archive regression coverage
test/core/archive.test.ts, .changeset/idempotent-added-archive.md
Tests successful early-synced archiving and conflicting-content failure behavior, with a patch changeset documenting the fix.

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

Possibly related issues

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.
Title check ✅ Passed The title clearly matches the main change: archive no longer fails when specs were already synced before archiving.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/archive-added-idempotent-after-sync

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed the archive/spec application path, linked issue, CI, and focused local tests. The ADDED no-op is scoped correctly and preserves conflict safety. Looks good to merge.

Keep all three archive tests: the two idempotent-ADDED tests from this
branch and the nested delta-spec test from main (#1355).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The archive fix itself remains correct, but the merge rewrote pnpm-lock.yaml from v9 to v6 with 3,870 unrelated changed lines and dependency-version churn despite no manifest change. This is causing Nix validation to fail on a missing offline tarball; please restore the lockfile from main and rerun CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@clay-good

Copy link
Copy Markdown
Collaborator Author

Restored pnpm-lock.yaml from main in 07b490b — the merge commit had accidentally staged a local v6 lockfile rewrite. Diff vs main is now only the changeset, src/core/specs-apply.ts, and the archive tests. @alfred-openspec please re-review.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Verified the remediation restores pnpm-lock.yaml byte-for-byte with main; the final diff is back to the intended archive fix, changeset, and focused tests. The identical-ADDED no-op remains correctly scoped, conflict safety is preserved, and CI is green.

@clay-good
clay-good added this pull request to the merge queue Jul 17, 2026
Merged via the queue into main with commit 7958924 Jul 17, 2026
14 checks passed
@clay-good
clay-good deleted the fix/archive-added-idempotent-after-sync branch July 17, 2026 20:09
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.

archive: ADDED fails when specs already synced to baseline (early-sync pattern)

2 participants