Skip to content

A scenario header with no body passes validate, then archive refuses the change #1857

Closed

Description

@dwin-gharibi

Description

A requirement whose only scenario is a header with nothing under it gets
opposite verdicts from the two commands that check it:

  • openspec validate <change> reports the change valid.
  • openspec archive <change> refuses it, with a generic "Requirement must
    have at least one scenario" that does not name the requirement.

This is the validate/archive disagreement the scenario-parity work was meant to
rule out: the failure is deferred from authoring time to archive time.

Reproduction

In a project created with openspec init + openspec new change edit, with a
valid proposal and tasks, the delta openspec/changes/edit/specs/billing/spec.md:

## ADDED Requirements
### Requirement: Credit Notes
The system SHALL issue a credit note when an invoice is voided.

#### Scenario: Invoice voided
openspec validate edit --json   # "valid": true
openspec archive edit --yes     # exit 1
#   ✗ Requirement must have at least one scenario
#   Aborted. No files were changed.

Control: the same scenario with - **WHEN** ... / - **THEN** ... lines under
it validates and archives.

Expected

validate rejects what archive will reject, and names the requirement, so
the author fixes it while writing the delta.

Actual

validate passes; archive fails later without naming the requirement.

Root cause

The two scenario counters the code says must agree do not:

  • The delta path counts every non-fenced #### header:
    SCENARIO_HEADER (src/core/parsers/requirement-text.ts:30) and
    countScenarios (:94), used by validate for ADDED and MODIFIED
    (src/core/validation/validator.ts:272, :307).
  • The spec path keeps a scenario only when its body has content:
    if (scenarioSection.content.trim())
    (src/core/parsers/markdown-parser.ts:176). Archive validates the rebuilt
    spec on this path.

The parity requirement is written at requirement-text.ts:26: "so the delta
counter must too (parity)".

Other shapes that disagree the same way: a scenario body of only blank or
whitespace lines, and a closed header (#### Scenario: X ####) with no body.

Environment

  • OpenSpec 1.13.0 (reproduced on the published npm package and on a build of
    main @ 9d4e597)
  • Node v20.19.0 and v22, Linux

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions