Skip to content

validate: omitted scenarios in a MODIFIED requirement are only rejected by archive #1477

Description

@pacaplan

Summary

A change can pass both normal and strict validation while being impossible to archive because a MODIFIED requirement omits (or renames) an existing scenario.

openspec archive correctly rejects the delta to prevent silent scenario loss, but openspec validate does not run the same applicability check. This lets an unarchivable change pass validation before implementation begins.

Reproduced with OpenSpec 1.7.0 and 1.6.0.

Minimal reproduction

Create one canonical spec:

<!-- openspec/specs/widgets/spec.md -->
# widgets Specification

## Purpose
Define widget behavior.

## Requirements

### Requirement: Widget state
The system SHALL report the widget state.

#### Scenario: Existing scenario
- **WHEN** the widget is queried
- **THEN** the system reports its state

Create one change that keeps the requirement header but renames the scenario:

<!-- openspec/changes/rename-scenario/specs/widgets/spec.md -->
## MODIFIED Requirements

### Requirement: Widget state
The system SHALL report the widget state.

#### Scenario: Renamed scenario
- **WHEN** the widget is queried
- **THEN** the system reports its state

Run:

$ npx --yes @fission-ai/openspec@1.7.0 validate --type change rename-scenario
Change 'rename-scenario' is valid
$ echo $?
0

$ npx --yes @fission-ai/openspec@1.7.0 validate --strict --type change rename-scenario
Change 'rename-scenario' is valid
$ echo $?
0

$ npx --yes @fission-ai/openspec@1.7.0 archive rename-scenario --yes --json
{
  "archive": null,
  "status": [
    {
      "severity": "error",
      "code": "archive_spec_update_failed",
      "message": "widgets MODIFIED failed for header \"### Requirement: Widget state\" - current spec contains scenario(s) not present in the modified block: \"Existing scenario\". Refresh the change spec before archiving to avoid dropping scenarios.",
      "fix": "Fix the change delta specs and rerun. No files were changed."
    }
  ]
}
$ echo $?
1

The failed archive is atomic: the active change remains in place and no archive directory is created.

Expected behavior

openspec validate --type change rename-scenario—or at minimum --strict—should exit nonzero and report that the MODIFIED block omits an existing scenario.

Ideally validation would reuse the same non-mutating applicability/loss-prevention checks that archive performs, so a change reported as valid is known to be applicable to the current canonical specs.

Why this matters

This is easy to produce while editing a scenario name. The change can then pass definition validation, be implemented and reviewed, and fail only at the final archive step days later.

Related issues

This reproduction is distinct: the requirement header exists, there is no parallel change or drift, and the delta is already unarchivable at authoring time because its MODIFIED block omits one canonical scenario.

Activity

  1. alfred-openspec commented on Jul 29, 2026

    @alfred-openspec
    Collaborator

    Thanks for the clean repro. Confirmed on 1.7.0: both validation modes exit successfully while archive rejects the change atomically, so we’ll align validation with the same non-mutating scenario-presence check and catch this before implementation.

  2. added
    bugSomething isn't working
    backlogReal issue or feature queued for later implementation
    P2-mediumImportant but not urgent
    coreCore engine/CLI
    on Jul 29, 2026
  3. clay-good commented on Jul 29, 2026

    @clay-good
    Collaborator

    Fix opened as #1482: openspec validate <change> now runs the same non-mutating scenario-presence check archive performs, on every validate path (validate <change>, --changes/--all, and change validate).

    Your exact repro on the branch:

    $ openspec validate --type change rename-scenario
    Change 'rename-scenario' has issues
    ✗ [ERROR] widgets/spec.md: MODIFIED "Widget state" omits scenario(s) the current spec still has: "Existing scenario". Copy them into the MODIFIED block (a MODIFIED requirement replaces the whole block, so archive refuses to drop them).
    $ echo $?
    1

    It errors in default mode as well as --strict, and stays silent when the main spec or the requirement header is absent (the sister-change case #1112 covers), so validate only reports what archive already refuses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2-mediumImportant but not urgentbacklogReal issue or feature queued for later implementationbugSomething isn't workingcoreCore engine/CLI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions