Skip to content

fix(validate): report what a MODIFIED block adds, not only what it drops - #1809

Open
ryandemelo wants to merge 2 commits into
Fission-AI:mainfrom
ryandemelo:fix/report-added-scenarios
Open

ryandemelo wants to merge 2 commits into
Fission-AI:mainfrom
ryandemelo:fix/report-added-scenarios

Conversation

@ryandemelo

@ryandemelo ryandemelo commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What

When the scenario-loss guard fires, the message now also says how many scenarios each side has and which ones the MODIFIED block introduces.

✗ [ERROR] widgets/spec.md: MODIFIED "Widget state" omits scenario(s) the current spec
  still has: "Second scenario". The modified block has 2 scenario(s) to the current
  spec's 2, and adds 1 the spec does not have: "Second scenario, widened". Copy the
  omitted scenarios into the MODIFIED block (a MODIFIED requirement replaces the whole
  block, so archive refuses to drop them).

Suggested by @crisradu75 on #1697. Archive prints the same sentence, so the two commands cannot disagree.

Refs #1697. This is the reporting half only; the format question stays open there, so this does not close it.

Why

The guard names what the block omits, and that is the whole message. The reader's next question is what the block put there instead, and it is the fact that separates the two cases the guard cannot: a block that omits two names and introduces two is shaped like a rename, one that omits two and introduces none is shaped like a truncation. Today answering that means opening both files.

It is worth being explicit that this decides nothing, because the surrounding thread is about exactly that. @johnmcarbajal's replay over 75 archived changes on #1697 found one MODIFIED block producing six findings against a single requirement — two renames and four accidents, identical in scenario count, heading and id presence, separable only by reading the prose. Intent is not recoverable from structure, and a count does not recover it either. This prints two facts and draws no conclusion from them. The guard fires on exactly the same inputs it did before, and the exit code is unchanged.

That also keeps it independent of the format question on #1697, which is a maintainer decision and is not touched here.

Shape

The comparison already walked current-against-incoming; the other direction is the same pass run the other way, so both now come from one diffScenarioNames, and both call sites print one shared describeScenarioBalance sentence.

That is deliberate rather than tidy. validate and archive agreeing about what counts as a dropped scenario is what findMissingCurrentScenarios exists for (#1477), and the counts printed next to it are worth no less: two commands reporting different numbers for the same block would be its own bug. findMissingCurrentScenarios stays as that function's missing half, so its contract and its tests are untouched.

Listing is capped at three names plus a count, so a wholesale rewrite cannot flood the message.

One wording change came with it: validate's fix instruction said "Copy them into the MODIFIED block", which became ambiguous once a second list of scenarios appeared before it. It now says "Copy the omitted scenarios".

Scope and safety

Tests

10 added, all existing ones untouched and passing.

  • diffScenarioNames: both directions and both totals, added-side multiplicity, fence masking on the incoming side, and agreement with findMissingCurrentScenarios.
  • describeScenarioBalance: the rename shape, the adds-none shape, and the listing cap.
  • Parity through both commands: validate's message and archive's error carry the same sentence for the same change, in the file that already pins that parity.

Full suite passes (5754) on current main.

Note

docs/troubleshooting.md quotes this message as a fragment ending before the new sentence, so it stays accurate. The published docs-lab/help/troubleshooting.md does not cover this error at all — a separate gap, not touched here.

Summary by CodeRabbit

  • Bug Fixes

    • Improved scenario-change validation to reliably detect missing and newly added scenarios, including duplicate names.
    • Updated openspec validate and openspec archive messages to show scenario counts and list up to three newly added scenarios.
    • Clarified differences between renamed, truncated, and otherwise changed scenarios while preserving safeguards against unintended scenario removal.
  • Tests

    • Added coverage for renamed, added, removed, duplicated, and fenced scenarios, including detailed balance messages.

@ryandemelo
ryandemelo requested a review from a team as a code owner September 7, 2026 01:01
@ryandemelo
ryandemelo requested review from alfred-openspec and removed request for a team September 7, 2026 01:01
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview 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: Advanced

Run ID: 20e7c24e-c35f-4c52-9b53-9bec2fc0b08e

📥 Commits

Reviewing files that changed from the base of the PR and between 2b7c65f and 56cf051.

📒 Files selected for processing (3)
  • src/core/parsers/requirement-blocks.ts
  • src/core/specs-apply.ts
  • src/core/validation/validator.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change adds a shared, multiplicity-aware scenario diff. Specification application and validation now report missing and added scenarios with bounded balance descriptions. Tests cover comparison, formatting, and scenario-loss guard behavior.

Changes

Scenario balance reporting

Layer / File(s) Summary
Scenario diff and description
src/core/parsers/requirement-blocks.ts
Adds ScenarioNameDiff, diffScenarioNames, and describeScenarioBalance. findMissingCurrentScenarios delegates to the shared diff.
Guard and validation integration
src/core/specs-apply.ts, src/core/validation/validator.ts
The MODIFIED requirement guard and scenario-loss validation use the shared diff and append scenario-balance text to errors.
Parser and guard validation
test/core/parsers/requirement-blocks.test.ts, test/core/validation.scenario-loss.test.ts, .changeset/scenario-loss-reports-added-scenarios.md
Tests cover bidirectional differences, duplicate names, fenced headers, capped added-name lists, renamed scenarios, dropped scenarios, and matching archive errors. The changeset declares a patch release.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Suggested reviewers: tabishb

Sequence Diagram(s)

sequenceDiagram
  participant buildUpdatedSpec
  participant findScenarioLossIssues
  participant diffScenarioNames
  participant describeScenarioBalance
  buildUpdatedSpec->>diffScenarioNames: compare current and modified blocks
  diffScenarioNames-->>buildUpdatedSpec: return missing, added, and counts
  buildUpdatedSpec->>describeScenarioBalance: format balance
  describeScenarioBalance-->>buildUpdatedSpec: return balance text
  findScenarioLossIssues->>diffScenarioNames: compare current and modified blocks
  diffScenarioNames-->>findScenarioLossIssues: return missing, added, and counts
  findScenarioLossIssues->>describeScenarioBalance: format balance
  describeScenarioBalance-->>findScenarioLossIssues: return balance text
Loading

Merge Risk: ⚪ Minimal · up to 56cf0

The scenario-loss messages are updated consistently for validation and archive flows, with no identified merge-blocking risk.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 5 files.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: MODIFIED block reports now include added scenarios, not only dropped scenarios.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

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.

alfred-openspec
alfred-openspec previously approved these changes Sep 7, 2026

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head c891d85. The shared bidirectional diff preserves the existing multiplicity-aware and fence-aware loss semantics, and validate/archive remain aligned. Focused tests (45), type-checking, lint, and the hosted Linux/macOS/Windows and security matrix are green. The living cli-validate contract remains accurate, and canonical docs-lab does not currently document this diagnostic.

When the scenario-loss guard fires it names the scenarios the block omits,
which is the whole message. A reader's next question is what the block put
there instead, and answering it separates the two cases the guard cannot:
a block that omits two names and introduces two is shaped like a rename,
one that omits two and introduces none is shaped like a truncation. Today
that costs opening both files.

Add the counts and the introduced names to the message. This decides
nothing. Intent is not recoverable from structure, the guard fires exactly
as before, and the exit code is unchanged.

The comparison already walked one direction, so the other is the same pass
run the other way. Both directions now come from diffScenarioNames, and
both commands print one shared sentence, so archive and validate cannot
drift on what they report any more than they can on what they catch.
findMissingCurrentScenarios stays as its missing half.

Also names the antecedent in validate's fix instruction, which became
ambiguous once a second list of scenarios appeared before it.
@ryandemelo
ryandemelo force-pushed the fix/report-added-scenarios branch from 2b7c65f to 56cf051 Compare September 18, 2026 00:40
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.

2 participants