Skip to content

show --json --deltas-only reports an invented MODIFIED for a bullet-form REMOVED, while archive deletes the requirement #1855

Closed

Description

@dwin-gharibi

Description

openspec show <change> --json --deltas-only is the command OpenSpec's own
error text recommends for inspecting parsed deltas
(src/core/validation/constants.ts:61: "Tip: run openspec change show <change-id> --json --deltas-only to inspect parsed deltas"). It reads deltas
through ChangeParser, which is a second delta reader, separate from
parseDeltaSpec, the one archive applies. The two disagree.

For a REMOVED written in the bullet form, the same change is described in two
contradictory ways:

  • show reports one MODIFIED delta, invented from the proposal's "What
    Changes" prose.
  • archive removes the requirement.

Reproduction

Main spec openspec/specs/billing/spec.md has Invoice Generation and
Late Fees (written by archiving a first change). The proposal's What Changes
is - **billing**: updates billing requirements. Delta:

## REMOVED Requirements
- `### Requirement: Late Fees`
openspec show edit --json --deltas-only
# deltaCount: 1, deltas: [{ operation: "MODIFIED", description: "updates billing requirements" }]

openspec archive edit --yes
# Totals: + 0, ~ 0, - 1, → 0   (Late Fees is deleted from the main spec)

Control: the same removal written as a header (### Requirement: Late Fees +
**Reason**: ...) is shown as REMOVED.

Expected

show reports what archive will apply: one REMOVED delta for Late Fees. It
should never report proposal prose as a structured operation.

Actual

show reports a MODIFIED that does not exist, and hides the deletion.

Three shapes are affected, all of them accepted by archive:

Delta show --json --deltas-only archive
bullet-form REMOVED (-, * or +) invented MODIFIED from proposal prose removes the requirement
## ADDED Requirements written twice only the first copy's requirements applies both copies (#1802)
RENAMED written with * or + bullets rename missing (or invented MODIFIED) renames (#1800)

Root cause

src/core/parsers/change-parser.ts:

  • :137 reads REMOVED only from ### Requirement: header children. The bullet
    form that parseRemovedNames (requirement-blocks.ts:333) accepts is
    invisible to it.
  • :106, :122, :137, :152 use findSection, which returns the first
    match only. parseDeltaSpec reads every copy (fix(parser): apply every delta section header #1802).
  • :175-176 (parseRenames) accepts only - or no bullet. parseDeltaSpec
    accepts every CommonMark marker (fix(parser): accept all CommonMark list markers in deltas #1800).
  • :36 and :43: when the spec files yield no structured delta, the parser
    falls back to the proposal's "What Changes" bullets, and reports them as
    operations.

The parser's own comment at :87 states the goal: "keep the two readers in
agreement".

Impact

The recommended inspection command misreports a deletion as a modification.
A reviewer or an AI agent checking a delta before archiving is told that a
requirement will be edited when it will be deleted. The same reader also feeds
openspec change list delta counts.

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
  • Reproduced in a project created entirely by openspec init +
    openspec new change, with the baseline main spec produced by archiving a
    first change

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