Skip to content

fix(archive): stop failing on RENAMED deltas that were already synced#1386

Merged
clay-good merged 1 commit into
Fission-AI:mainfrom
mc856:fix-early-sync-removed-renamed
Jul 18, 2026
Merged

fix(archive): stop failing on RENAMED deltas that were already synced#1386
clay-good merged 1 commit into
Fission-AI:mainfrom
mc856:fix-early-sync-removed-renamed

Conversation

@mc856

@mc856 mc856 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Refs #1376 — this is the follow-up its Notes section anticipated ("REMOVED/RENAMED deltas referencing already-synced state would still error … Can follow up if that pattern shows up in practice").

What was wrong

#1376 made openspec archive idempotent for ADDED requirements after an early sync, but scoped the fix to ADDED. The sync workflow template instructs the agent to fully apply REMOVED and RENAMED deltas to the main spec too — so a change synced through the official workflow still could not archive:

core-layer REMOVED failed for header "### Requirement: ..." - not found
core-layer RENAMED failed for header "### Requirement: ..." - source not found

How it was fixed

In buildUpdatedSpec (src/core/specs-apply.ts):

  • RENAMED: if the source header is gone but the target header exists, the rename was already applied — skip it. A rename whose source and target are both missing still aborts (that's a typo, not an early sync), and source + target both present still aborts with the original "target already exists" error.
  • REMOVED: a requirement that is already absent is skipped instead of aborting, with a visible warning (⚠️ Warning: <spec> - REMOVED requirement "<name>" not found (already removed?); skipping.). This is the answer to the safety concern in fix(archive): stop failing on specs that were already synced before archiving #1376's notes: unlike ADDED (content identity) and RENAMED (target existence), a REMOVED skip has no positive evidence to distinguish "already done" from a typo'd name — so it stays loud rather than silent, and the desired end state (requirement absent) is true either way.
  • Counts now report only operations actually applied, matching the ADDED behavior from fix(archive): stop failing on specs that were already synced before archiving #1376 — an already-synced change shows - 0 / → 0.

Verification

Repro: apply a change's REMOVED/RENAMED deltas to the main spec via the sync workflow, then openspec archive <change> --yes. Before: abort with exit code 1, change stays active. After (built CLI):

Specs to update:
  core-layer: update
⚠️  Warning: core-layer - REMOVED requirement "The system SHALL expose a legacy endpoint" not found (already removed?); skipping.
Applying changes to openspec/specs/core-layer/spec.md:
Totals: + 0, ~ 0, - 0, → 0
Specs updated successfully.
Change 'early-synced-cleanup' archived as '2026-07-19-early-synced-cleanup'.

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

  • two repro tests (fail on main, pass here): early-synced REMOVED and RENAMED changes archive cleanly, with no duplicate or resurrected headers; the REMOVED test also asserts the skip warning is printed
  • one safety test: a rename whose source and target are both missing still aborts with the original error and leaves all files untouched

One intentional test change: the existing multi-spec atomicity test used "REMOVED of a missing requirement" as its failure trigger, which this fix turns into a no-op. The trigger is now a MODIFIED that cannot resolve; the test's intent — a failure in the second spec aborts the whole archive and leaves both specs untouched — is unchanged.

Archive suite: 38/38. Full suite: 1,993 passed / 1 failed — the failure is test/commands/workset.test.ts (launch-environment dependent) and fails identically on main.

Notes

Scoped to the two delta branches #1376 left out; the ADDED and MODIFIED paths are untouched. applySpecs() still doesn't thread silent into buildUpdatedSpec — pre-existing, no internal callers, left alone.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed archiving failures for renamed items that were already synchronized.
    • Prevented duplicate renamed requirements when the target header already exists.
    • Preserved error handling when both the original and renamed headers are missing.
    • Updated rename counts to reflect only changes actually applied.

@mc856
mc856 requested a review from TabishB as a code owner July 18, 2026 18:13
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

RENAMED archive application now treats an existing target requirement as an already-synchronized no-op, counts only applied renames, preserves failures for missing endpoints, and adds regression tests plus patch release metadata.

Changes

Archive rename idempotency

Layer / File(s) Summary
RENAMED application behavior
src/core/specs-apply.ts, .changeset/idempotent-renamed-archive.md
Missing sources are skipped when the target exists, unresolved endpoints still fail, and reported rename counts include only applied renames.
Archive rename regression coverage
test/core/archive.test.ts
Tests cover successful early-synchronized renames and abort behavior when both headers are missing.

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

Suggested reviewers: tabishb, clay-good

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main archive bug fix for already-synced RENAMED deltas.
✨ 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.

@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 RENAMED no-op is safe because the target header is positive evidence, but the REMOVED branch now converts every missing requirement into a warning and no-op. Validation does not compare REMOVED names against the baseline, so a typo or stale name can pass validation and archive while leaving the intended requirement untouched; please keep REMOVED strict for now, or require explicit sync provenance, baseline evidence, or a deliberate override.

The early-sync idempotency fix (Fission-AI#1376) covered only ADDED requirements.
A change whose RENAMED deltas were already applied to the baseline by
the sync workflow still aborted archive with 'RENAMED failed - source
not found'.

RENAMED now skips when the source header is gone but the target header
exists in the spec — the target's presence is positive evidence the
rename was already applied. A rename whose source and target are both
missing still aborts, as does every other genuine conflict. Reported
counts now reflect only renames actually applied.

REMOVED is intentionally left strict: validation does not compare
REMOVED names against the baseline, so treating a missing requirement
as a no-op would let a typo'd or stale name archive silently.
@mc856
mc856 force-pushed the fix-early-sync-removed-renamed branch from dae4522 to c4373bb Compare July 18, 2026 19:21
@mc856 mc856 changed the title fix(archive): stop failing on REMOVED and RENAMED deltas that were already synced fix(archive): stop failing on RENAMED deltas that were already synced Jul 18, 2026
@mc856

mc856 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Fair point — took your first option. You're right that the warning doesn't close the gap: validation never compares REMOVED names against the baseline, so a typo'd or stale name would pass validate and archive with only a console line to show for it.

Narrowed the PR to RENAMED-only in c4373bb: the REMOVED branch is back to the strict error, byte-for-byte what's on main (including the #403 new-spec path), and the changeset, tests, and counts fix now cover only the rename case. The residual is that a sync-workflow change whose REMOVED deltas were applied early will still abort at archive — it fails loudly rather than silently, which seems like the right trade until removal has real provenance.

If you'd want the provenance/baseline-evidence route for REMOVED (e.g. sync recording what it applied, or an explicit override flag), happy to take that as a follow-up — it felt too big to bolt onto this fix.

@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: 1

🤖 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 `@src/core/specs-apply.ts`:
- Line 369: Update the removal-count handling in the spec application flow to
track only successfully applied removals, using a counter such as
removedApplied. Increment it when each REMOVED requirement is actually
processed, leave it unchanged when missing requirements are skipped for new
specs, and assign the resulting counter to removed instead of
plan.removed.length.
🪄 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: ce40f451-d287-4849-8b90-7d42ca23b7f9

📥 Commits

Reviewing files that changed from the base of the PR and between dae4522 and c4373bb.

📒 Files selected for processing (3)
  • .changeset/idempotent-renamed-archive.md
  • src/core/specs-apply.ts
  • test/core/archive.test.ts

Comment thread src/core/specs-apply.ts
modified: plan.modified.length,
removed: plan.removed.length,
renamed: plan.renamed.length,
renamed: renamedApplied,

@coderabbitai coderabbitai Bot Jul 18, 2026

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 | 🟡 Minor | ⚡ Quick win

Track actual removals for accurate operation counts.

Since the PR aims to ensure operation counts reflect only changes that were actually applied, removed should also track applied changes using a counter (e.g., removedApplied), rather than defaulting to plan.removed.length. When isNewSpec is true, missing REMOVED requirements are skipped (warned and ignored), which means plan.removed.length overcounts the operations actually performed.

💡 Proposed fix for accurate removal counts
-      removed: plan.removed.length,
+      removed: removedApplied, // Assuming a `removedApplied` counter is introduced in the REMOVED loop above
🤖 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 `@src/core/specs-apply.ts` at line 369, Update the removal-count handling in
the spec application flow to track only successfully applied removals, using a
counter such as removedApplied. Increment it when each REMOVED requirement is
actually processed, leave it unchanged when missing requirements are skipped for
new specs, and assign the resulting counter to removed instead of
plan.removed.length.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

True in the new-spec corner — the #403 path skips a missing REMOVED but still counts it. That's pre-existing behavior on main, though, and per the review above this PR deliberately restores the REMOVED branch byte-for-byte to what's on main (see the scope note). Re-touching that path, even just its count, would undo the narrowing. The counts fix here covers the rename case this PR actually changes; a removedApplied counter can ride along with any future REMOVED follow-up.

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.

@mc856, agreed. The RENAMED counter change accurately covers the behavior introduced in this PR, while the new-spec REMOVED overcount is pre-existing and intentionally deferred to preserve the narrowed scope.

Would you like me to open a follow-up GitHub issue for the removedApplied accounting fix?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No need for a separate issue, I think — it's captured in this thread, and it's small enough to ride along with whatever REMOVED provenance follow-up the maintainers decide on. Their call if they'd rather track it on its own.

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.

@mc856, understood—no follow-up issue. The existing thread provides sufficient context for any future REMOVED provenance work.

@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 at c4373bb: the unsafe REMOVED behavior is gone, REMOVED remains strict, and RENAMED only skips when the target header provides positive evidence that the rename already landed. The focused archive suite passes locally and the full cross-platform CI suite is green.

@clay-good
clay-good added this pull request to the merge queue Jul 18, 2026
Merged via the queue into Fission-AI:main with commit b419e96 Jul 18, 2026
11 checks passed
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.

3 participants