fix(desktop): track isDisabled flips in the adapter no-op notify gate
Review follow-up: __internal_setAdapter assigned this.isDisabled before the fast path but never fed it into the new 'changed' flag, so an isDisabled-only flip on an otherwise-identical adapter swap would have been silently swallowed. Seed 'changed' with the isDisabled comparison and add a guard test (mutation-checked: reverting the seed fails it).
This commit is contained in:
@@ -146,7 +146,9 @@ class IncrementalExternalStoreThreadRuntimeCore extends ExternalStoreThreadRunti
|
||||
}
|
||||
|
||||
const isRunning = store.isRunning ?? false
|
||||
this.isDisabled = store.isDisabled ?? false
|
||||
const newDisabled = store.isDisabled ?? false
|
||||
const disabledChanged = this.isDisabled !== newDisabled
|
||||
this.isDisabled = newDisabled
|
||||
|
||||
const oldStore = self._store
|
||||
self._store = store
|
||||
@@ -158,7 +160,7 @@ class IncrementalExternalStoreThreadRuntimeCore extends ExternalStoreThreadRunti
|
||||
// (render -> new literal -> setAdapter -> notify -> render), which React
|
||||
// kills with "Maximum update depth exceeded" and takes the session tile
|
||||
// down with its error boundary.
|
||||
let changed = false
|
||||
let changed = disabledChanged
|
||||
|
||||
if (this.extras !== store.extras) {
|
||||
this.extras = store.extras
|
||||
|
||||
@@ -115,6 +115,29 @@ describe('IncrementalExternalStoreThreadRuntimeCore adapter swap notifications',
|
||||
expect(notifications).toBeGreaterThan(0)
|
||||
})
|
||||
|
||||
it('still notifies on an isDisabled flip even when transcript and run state are unchanged', () => {
|
||||
const repo = repositoryOf([message('a', 'one')])
|
||||
const core = new IncrementalExternalStoreRuntimeCore(adapterWith(repo))
|
||||
const thread = core.threads.getMainThreadRuntimeCore()
|
||||
|
||||
let notifications = 0
|
||||
thread.subscribe(() => {
|
||||
notifications += 1
|
||||
})
|
||||
|
||||
core.setAdapter(adapterWith(repo, { isDisabled: true }))
|
||||
|
||||
expect(notifications).toBeGreaterThan(0)
|
||||
|
||||
// And flipping back also notifies — but an unchanged repeat stays silent.
|
||||
core.setAdapter(adapterWith(repo, { isDisabled: false }))
|
||||
const afterFlipBack = notifications
|
||||
|
||||
core.setAdapter(adapterWith(repo, { isDisabled: false }))
|
||||
|
||||
expect(notifications).toBe(afterFlipBack)
|
||||
})
|
||||
|
||||
it('a subscriber that swaps a fresh-but-identical adapter on every notify must not recurse unboundedly', () => {
|
||||
// Direct simulation of the feedback loop: the subscriber plays the role of
|
||||
// React re-rendering ChatRuntimeBoundary (new literal -> setAdapter). With
|
||||
|
||||
Reference in New Issue
Block a user