Skip to content

ModuleGraph: Stop bumping the graph revision on story-index invalidation#35178

Merged
JReinhold merged 2 commits into
nextfrom
jeppe-cursor/module-graph-drop-coarse-bump
Jun 18, 2026
Merged

ModuleGraph: Stop bumping the graph revision on story-index invalidation#35178
JReinhold merged 2 commits into
nextfrom
jeppe-cursor/module-graph-drop-coarse-bump

Conversation

@JReinhold

@JReinhold JReinhold commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Closes #

What I did

A story-index invalidation (any story file save, story add/remove/rename, or a preview.* config change) fired an untargeted _bumpGraphRevision on the core/module-graph service. That coarse bump advanced the global graph revision and reset latestChangedStoryFiles to empty.

That blanket bump is redundant with the precise change paths the engine already has:

  • A content edit to a story file (or a story renamed within a file) arrives as a builder file-change event → _applyGraphUpdate with the affected story files.
  • A story entering or leaving the index is replayed as an add/unlink by refreshStoryFiles_applyGraphUpdate with the affected story files.

Because the coarse bump runs concurrently with those targeted updates, it races them and can clobber the latest changed-story set back to empty. getLatestStoryChanges subscribers (e.g. docgen) that key off that set then intermittently miss a refresh after a story save.

This PR:

  • Removes the _bumpGraphRevision command and the engine onStoryIndexInvalidated callback (the coarse bump).
  • Keeps the STORY_INDEX_INVALIDATEDrefreshStoryFiles reconciliation, so newly added / removed story roots are still walked and reported as targeted updates.

I verified the targeting empirically in a live react-vite/default-ts sandbox (temporary engine instrumentation): editing a story file, a directly-imported component, and a deep transitive dependency each produced the correct targeted bumpedStoryFiles through the builder file-change path alone, while STORY_INDEX_INVALIDATED only ever fired for story-file saves.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

  1. Run a sandbox with the docgen server enabled, e.g. yarn task sandbox --template react-vite/default-ts --start-from auto (the default sandbox already sets features.experimentalDocgenServer and features.changeDetection).
  2. Open Storybook and select a story; open the Code panel.
  3. Edit the story file (e.g. change an arg or add a comment) and save. The Code panel snippet should update without needing a manual reload.
  4. Edit a component imported by the story and save. The component's docgen/controls should update.
  5. Add a new *.stories.tsx file; it should appear in the sidebar and its docgen/snippets should populate.

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This pull request has been released as version 0.0.0-pr-35178-sha-a7b9e5e7. Try it out in a new sandbox by running npx storybook@0.0.0-pr-35178-sha-a7b9e5e7 sandbox or in an existing project with npx storybook@0.0.0-pr-35178-sha-a7b9e5e7 upgrade.

More information
Published version 0.0.0-pr-35178-sha-a7b9e5e7
Triggered by @JReinhold
Repository storybookjs/storybook
Branch jeppe-cursor/module-graph-drop-coarse-bump
Commit a7b9e5e7
Datetime Wed Jun 17 07:46:38 UTC 2026 (1781682398)
Workflow run 27673858090

To request a new release of this pull request, mention the @storybookjs/core team.

core team members can create a new canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=35178

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes
    • Improved change detection so graph revisions advance only when story files actually change, avoiding unnecessary refresh work when the story index is invalidated but the indexed story set remains the same.
    • Updated reconciliation behavior to emit focused update events for newly discovered story files instead of relying on story-index invalidation callbacks.
  • Tests
    • Updated and added test coverage to verify the new invalidation/update trigger behavior and revision advancement rules.

A story-index invalidation fired an untargeted `_bumpGraphRevision` that
advanced the global graph revision and reset `latestChangedStoryFiles` to
empty. That coarse bump is redundant: a story-file content edit already
arrives as a builder file-change event, and a story entering/leaving the
index is replayed as an add/unlink by `refreshStoryFiles` — both produce a
precise `_applyGraphUpdate`. The blanket bump only races those targeted
updates and can clobber the latest changed-story set, so `getLatestStoryChanges`
subscribers (e.g. docgen) intermittently miss a refresh after a story save.

Keep the index-invalidation -> `refreshStoryFiles` reconciliation; remove the
`_bumpGraphRevision` command and the engine `onStoryIndexInvalidated` callback.

Co-authored-by: Cursor <cursoragent@cursor.com>
@JReinhold JReinhold added bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release. labels Jun 16, 2026
@JReinhold JReinhold self-assigned this Jun 16, 2026
@JReinhold JReinhold marked this pull request as ready for review June 16, 2026 09:31
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4558b510-0392-45a7-9ac3-5eafbd82ee9f

📥 Commits

Reviewing files that changed from the base of the PR and between a7b9e5e and 220b43c.

📒 Files selected for processing (1)
  • code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts

📝 Walkthrough

Walkthrough

Removes the onStoryIndexInvalidated callback from ModuleGraphEngineOptions and the _bumpGraphRevision internal command from moduleGraphServiceDef. Story-index invalidation no longer directly bumps the graph revision; instead, onStoryIndexInvalidated() relies on refreshStoryFiles add/unlink replay to emit onUpdate with bumpedStoryFiles only when the story file set actually changes.

Changes

Remove direct revision-bump path from story-index invalidation

Layer / File(s) Summary
ModuleGraphEngine interface and onStoryIndexInvalidated behavior
code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts
Removes onStoryIndexInvalidated?: () => void from ModuleGraphEngineOptions. Updates onStoryIndexInvalidated() to stop invoking that callback; revision updates now flow via onUpdate through refreshStoryFiles replay.
Service definition and server constructor wiring
code/core/src/shared/open-service/services/module-graph/definition.ts, code/core/src/shared/open-service/services/module-graph/server.ts
Removes the _bumpGraphRevision command (which incremented graphRevision and cleared latestChangedStoryFiles) from moduleGraphServiceDef.commands. Removes the onStoryIndexInvalidated engine constructor callback in registerModuleGraphService that dispatched that command.
Test updates across engine, server, and change-detection
code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.test.ts, code/core/src/shared/open-service/services/module-graph/server.test.ts, code/core/src/core-server/change-detection/...
Drops onStoryIndexInvalidated mock from engine test setup; asserts onUpdate with bumpedStoryFiles instead; adds a test for no-op invalidation. Removes _bumpGraphRevision-specific and bare-revision-bump scope tests from server tests; replaces adapter/build wiring test with a STORY_INDEX_INVALIDATED channel-event scenario verifying targeted reconciliation. Updates change-detection test description and helper wiring to reflect revision advancement as the rescan trigger.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts (1)

257-271: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Buffer invalidations that arrive before incrementalPatcher is ready.

Line 264 drops STORY_INDEX_INVALIDATED events when startup is still building (this.incrementalPatcher is unset). With the callback path removed, that invalidation has no deferred replay, so add/unlink reconciliation can be missed until a later invalidation.

💡 Proposed fix
 export class ModuleGraphEngine {
+  private pendingStoryIndexInvalidation = false;
   private readonly workingDir: string;
   private adapter: ChangeDetectionAdapter | undefined;
@@
   onStoryIndexInvalidated(): void {
+    if (!this.incrementalPatcher) {
+      this.pendingStoryIndexInvalidation = true;
+      return;
+    }
+
     if (!this.refreshInFlight && this.incrementalPatcher) {
       this.refreshInFlight = true;
       this.refreshSettled = this.refreshStoryFiles()
@@
   private async startInternal(): Promise<void> {
@@
     this.incrementalPatcher = new IncrementalPatcher({
@@
     });
+
+    if (this.pendingStoryIndexInvalidation) {
+      this.pendingStoryIndexInvalidation = false;
+      this.onStoryIndexInvalidated();
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts`
around lines 257 - 271, The onStoryIndexInvalidated() method currently skips all
processing when this.incrementalPatcher is not yet initialized during startup,
causing invalidation events to be dropped with no deferred replay mechanism. Add
a buffer or queue to store invalidations that arrive before
this.incrementalPatcher is ready, then process any buffered invalidations once
this.incrementalPatcher becomes available. Modify the guard condition to allow
early invalidations to be queued even when this.incrementalPatcher is unset, and
ensure the initialization of this.incrementalPatcher also triggers processing of
any buffered invalidations to prevent missed add/unlink reconciliation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts`:
- Around line 257-271: The onStoryIndexInvalidated() method currently skips all
processing when this.incrementalPatcher is not yet initialized during startup,
causing invalidation events to be dropped with no deferred replay mechanism. Add
a buffer or queue to store invalidations that arrive before
this.incrementalPatcher is ready, then process any buffered invalidations once
this.incrementalPatcher becomes available. Modify the guard condition to allow
early invalidations to be queued even when this.incrementalPatcher is unset, and
ensure the initialization of this.incrementalPatcher also triggers processing of
any buffered invalidations to prevent missed add/unlink reconciliation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6b624929-c0bd-415c-b640-b7ed16ec2d3a

📥 Commits

Reviewing files that changed from the base of the PR and between 62011d4 and a7b9e5e.

📒 Files selected for processing (7)
  • code/core/src/core-server/change-detection/change-detection-service.test.ts
  • code/core/src/core-server/change-detection/change-detection.test-helpers.ts
  • code/core/src/shared/open-service/services/module-graph/definition.ts
  • code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.test.ts
  • code/core/src/shared/open-service/services/module-graph/engine/module-graph-engine.ts
  • code/core/src/shared/open-service/services/module-graph/server.test.ts
  • code/core/src/shared/open-service/services/module-graph/server.ts
💤 Files with no reviewable changes (3)
  • code/core/src/core-server/change-detection/change-detection.test-helpers.ts
  • code/core/src/shared/open-service/services/module-graph/server.ts
  • code/core/src/shared/open-service/services/module-graph/definition.ts

@JReinhold JReinhold requested a review from yannbf June 16, 2026 10:06
@JReinhold JReinhold merged commit e1c7af7 into next Jun 18, 2026
12 of 14 checks passed
@JReinhold JReinhold deleted the jeppe-cursor/module-graph-drop-coarse-bump branch June 18, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants