startBackgroundFetch assigned the field from its own goroutine, and
only after the initial fetch had completed, while the UI thread reads
it in triggerImmediateFetch on every repo switch, with no
synchronization.
Create the channel in startBackgroundRoutines instead, which runs on
the UI thread before the fetch goroutine is spawned; everything the UI
thread does afterwards is ordered after the write, so the read is
race-free without any locking. To make this possible, goEvery now
takes the retrigger channel as a parameter instead of creating and
returning it; callers that have no use for a retrigger channel pass
nil, and a nil channel in a select is simply never ready.
As a side effect, a repo switch that happens before the fetch loop has
started (during the intro popup or the initial fetch) now latches a
trigger and causes an immediate fetch once the loop is running, where
previously it was silently dropped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>