Halt background work when a newer build is detected#37
Merged
Conversation
Until now, the only thing that happened on `onNeedRefresh` was that `updateState.available` flipped to true and the UpdateBanner appeared. Every background worker on the tab kept running — lease heartbeats, embedding generation, journaling, samskara, wiki, the librarian, plus the hourly usage poll — against code that was about to be replaced. On a long-lived tab the gap between "banner visible" and "user clicks Reload" can be tens of minutes, which means a non-trivial slice of background processing happens on a build the rest of the deploy has moved past. state.svelte.ts gains a single `haltBackgroundWork()` entry point and a module-scoped `workersHalted` latch. The halt extracts the manager.stop() + stopUsagePolling() sequence that `lock()` already ran inline into a shared `stopBackgroundWorkers()` helper, so the two teardown sites can't drift, and gates `startBackgroundWorkers()` on the latch so a settings fetch resolving after the SW update can't fire workers back up. update.svelte.ts calls `haltBackgroundWork()` from `onNeedRefresh` right after setting `updateState.available = true`. The update poller itself stays in update.svelte.ts and is not in the worker list — it's what announced the new build, so it has to keep running. The chat loop is user-initiated and continues; users get to finish their turn before the banner asks them to reload. The latch is one-way. The only thing that clears it is the page reload `applyUpdate()` performs, which is the correct semantic: once we know we're stale, sign-out + sign-in inside the same page session shouldn't quietly resume processing on the old code.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Synopsis
Stop every background worker plus the usage poller the moment the SW reports a newer build is waiting. Workers stay halted until the user clicks Reload and the page picks up the new bundle.
Purpose
Currently
onNeedRefreshonly flipsupdateState.availableso the UpdateBanner appears. Workers keep running - lease heartbeats, embeddings, journaling, samskara, wiki, the librarian, and the hourly usage poll - against code the rest of the deploy has moved past. On a long-lived tab the gap between "banner visible" and "user clicks Reload" can be tens of minutes, so a non-trivial slice of background processing happens on a stale build.Description
Existing behaviour.
state.svelte.ts::startBackgroundWorkers()fires the eight lazy manager handles +startUsagePolling()fromloadSettingsThenStartWorkers()afteractivate(). Teardown lived inline inlock(): same eightmanager.stop()calls +stopUsagePolling(), no shared helper.update.svelte.ts::onNeedRefreshsetupdateState.available = trueand stopped there.This PR. Three changes, all in
state.svelte.tsplus one call site inupdate.svelte.ts:manager.stop()+stopUsagePolling()block out oflock()into a sharedstopBackgroundWorkers()helper, so the two teardown sites can't drifthaltBackgroundWork()and a module-scopedworkersHaltedlatch. CallinghaltBackgroundWork()flips the latch and runsstopBackgroundWorkers(). The latch is one-way - only the page reloadapplyUpdate()performs clears itstartBackgroundWorkers()on the latch so a settings fetch resolving afteronNeedRefreshcan't fire workers back up. This closes the race whereonNeedRefreshlands betweenactivate()and the settings-then-workers chainIn
update.svelte.ts::onNeedRefresh, after flippingupdateState.available = true, callhaltBackgroundWork(). Direction of import:update.svelte.ts -> state.svelte.tsonly; no circular dep.This fixes PURPOSE because workers stop on the same tab that detected the new build, within the SW poll cadence (3 min) of the deploy. Other tabs halt on their own poll. The reload itself is unaffected — the banner / Reload button flow is untouched.
Notes:
update.svelte.tsand is deliberately not in the worker list - it's what announced the new buildworkersHaltedis module-scoped (not onapp) so it survives a lock/unlock cycle - sign-out + sign-in inside the same page session shouldn't quietly resume on stale codeGenerated by Claude Code