Skip to content

fix(connections): notify subscribers when updateConnections adds connections - #389

Open
draedful wants to merge 2 commits into
v2from
fix/v2-update-entities-connections
Open

draedful wants to merge 2 commits into
v2from
fix/v2-update-entities-connections

Conversation

@draedful

@draedful draedful commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

ConnectionsStore.updateConnections reduced into the existing Map and assigned the same reference back to $connectionsMap. The signal saw no change, so it did not notify, and the computed $connections stayed stale.

Effect for graph.updateEntities({ connections: [newConnection] }):

  • getConnectionState(id) found the new connection;
  • $connections, getConnections() and toJSON() did not include it;
  • GraphLayer / BlockConnections subscribe to $connections, so no BlockConnection was mounted and nothing was rendered.

A later addConnection (which calls notifyConnectionMapChanged()) hid the problem.

Fix

packages/graph/src/store/connection/ConnectionList.ts:

  • mutate the live map entry by entry, then call the existing notifyConnectionMapChanged() to publish a new Map reference (the same pattern addConnection / deleteConnections use);
  • the in-place sequential mutation is intentional. getOrCreateConnection still sees earlier entries of the same batch, so duplicate ids behave as before: one ConnectionState, reused and updated, last entry wins;
  • empty input is a no-op (no notification);
  • explicit : void return type.

Behavior note: a batch that only updates existing ids now also publishes a new $connections array (before, nothing fired). The result is one extra keyed children diff in BlockConnections; component and state identity are kept.

Tests

New packages/graph/src/store/connection/updateConnections.test.ts:

  • a new connection via updateEntities notifies $connections once and appears in $connections / getConnections() / toJSON();
  • a started graph mounts a BlockConnection for it;
  • duplicate ids in one batch: map size 1, last entry wins, the state is reused (updateConnection called once), one notification;
  • existing connections keep their identity and are updated alongside newly appended ones (the computed list is read before the update, so a stale cache would be caught);
  • empty update does not notify.

Without the fix, 4 of 5 tests fail; the empty-input test guards the new early return.

Checks

  • pnpm run typecheck
  • pnpm run test:type-configs
  • pnpm test: graph 334 passed / 6 todo, graph-react 141 passed / 4 skipped, devtools 11 passed
  • pnpm run lint: 0 errors (1 pre-existing storybook warning)
  • node scripts/strict-diagnostics.mjs

🤖 Generated with Claude Code

Summary by Sourcery

Publish connection map updates reliably so connections added through entity updates appear in state, serialization, and the rendered graph.

Bug Fixes:

  • Notify connection subscribers when updateConnections adds or updates entries so computed connection collections and rendered connection views stay current.

Enhancements:

  • Preserve sequential batch semantics, duplicate-ID state reuse, and partial updates when an invalid entry throws.

Tests:

  • Add coverage for subscriber notifications, rendering, duplicate IDs, empty updates, partial failures, and existing-connection identity.

…ections

updateConnections reduced into the existing Map and assigned the same
reference back to $connectionsMap, so the signal did not notify and
$connections stayed stale. Connections added via graph.updateEntities
were not rendered and were missing from getConnections()/toJSON().

Mutate the live map sequentially (keeps duplicate-id behavior: last
entry wins, state reused) and publish a new Map reference via
notifyConnectionMapChanged(). Empty input is a no-op.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@draedful
draedful requested a review from Antamansid as a code owner October 16, 2026 14:00
@sourcery-ai

sourcery-ai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Fixes stale reactive connection data by updating the live map sequentially and publishing a new Map reference once for each non-empty update batch. The accompanying tests cover subscriber notification, serialized/computed results, rendering, duplicate-ID semantics, state identity, and empty-input behavior.

File-Level Changes

Change Details Files
Publishes connection-map updates through a new Map reference so reactive subscribers and computed connection lists refresh.
  • Treats empty batches as no-ops.
  • Processes entries sequentially to preserve duplicate-ID reuse and last-entry-wins behavior.
  • Mutates each map entry, then invokes the existing change-notification path once per non-empty batch.
  • Adds an explicit void return type.
packages/graph/src/store/connection/ConnectionList.ts
Adds regression coverage for reactive updates, rendering, identity preservation, duplicate handling, and empty batches.
  • Verifies updateEntities additions notify once and appear in all connection representations.
  • Verifies a running graph mounts the corresponding BlockConnection.
  • Verifies duplicate IDs reuse one state and retain the final entry.
  • Verifies existing state identity is preserved while new connections are appended.
  • Verifies empty updates do not notify.
packages/graph/src/store/connection/updateConnections.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gravity-ui-bot

Copy link
Copy Markdown
Contributor

Preview is ready.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="packages/graph/src/store/connection/ConnectionList.ts" line_range="171-175" />
<code_context>
+    if (!connections.length) return;
+    // Mutate sequentially so getOrCreateConnection sees earlier entries of this batch (duplicate ids),
+    // then publish a new Map reference: reassigning the same Map would not notify subscribers.
+    connections.forEach((connection) => {
       const c = this.getOrCreateConnection(connection);
-      acc.set(c.id, c);
-      return acc;
-    }, this.$connectionsMap.value);
+      this.$connectionsMap.value.set(c.id, c);
+    });
+    this.notifyConnectionMapChanged();
   }

</code_context>
<issue_to_address>
**Partial batches stay invisible**

When a batch contains a valid connection followed by an invalid value that throws during processing, an earlier connection is inserted into the live map, but a later `getOrCreateConnection` throws before `notifyConnectionMapChanged()` runs. The map then contains the earlier connection while `$connections` and its subscribers remain stale.

Publish the map in a `finally` block so any mutations completed before an exception are visible to subscribers.
</issue_to_address>

### Comment 2
<location path="packages/graph/src/store/connection/ConnectionList.ts" line_range="168-175" />
<code_context>
-  public updateConnections(connections: TConnection[]) {
-    this.$connectionsMap.value = connections.reduce((acc, connection) => {
+  public updateConnections(connections: TConnection[]): void {
+    if (!connections.length) return;
+    // Mutate sequentially so getOrCreateConnection sees earlier entries of this batch (duplicate ids),
+    // then publish a new Map reference: reassigning the same Map would not notify subscribers.
+    connections.forEach((connection) => {
       const c = this.getOrCreateConnection(connection);
-      acc.set(c.id, c);
-      return acc;
-    }, this.$connectionsMap.value);
+      this.$connectionsMap.value.set(c.id, c);
+    });
+    this.notifyConnectionMapChanged();
   }

</code_context>
<issue_to_address>
**Sparse batches spuriously notify**

When `updateConnections` receives an array with a positive length but only holes, `forEach` skips holes, so a nonzero-length sparse array processes no connections but still calls `notifyConnectionMapChanged()`. Subscribers receive a change notification and `BlockConnections` schedules an unnecessary render despite no data changing.

Notify only if the iteration processed at least one connection.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: packages/graph/src/store/connection/ConnectionList.ts:175


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread packages/graph/src/store/connection/ConnectionList.ts Outdated
Comment thread packages/graph/src/store/connection/ConnectionList.ts Outdated
…op notify

Notify in a finally block so entries applied before a throwing entry
(e.g. untyped null input) are published to $connections subscribers,
and notify only when at least one entry was applied, so empty and
sparse arrays do not trigger a spurious change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sourcery assessment

Approved.

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