Skip to content

validate --strict: scenario-currency check has no way to declare a deliberate scenario rename, so a narrowing reads as an omission #1793

Description

@brettheap

Summary

validate --strict reports an ERROR when a ## MODIFIED Requirements block
omits a scenario the current spec still carries. The check compares scenario
TITLE SETS, so a deliberate rename — the same behaviour, narrowed and
retitled — is indistinguishable from an accidental drop, and the only edit
that satisfies the check is to restore the old title. Where the rename was the
point of the change, that means reverting the change.

There is already precedent inside this very check for an author-declared
exclusion: a block whose requirement is renamed away by a
## RENAMED Requirements section in the same delta is skipped
(renamedAway in dist/core/validation/validator.js). What is missing is the
same affordance one level down, at the scenario.

Reproduction (minimal, complete)

openspec/specs/example-capability/spec.md

## Requirements

### Requirement: Composed views are read-only with a repository jump
A composed view SHALL be read-only, and SHALL offer a jump to the repository.

#### Scenario: Gate verbs hide on a composed view
- **WHEN** a composed view is rendered
- **THEN** the gate verbs are hidden

openspec/changes/rename-a-scenario/specs/example-capability/spec.md

## MODIFIED Requirements

### Requirement: Composed views are read-only with a repository jump
A composed view SHALL be read-only, SHALL offer a jump to the repository, and SHALL admit document creation.

**Merged into `Tile-bound gate verbs hide on a composed view` by rename-a-scenario (2026-09-05):** `Gate verbs hide on a composed view`

#### Scenario: Tile-bound gate verbs hide on a composed view
- **WHEN** a composed view is rendered
- **THEN** the TILE-BOUND gate verbs are hidden
- **AND** document creation stays available

Plus a two-line proposal.md and tasks.md. Then:

$ openspec validate --all --strict      # @fission-ai/openspec@1.12.0
✓ spec/example-capability
✗ change/rename-a-scenario
  ✗ [ERROR] example-capability/spec.md: MODIFIED "Composed views are read-only with a repository jump" omits scenario(s) the current spec still has: "Gate verbs hide on a composed view". Copy them into the MODIFIED block (a MODIFIED requirement replaces the whole block, so archive refuses to drop them).
Totals: 1 passed, 1 failed (2 items)

$ openspec validate --all --strict      # @fission-ai/openspec@1.2.0
Totals: 2 passed, 0 failed (2 items)

Doing what the message asks — copying Gate verbs hide on a composed view back
into the block — reinstates an unconditional statement the change exists to
narrow, beside the narrower successor that contradicts it.

Why "just keep both scenarios" is not available here

The two titles are not two behaviours. Tile-bound gate verbs hide on a composed view is the SAME behaviour with a narrower scope, because the same change
deliberately admits document creation on a composed view. Carrying both would
put a contradiction in one requirement block.

What we do today, and why we would rather not

We run the CLI through a content-addressed pin
(contracts/openspec-cli-pin.yaml) and reconcile its --json findings against
an enumerated, cited, per-finding disposition list, so exactly two findings in
our corpus are accepted with a written citation and everything else fails. It
works, and it is strictly worse than the check being able to read the
declaration: our suppression lives in our pin, so it is invisible to anyone
reading the delta, and it has to be re-derived at every CLI upgrade.

Asks, in order of preference

  1. Honour a declared scenario rename. Either a first-class
    ## RENAMED Scenarios (or a scenario-level entry under the existing
    ## RENAMED Requirements) that the currency check consults exactly as it
    already consults renamedAway; or a recognized in-block marker line naming
    old title → new title. Our house form, in use since 2026-08 and defined
    by our own promoted spec, is:

    **Merged into `<new scenario title>` by <change-id> (<date>):** `<old scenario title>`
    

    We are not asking for OUR spelling — a canonical one you choose is better.
    What matters is that a DECLARED rename is distinguishable from a silent drop,
    which is the whole distinction the check is trying to make.

  2. Failing that, per-finding suppression the delta can carry — an
    openspec/config.yaml rule list, or a recognized inline directive, keyed by
    check id + item + requirement, so the exception is visible in the repository
    that takes it rather than in a wrapper around the CLI.

  3. Failing both, a severity knob for this one check, so a corpus that has
    made the judgment once can run --strict without either reverting a decision
    or filtering the tool's output. This is the weakest ask and we mention it only
    so the option is on the table.

Notes for whoever picks this up

  • The check is dist/core/validation/validator.js, the loop over modified
    producing omits scenario(s) the current spec still has.
  • It already carries one author-declared exclusion (renamedAway), so the shape
    of the fix has precedent in the same function.
  • 1.2.0 does not carry the check at all; it appears somewhere in 1.3–1.12.
    We upgraded 1.2.0 → 1.12.0 in one step and did not bisect, so we cannot name
    the introducing release.
  • Happy to supply the two real deltas from our corpus if a synthetic
    reproduction is not enough, and happy to test a patch — we run this CLI
    digest-pinned in CI, so we can pin a candidate build precisely.

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

    design-reviewNeeds product/design decision

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions