Skip to content

Fix archive exit code on validation failure#1311

Open
danilopopeye wants to merge 2 commits into
Fission-AI:mainfrom
danilopopeye:fix/archive-exit-code-on-validation-failure
Open

Fix archive exit code on validation failure#1311
danilopopeye wants to merge 2 commits into
Fission-AI:mainfrom
danilopopeye:fix/archive-exit-code-on-validation-failure

Conversation

@danilopopeye

@danilopopeye danilopopeye commented Jul 6, 2026

Copy link
Copy Markdown

Summary

In human (non---json) mode, openspec archive <change> -y returned exit code 0 when validation failed and nothing was archived. The command printed "Validation failed" (or "Aborted. No files were changed.") and exited cleanly, so scripts and CI could not distinguish a blocked archive from a successful one.

The --json path was already correct — it throws ArchiveBlockedError, caught by printJsonFailure, which sets process.exitCode = 1. This was an asymmetry between the two modes for the same failure class.

Reproduced in a clean sandbox against 1.5.0: openspec archive bad-change -y with a delta spec missing the SHALL/MUST keyword → printed "Validation failed", exited 0, changes/archive/ was never created.

Changes

Set process.exitCode = 1 at the three human-mode abort points in ArchiveCommand.run() before return null:

  • delta-spec validation failure
  • spec rebuild failure (buildUpdatedSpec threw)
  • rebuilt-spec validation failure

This mirrors what printJsonFailure already does in the same file for the JSON path. Legitimate user cancellations (no change selected, declining a confirmation prompt) remain exit 0 by design.

Related

Testing

  • New regression tests in test/core/archive.test.ts covering both blocking paths reachable in human mode (delta validation failure, spec rebuild failure) plus a success path asserting exit code stays unset.
  • Test setup now saves/restores process.exitCode to prevent leakage between tests.
  • pnpm exec vitest run — 99 files / 1811 tests passing.
  • pnpm run lint — 0 errors.
  • Manual sandbox repro now exits 1 and does not archive.

Notes

The third abort path (rebuilt-spec validation failure) got the same one-line fix but is defensively hard to trigger in isolation — the delta-spec validator catches most malformed deltas first. I kept the dedicated test for the two reachable paths rather than contriving validator internals to hit the third.

Summary by CodeRabbit

  • Bug Fixes
    • Archive commands now return a non-zero exit code when an archive is blocked by validation or rebuild failures in normal output mode.
    • Successful archives continue to exit normally.
    • Blocked archive attempts no longer leave changes partially archived.
    • Added regression coverage to ensure the correct exit-code behavior in human (non-JSON) mode.

In human (non-JSON) mode, openspec archive returned exit code 0 when
validation failed and nothing was archived. The three blocking paths
in ArchiveCommand.run() printed an error message but returned null
silently, leaving process.exitCode at 0. Scripts and CI could not
distinguish a blocked archive from a successful one.

The --json path was already correct (it throws ArchiveBlockedError,
caught by printJsonFailure which sets exitCode = 1). This was an
asymmetry between the two modes for the same failure.

Set process.exitCode = 1 at the three human-mode abort points before
returning null:
  - delta-spec validation failure
  - spec rebuild failure
  - rebuilt-spec validation failure

Legitimate user cancellations (selecting no change, declining a
confirmation prompt) remain exit 0 by design.

Aligns archive with the same exit-code guarantee already approved for
apply instructions in Fission-AI#1250. References Fission-AI#498.
@danilopopeye danilopopeye requested a review from TabishB as a code owner July 6, 2026 00:27
@coderabbitai

coderabbitai Bot commented Jul 6, 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: 46b0bccc-8895-40b5-91f3-efc6503c4729

📥 Commits

Reviewing files that changed from the base of the PR and between c8bea24 and 1053dbd.

📒 Files selected for processing (1)
  • test/core/archive.test.ts

📝 Walkthrough

Walkthrough

This change updates ArchiveCommand.run() to set process.exitCode = 1 on three abort paths in human mode, adds regression tests that isolate and verify exit-code behavior, and documents the fix in a changeset.

Changes

Archive exit code fix

Layer / File(s) Summary
Set non-zero exit code on abort paths
src/core/archive.ts
ArchiveCommand.run() now sets process.exitCode = 1 before returning null on delta validation failure, spec rebuild failure, and rebuilt-spec validation failure.
Regression tests and exit code isolation
test/core/archive.test.ts
Tests capture and restore process.exitCode around each test case, and a new describe block verifies exit code 1 for blocked archives and unset exit code on success.
Changeset documentation
.changeset/fix-archive-exit-code.md
Documents the patch release fixing the exit code for human-mode archive failures.

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

Suggested reviewers: alfred-openspec, TabishB

🚥 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 accurately summarizes the main change: fixing archive exit codes when validation fails.
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 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.

🧹 Nitpick comments (1)
test/core/archive.test.ts (1)

837-921: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Missing regression test for the third abort path.

This new describe block covers the delta-validation-failure path (archive.ts Line 309) and the spec-rebuild-failure path (archive.ts Line 432), but not the rebuilt-spec validation-failure path (archive.ts Line 456, reached when buildUpdatedSpec succeeds but Validator.validateSpecContent on the rebuilt content reports invalid). Since this is one of the three paths the PR explicitly fixes, add a third test to prevent silent regression on that branch.

🤖 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 `@test/core/archive.test.ts` around lines 837 - 921, Add a third regression
test in the existing archive exit-code describe block to cover the rebuilt-spec
validation-failure path in archiveCommand.execute. Create a change that reaches
buildUpdatedSpec successfully, then fails Validator.validateSpecContent on the
rebuilt content, and assert process.exitCode becomes 1, the failure is logged,
and no archive is created. Use the existing archiveCommand,
Validator.validateSpecContent, and buildUpdatedSpec flow as the locating
symbols.
🤖 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.

Nitpick comments:
In `@test/core/archive.test.ts`:
- Around line 837-921: Add a third regression test in the existing archive
exit-code describe block to cover the rebuilt-spec validation-failure path in
archiveCommand.execute. Create a change that reaches buildUpdatedSpec
successfully, then fails Validator.validateSpecContent on the rebuilt content,
and assert process.exitCode becomes 1, the failure is logged, and no archive is
created. Use the existing archiveCommand, Validator.validateSpecContent, and
buildUpdatedSpec flow as the locating symbols.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 03ab610b-2755-4cba-9169-bfcbf32eba27

📥 Commits

Reviewing files that changed from the base of the PR and between 65a7233 and c8bea24.

📒 Files selected for processing (3)
  • .changeset/fix-archive-exit-code.md
  • src/core/archive.ts
  • test/core/archive.test.ts

Cover the third archive blocking path (spot 3): buildUpdatedSpec
succeeds but Validator.validateSpecContent rejects the rebuilt
content. Spy on validateSpecContent (same pattern as the existing
--no-validate test) to force the rebuilt spec invalid while the rest
of the flow runs for real, since this branch is otherwise defensive
and nearly unreachable — spot 1 already enforces the same
SHALL/MUST/scenario rules on the delta.

Asserts process.exitCode === 1, the failure is logged, the main spec
is left unchanged, and no archive is created.
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.

1 participant