Skip to content

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

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

fix(validate): report what a MODIFIED block adds, not only what it drops#1809
ryandemelo wants to merge 1 commit 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.

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 (4429).

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 detect both missing and newly added scenarios, including duplicate scenario names.
    • Updated error messages to clearly explain scenario differences and counts, helping identify when a change specification is out of date.
    • Preserved safeguards that prevent scenarios from being unintentionally removed during updates.
  • Tests

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

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 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 Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 80274cb8-5b55-4501-bf5b-deb749000ff6

📥 Commits

Reviewing files that changed from the base of the PR and between e062b95 and c891d85.

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

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


📝 Walkthrough

Walkthrough

The change adds a shared, multiplicity-aware scenario diff. Specification application and validation now report both missing and added scenarios with bounded balance descriptions. Tests cover duplicate names, fenced headers, message formatting, and 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 tests
test/core/parsers/requirement-blocks.test.ts, test/core/validation.scenario-loss.test.ts
Tests cover bidirectional differences, duplicate names, fenced headers, capped added-name lists, renamed scenarios, dropped scenarios, and matching archive errors.

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

Merge Risk: ⚪ Minimal · up to c891d

Validate and archive now report scenario counts and newly introduced scenario names when a modified block omits scenarios, without changing guard behavior, strict mode, or exit codes. The behavior is covered for key comparison and messaging cases, with no current merge-blocking risk identified.

Suggested reviewers: clay-good, 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
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: MODIFIED-block validation now reports added scenarios as well as dropped scenarios.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 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.

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