Skip to content

Halt background work when a newer build is detected#37

Merged
sysread merged 1 commit into
mainfrom
claude/halt-work-on-update-NKXzi
May 13, 2026
Merged

Halt background work when a newer build is detected#37
sysread merged 1 commit into
mainfrom
claude/halt-work-on-update-NKXzi

Conversation

@sysread
Copy link
Copy Markdown
Owner

@sysread sysread commented May 13, 2026

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 onNeedRefresh only flips updateState.available so 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() from loadSettingsThenStartWorkers() after activate(). Teardown lived inline in lock(): same eight manager.stop() calls + stopUsagePolling(), no shared helper. update.svelte.ts::onNeedRefresh set updateState.available = true and stopped there.

This PR. Three changes, all in state.svelte.ts plus one call site in update.svelte.ts:

  • extracts the eight manager.stop() + stopUsagePolling() block out of lock() into a shared stopBackgroundWorkers() helper, so the two teardown sites can't drift
  • adds haltBackgroundWork() and a module-scoped workersHalted latch. Calling haltBackgroundWork() flips the latch and runs stopBackgroundWorkers(). The latch is one-way - only the page reload applyUpdate() performs clears it
  • gates startBackgroundWorkers() on the latch so a settings fetch resolving after onNeedRefresh can't fire workers back up. This closes the race where onNeedRefresh lands between activate() and the settings-then-workers chain

In update.svelte.ts::onNeedRefresh, after flipping updateState.available = true, call haltBackgroundWork(). Direction of import: update.svelte.ts -> state.svelte.ts only; 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 poller stays in update.svelte.ts and is deliberately not in the worker list - it's what announced the new build
  • chat loop is user-initiated, not background - users finish their turn before the banner asks them to reload; not gated
  • another tab can pick up the released lease briefly until its own update poll fires - acceptable; lease coordinator handles the churn
  • workersHalted is module-scoped (not on app) so it survives a lock/unlock cycle - sign-out + sign-in inside the same page session shouldn't quietly resume on stale code

Generated by Claude Code

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.
@sysread sysread merged commit cfdeca9 into main May 13, 2026
@sysread sysread deleted the claude/halt-work-on-update-NKXzi branch May 13, 2026 00:44
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